MVVM - Model或ViewModel中的PropertyChanged?

Jas*_*n D 19 c# mvvm

我已经完成了一些MVVM教程,我已经看到了这两种方式.大多数人使用ViewModel for PropertyChanged(这是我一直在做的),但我遇到了一个在模型中做到这一点.两种方法都可以接受吗 如果是这样,不同方法的好处/缺点是什么?

Jon*_*len 33

微软的模式和实践,MVVM的发明者,我都不同意所选择的答案.

通常,模型实现了可以轻松绑定到视图的工具.这通常意味着它通过INotifyPropertyChanged和INotifyCollectionChanged接口支持属性和集合更改通知.表示对象集合的模型类通常派生自ObservableCollection类,该类提供INotifyCollectionChanged接口的实现.

- Microsoft模式和实践:http://msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx#sec4

此时数据绑定开始发挥作用.在简单示例中,View是直接绑定到Model的数据.模型的各个部分通过单向数据绑定简单地显示在视图中.可以通过直接将控件双向绑定到数据来编辑模型的其他部分.例如,Model中的布尔值可以是绑定到CheckBox的数据,也可以是绑定到TextBox的字符串字段.

- MVVM的发明者John Gossman:http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx

我自己的文章:http://www.infoq.com/articles/View-Model-Definition


具有"视图模型"的反模式只是包装模型并公开相同的属性列表.视图模型的工作是调用外部服务并公开这些服务返回的个体和模型集合.

原因:

  1. 如果直接更新模型,则视图模型将不知道触发属性更改事件.这会导致UI不同步.
  2. 这严重限制了在父视图模型和子视图模型之间发送消息的选项.
  3. 如果模型有自己的属性更改通知,则#1和2不是问题.相反,如果包装器VM超出范围但模型没有,则必须担心内存泄漏.
  4. 如果你的模型很复杂,有很多子对象,那么你必须遍历整个树并创建第二个对象图,它会遮蔽第一个.这可能非常繁琐且容易出错.
  5. 包裹的集合特别难以使用.任何时候(UI或后端)从集合中插入或删除项目,都需要更新影子集合以匹配.这种代码真的很难搞定.

这并不是说你永远不需要一个包装模型的视图模型.如果您的视图模型公开了与模型明显不同的属性,并且不能仅使用IValueConverter进行包装,那么包装视图模型是有意义的.

您可能需要包装视图模型的另一个原因是您的数据类由于某种原因不支持数据绑定.但即便如此,通常最好只创建一个普通的可绑定模型并从原始数据类中复制数据.

当然,您的视图模型将具有UI特定属性,例如当前选择集合中的哪个项目.

  • ViewModel是调用存储库的原因,如果您使用的话.如果不是,则直接调用外部服务.视图模型的设计不会改变任何一种方式. (2认同)
  • 作者"逃之夭夭"因为**他明白他在说什么.你宣传根本不需要视图模型,这是完全错误的.直接绑定到模型是特殊情况,而不是正常情况.这里的问题是"做什么".也许你重读了这个问题和我的答案.认为一些阅读可以帮助您了解MVVM的工作原理,但似乎这是失去的努力.特别是如果要求变得更复杂,则需要更好的架构和设计.但是,如果你不关心这些事情,我为什么要打扰. (2认同)
  • @JonathanAllen谢谢.你提出了一些非常好的观点.我同意包装器虚拟机浪费代码并且不会对项目结构添加任何内容,只会使其更复杂.VM应负责管理View逻辑,而不是深入研究实际业务逻辑所在的模型.拥有封装虚拟机只会增加复杂性. (2认同)

Mar*_*tus 16

INotifyPropertyChanged(INPC)接口用于Binding.

所以,在一般情况下,你想在你的实现它ViewModel.

ViewModel用于解耦Model从你的View,所以没有必要有INPC在你的Model,因为你不希望Bindings你的Model.

在大多数情况下,即使是较小的房产,您仍然只有很小的房产ViewModel.

如果你想要一个坚实的基础MVVM,你可能会使用某种MVVM框架,如caliburn.micro.使用它将为您提供ViewModelBase(或此处NotifyPropertyChangedBase),这样您就不必自己实现这些接口成员,只需使用NotifyOfPropertyChange(() => MyProperty),这样更容易,更不容易出错.

更新 由于似乎有许多Windows窗体开发人员,这里有一篇很好的文章,将更深入地了解MVVM的内容: MVVM上的MSDN杂志

我特别关注了关于数据模型的部分,问题是关于.

  • 所以请求我评论为什么我对这个答案进行了低估:因为Jonathan Allen的答案实际上与MPP一致. (2认同)
  • 我也不同意这个答案,并出于同样的原因同意@JonathanAllen。如果视图已启动(由正在查看模型的视图模型支持),并且模型从视图以外的其他位置发生更改(可能收到更改模型属性的网络命令),则视图将不同步,除非模型本身引发属性改变。INotifyPropertyChanged 不是 WPF 的一部分(它位于 System.ComponentModel 中)并且不知道绑定。实际上,WPF 绑定使用 INotifyPropertyChanged 来实现其绑定机制——但它们是不同的概念。 (2认同)

Cha*_*ger 5

绝对同意乔纳森·艾伦的观点。

如果您没有任何内容可以添加到您的“视图模型”(命令、影响演示文稿的特定于视图的属性等),那么我肯定会在模型中实现 INotifyPropertyChanged 并直接公开它(如果可以的话 - “模型”可能不会)成为你的)。您不仅最终会重复大量样板代码,而且保持两者同步绝对是一件痛苦的事情。

INotifyPropertyChanged 不是特定于视图的接口,它仅执行其名称所暗示的功能 - 在属性更改时引发事件。WinForms、WPF 和 Silverlight 恰好支持它进行绑定 - 我当然将它用于非演示目的!

  • 但我为什么要费心呢?如果我有一个显示“项目”只读数据网格的视图,则将该网格绑定到“ObservableCollection<Item>”比必须盲目地创建一个“ItemViewModel”来包装它要简单得多,同步底层项目中的更改以触发适当的“PropertyChanged”事件的代码、将底层集合同步到 ViewModel 集合的代码等。在这种情况下,直接绑定到模型会更简单 - 甚至是棱镜指南说:http://msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx#sec4 (3认同)
  • 呃,不。拥有视图模型的全部意义在于,它可以让您将数据与数据的外部操作分开。一旦开始将“保存”等 ICommand 与“名字/姓氏”等普通属性混合在一起,您就离开了该模式。诸如验证和计算属性(例如 FullName)之类的内部操作应该在模型和单元测试中,仅此而已。 (2认同)