34

案子

假设我有一个Person类, aPersonViewModel和 a PersonView

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

然而。

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

这就是我将如何解决它:

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

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

加法

PersonViewDataContext 是PersonViewModel. Person从 JSON 填充并在其生命周期内多次更新。

随意为我的特定案例建议架构更改。

回答

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

4

2 回答 2

32

当视图直接绑定到模型时(ViewModel 暴露模型时也是如此),您正在混合 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(和其他一些东西)。当你收到一个新Person实例时,你只需要调用ReadFromModel(newPerson)你的 ViewModel 来更新 UI,因为 View 绑定到的 ValueViewModel 会在它们的值发生变化时通知 UI。

这里有一个非常简化的内部结构示例ValueViewModel

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

这是我们在 MVVM 库中使用的一种方法。它的优点是它迫使开发人员清楚地将代码与设计人员的关注点分开。而且,作为副作用,它会在所有视图和视图模型中生成标准化的代码布局,从而提高代码质量。

于 2012-04-26T08:37:28.313 回答
7

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

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

//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 中

  <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);
      } 
 }
于 2012-04-25T21:27:11.600 回答