20

我已经阅读了一些 MVVM 教程,并且我已经看到了这两种方式。大多数人将 ViewModel 用于 PropertyChanged(这是我一直在做的),但我遇到了一个在 Model 中执行此操作的模型。两种方法都可以接受吗?如果是这样,不同方法的优点/缺点是什么?

4

7 回答 7

38

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

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

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

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

-- John Gossman,MVVM 的发明者: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 的属性,例如当前选择了集合中的哪个项目。

于 2013-05-31T20:45:01.543 回答
15

( INotifyPropertyChangedINPC) 接口用于Binding.

因此,在一般情况下,您希望在ViewModel.

ViewModel用于将 与Model您的解耦View,因此无需在您Model的.BindingsModel

在大多数情况下,即使对于较小的属性,您仍然拥有非常小的ViewModel.

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

更新 由于似乎有许多 Windows 窗体开发人员,这里有一篇优秀的文章,可以更深入地了解 MVVM 的含义: MSDN Magazine on MVVM

我特别链接了有关数据模型的部分,即问题所在。

于 2013-05-31T19:39:59.730 回答
4

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

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

INotifyPropertyChanged 不是一个特定于视图的接口,它只做顾名思义——当属性改变时引发一个事件。WinForms、WPF 和 Silverlight 恰好支持它用于 Binding - 我当然将它用于非展示目的!

于 2013-06-03T18:12:47.090 回答
2

MVVM 的创建者 JohnGossman 在这篇博客文章(@Jonathan Allen 提到)中指出:

在简单的示例中,视图是直接绑定到模型的数据。模型的一部分通过单向数据绑定简单地显示在视图中。模型的其他部分可以通过直接将控件双向绑定到数据来进行编辑。例如,模型中的布尔值可以是绑定到 CheckBox 的数据,或者是绑定到 TextBox 的字符串字段。

然而,在实践中,只有一小部分应用程序 UI 可以直接与模型绑定数据,尤其是当模型是应用程序开发人员无法控制的预先存在的类或数据模式时。

我更喜欢遵循在应用程序扩展时仍然适用的做法。如果“在实践中 [...],只有一小部分应用程序 UI 可以直接将数据绑定到模型”,这似乎不是一个好的做法,因为我不打算只解决“简单案例”或“应用程序 UI 的一小部分”。
对于“简单情况”,我什至不会一开始就使用 MVVM。

于 2017-07-13T21:23:46.973 回答
1

根据经验,您将绑定到的任何对象(即使您不需要双向绑定和属性更改通知),都必须实现INotifyPropertyChanged. 这是因为不这样做可能会导致内存泄漏

于 2013-05-31T19:37:05.890 回答
1

INotifyPropertyChanged 应该由视图使用的所有类型实现(当然,除非它只有常量值)。

您是否将模型(不是视图模型)返回到视图?如果是,那么它应该实现 INotifyPropertyChanged。

于 2013-05-31T19:39:02.040 回答
1

虽然我通常支持实现 INPC 的模型,但复合视图模型中对 INPC 的调用是在它公开可绑定到视图的推断属性时。IMO 因为 INPC 被嵌入到 System.dll 中,所以实现它的模型可以被认为是 POCO。对于集合,基于模型的 INPC 具有性能优势。在 64 位平台上,与 ObservableCollection<Model> 相比,包装 VM 的字节大小(加载 SOS 调试器扩展以获取实际大小)将具有 8 倍乘数。

于 2013-07-24T13:53:23.003 回答