MVVM:修改模型,如何正确更新ViewModel和View?

案件

假设我有一个Person类,一个PersonViewModel和一个PersonView

PersonView中的属性更新为Person模型非常简单。 PersonViewModel包含一个Person对象,并具有PersonView绑定的公共属性,以便更新Person模型。

然而。

想象一下, Person模型可以通过Service更新。 现在需要将属性更改传递给PersonViewModel ,然后传递给PersonView

我就是这样解决的:

对于Person模型中的每个属性,我都会引发PropertyChanged事件。 PersonViewModel订阅Person的PropertyChanged事件。 然后PersonViewModel将引发另一个PropertyChanged以更新PersonView

这对我来说似乎是最明显的方式,但我有点想把这个问题抛到那里,希望有人能给我一个更好的方法。 这真的很简单,还是有更好的方法将模型标记为已修改并更新ViewModel上的相应属性?

附加

PersonView的DataContext是PersonViewModelPerson从JSON填充并在其生命周期内多次更新。

随意为我的特定情况建议架构更改。

回答

我将aqwert标记为我问题的答案,因为它为我提供了我已经提出的解决方案的替代方案。

如果视图直接绑定到Model,那么只要服务使用相同的实例,对模型属性的任何更改都将传播到视图。

但是,如果要在服务中重新创建新模型,则会为viewmodel提供新模型。 我希望将模型视为视图模型上的属性,因此当您设置该属性时,应该提醒所有绑定更改。

 //in the ViewModel public Person Model { get { return _person; } set { _person = value; RaisePropertyChanged("Model"); //<- this should tell the view to update } } 

编辑:

由于您声明了特定的ViewModel逻辑,因此您可以在ViewModel定制这些属性

  private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) { if(e.PropertyName == "Prop1") RaisePropertyChanged("SpecicalProperty"); ... } public string SpecicalProperty { get { reutrn Model.Prop1 + " some additional logic for the view"; } } 

在XAML

    

这样,只有ModelViewModel属性绑定到视图而不复制数据。

您可以更好地创建帮助程序,以将属性更改从模型链接到视图模型或使用映射字典

  _mapping.Add("Prop1", new string[] { "SpecicalProperty", "SpecicalProperty2" }); 

然后通过获取属性列表找到要更新的属性

  private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e) { string[] props; if(_mapping.TryGetValue(e.PropertyName, out props)) { foreach(var prop in props) RaisePropertyChanged(prop); } } 

当视图直接绑定到模型时,您将混合UI代码和数据代码。 MVVM的目标是将这两个代码域分开。 这就是ViewModel的用途。

视图模型必须具有视图可以绑定的自己的属性。 一个例子:

 class PersonViewModel { private Person OriginalModel { get; set; } public ValueViewModel Name { get; set; } public ValueViewModel Postcode { get; set; } protected void ReadFromModel(Person person) { OriginalModel = person; Name.Value = OriginalModel.Name; Postcode.Value = OriginalModel.Postcode; } protected Person WriteToModel() { OriginalModel.Name = Name.Value; //... return OriginalModel; } } 

使用这样的ViewModel设计实际上将数据对象与用户界面代码分开。 当Person更改时,UI不需要适合,因为ViewModel将它们分开。

现在回答你的问题。 正如您在上面的示例中看到的,我使用了通用的ValueViewModel 。 这个类实现了INotifyPropertyChanged (以及其他一些东西)。 但是当你收到一个新的Person时,你只需要在你的ViewModel上调用ReadFromModel(newPerson)来更新UI,因为View绑定的ValueViewModels会在它们的值发生变化时通知UI。

这是ValueViewModel内部结构的一个极其简化的示例:

 class ValueViewModel : INotifyPropertyChanged { private T _value; public T Value { get { return _value;} set { _value = value; RaisePropertyChanged("Value"); } } } 

这是我们在MVVM库中使用的方法。 它的优势在于它迫使开发人员明确地将其代码与设计人员的关注点分开。 作为副作用,它在所有Views和ViewModel中生成标准化的代码布局,从而提高代码质量。