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

时间:2012-04-25 21:23:14

标签: c# silverlight mvvm domain-driven-design prism

案例

说我有一个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标记为我问题的答案,因为它为我提供了我已经提出的解决方案的替代方案。

2 个答案:

答案 0 :(得分:26)

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

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

class PersonViewModel
{
    private Person OriginalModel { get; set; }

    public ValueViewModel<string> Name { get; set; }
    public ValueViewModel<int> 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<T>。这个类实现了INotifyPropertyChanged(以及其他一些东西)。但是当你收到一个新人时,你只需要在你的ViewModel上调用ReadFromModel(newPerson)来更新UI,因为View绑定的ValueViewModels会在他们的值发生变化时通知用户界面。

这里是ValueViewModel

的内部结构的极其简化的示例
class ValueViewModel<T> : INotifyPropertyChanged
{
    private T _value;
    public T Value 
    {
        get { return _value;}
        set
        {
            _value = value;
            RaisePropertyChanged("Value");
        }
    }
}

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

答案 1 :(得分:7)

如果视图直接绑定到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"; 
     }
   }

IN XAML

  <TextBlock Text="{Binding Model.PropertyDirect}" />  
  <TextBlock Text="{Binding SpecicalProperty}" />

这样,只有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);
      } 
 }