从序列化ViewModel还原时,Combox SelectedItem不适用

在恢复ViewModel(使用Json.Net序列化)时使用C#WPF和MVVM模式时,我遇到了一个奇怪的问题。

该软件的想法是 – 关闭窗口时 – 将当前Viewmodel状态保存在json文件中。

在下一次启动时,应用程序只是为json提供支持。

  • 如果有文件,则将其反序列化并恢复ViewModel(设置公共属性)。
  • 如果没有文件,则创建viewmodel并设置默认值。

现在我的问题是,当使用json文件恢复它时,包含自定义类型列表的combobox,combobox具有值但没有SelectedItem 。 在创建viewmodel实例并使用默认值初始化公共属性时(通过后面的代码执行此操作),一切都很好。

以下是一些代表“错误”的代码:View

        

代码背后

 using System; using System.Windows; using System.IO; using Newtonsoft.Json; namespace CrazyWpf { public partial class MainWindow : Window { private DemoViewModel dvm; public MainWindow() { InitializeComponent(); this.dvm = (DemoViewModel)this.rootElement.DataContext; } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { string filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "settings.json"); if (File.Exists(filePath)) File.Delete(filePath); File.WriteAllText(filePath, JsonConvert.SerializeObject(this.dvm, Formatting.Indented)); } private void Window_Loaded(object sender, RoutedEventArgs e) { string filePath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "settings.json"); if (!File.Exists(filePath)) { this.SetDefaultSettings(); return; } DemoViewModel d = JsonConvert.DeserializeObject(File.ReadAllText(filePath)); this.dvm.ID = d.ID; this.dvm.Name = d.Name; this.dvm.StatusType = d.StatusType; } } } 

BaseViewModel:

 using System.ComponentModel; namespace CrazyWpf { public abstract class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

视图模型

 using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; namespace CrazyWpf { class DemoViewModel : BaseViewModel { [JsonIgnore] private int id; [JsonProperty(Order = 1)] public int ID { get { return this.id; } set { if (this.id != value) { this.id = value; this.OnPropertyChanged("ID"); } } } [JsonIgnore] private string name; [JsonProperty(Order = 2)] public string Name { get { return this.name; } set { if (this.name != value && value != null) { this.name = value; this.OnPropertyChanged("Name"); } } } [JsonIgnore] private StatusTyp statusType; [JsonProperty(Order = 3)] public StatusTyp StatusType { get { return this.statusType; } set { if (this.statusType != value && value != null) { this.statusType = value; this.OnPropertyChanged("StatusType"); } } } [JsonIgnore] private List statusTypeList; [JsonProperty(Order = 4)] public List StatusTypeList { get { return this.statusTypeList; } set { if (this.statusTypeList != value && value != null) { this.statusTypeList = value; this.OnPropertyChanged("StatusTypeList"); } } } public DemoViewModel() { this.StatusTypeList = new Func<List>(() => { var list = Enum.GetValues(typeof(Status)) .Cast() .ToDictionary(k => (int)k, v => v.ToString()) .Select(e => new StatusTyp() { Value = e.Key, Name = e.Value, Status = Enum.GetValues(typeof(Status)) .Cast(). Where(x => { return (int)x == e.Key; }).FirstOrDefault() }) .ToList(); return list; })(); } } public class StatusTyp { public int Value { get; set; } public string Name { get; set; } public Status Status { get; set; } } public enum Status { NotDetermined = 0, Determined = 1, Undeterminded = 2, Unknown = 3 } } 

如果你有一个ItemsSource和一个SelectedItem,那么SelectedItem中的实例必须在绑定到ItemsSource的集合中。 如果不是,那么您的绑定将无法按预期工作。

该控件使用引用相等性来确定ItemsSource中的哪个项是SelectedItem中的项并更新UI。 这通常不是问题,因为控件为您填充SelectedItem,但如果您从ViewModel端更新,则必须确保正确管理您的引用。

在序列化/反序列化视图模型时,这可能是一个问题。 大多数常见的序列化程序不跟踪引用,因此无法在反序列化时恢复它们。 可以在原始对象图中的多个位置引用相同的对象,但在反序列化后,您现在在整个重新水合图中具有多个原始遍布的实例。 这不符合您的要求。

您需要做的是,在反序列化之后,在集合中查找匹配的实例,并将其替换为SelectedItem中的实例。 或者,使用跟踪实例的序列化程序。 。 XAML序列化器已经做到了这一点,并且是.net对象图的一个非常好的xml序列化器。