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

Posted

技术标签:

【中文标题】MVVM:修改模型,如何正确更新 ViewModel 和 View?【英文标题】:MVVM: Modified model, how to correctly update ViewModel and View? 【发布时间】:2012-05-06 15:07:34 【问题描述】:

案例

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

【问题讨论】:

【参考方案1】:

当视图直接绑定到模型时(当 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&lt;T&gt;。这个类实现了INotifyPropertyChanged(和其他一些东西)。当您收到一个新的 Person 实例时,您只需在 ViewModel 上调用 ReadFromModel(newPerson) 即可更新 UI,因为 View 绑定到的 ValueViewModel 将在其值更改时通知 UI。

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

class ValueViewModel<T> : INotifyPropertyChanged

    private T _value;
    public T Value 
    
        get  return _value;
        set
        
            _value = value;
            RaisePropertyChanged("Value");
        
    

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

【讨论】:

通用ValueViewModel&lt;T&gt; 类的良好解释和出色的sn-p!请考虑对属性更改通知实施平等检查。顺便说一句,here isValueViewModel&lt;T&gt; 类的实现使用了 MVVM Light 框架的ViewModelBase 类。 感谢您的建议。与此同时,我们已经添加了相等性检查。 @PVitt cloud 你请看this Question 它与你的答案有关,所以也许你能告诉我如何解决这个问题。提前致谢 @gts13 老实说,我不知道。几年前我离开了 .NET 生态系统,因此我不再关注它的发展。不过,看看***.com/questions/1405739/… @gts13 据我了解,这是一种责任倒置。 ViewModel 是获取新模型的活动部分。 ViewModel 如何获得 delivered 的新模型无关紧要。但是模型只是一个没有任何代码的哑数据对象。想想仓库。仓库里的东西是模型。仓库管理员是 ViewModel。这些东西不能说它是旧的。必须有人告诉仓库经理他的东西是旧的,他需要买新的东西。【参考方案2】:

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

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

//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);
       
 

【讨论】:

对不起,我忘了说 PersonView 的 DataContext 是 ViewModel。 ViewModel 包含许多与模型分离的 UI 逻辑。 是的,这是典型的。当您通过视图模型公开模型时,将实例更改为另一个模型应该会更新视图 是的,在我的例子中,虽然在模型的生命周期中只有少数属性会改变。您是否建议替换完整的模型对象而不是寻找更新单独属性的方法?我觉得这可能会导致很多开销,但这只是一种预感。 如果你的模型对象很“重”,并且当你反序列化传入的 JSON 时只有少数属性发生变化,那么在视图模型中创建包装器属性可能是值得的。否则,只要没有发现 UI 性能问题,我就会使用完整的模型对象替换。 @ndsc:对不起,我删除了我之前的评论,因为我觉得 aqwert 真的把它与最后一个联系在一起了。重新包装器,这是绝对正确的,这将是强烈解耦模型和视图的另一个原因。我认为您不应该担心两次引发 PC 事件,这是明确分离关注点的关键,其本身的开销可以忽略不计。

以上是关于MVVM:修改模型,如何正确更新 ViewModel 和 View?的主要内容,如果未能解决你的问题,请参考以下文章

MVVM简介

如何在MVVM中使用相同的ViewModel拥有多个视图?

是否可以使用 MVVM 从视图修改视图模型中的属性? [复制]

MVVM 和 VM 集合

[DataGridCheckBoxColumn在属性更改时不会在MVVM中更新

如何为此 SwiftUI CoreData 模型正确插入排序描述符?