MVVM -属性更改在模型或视图模型

本文关键字:模型 视图 属性 MVVM | 更新日期: 2023-09-27 18:16:23

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

MVVM -属性更改在模型或视图模型

MVVM的发明者Microsoft's Patterns and Practices和我都不同意所选的答案。

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

—Microsoft Patterns and Practices: 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


拥有一个"视图模型"是一种反模式,它只是包装了一个模型并公开了相同的属性列表。视图模型的工作是调用外部服务,并公开这些服务返回的单个模型和模型集合。

原因:

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

这并不是说你永远不需要一个包装模型的视图模型。如果视图模型公开的属性与模型有很大不同,不能用IValueConverter来掩盖,那么包装视图模型是有意义的。

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

当然,你的视图模型将具有特定于UI的属性,例如集合中的哪个项目当前被选中。

INotifyPropertyChanged (INPC)接口用于Binding

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

ViewModel用于从View中解耦Model,因此不需要在Model中使用INPC,因为您不希望BindingsModel

在大多数情况下,即使是较小的属性,您仍然有一个非常小的ViewModel

如果你想为MVVM建立一个坚实的基础,你可能会使用一些MVVM框架,比如caliburn.micro。使用它会给你一个ViewModelBase(或者这里的NotifyPropertyChangedBase),这样你就不必自己实现那些接口成员,只需要使用NotifyOfPropertyChange(() => MyProperty),这是更容易和更少出错的方式。

似乎有很多Windows窗体开发人员在那里,这里有一个优秀的这篇文章将让你更深入地了解什么是MVVM:MSDN Magazine on MVVM

我特别链接了关于数据模型的部分,这个问题是关于

我绝对同意Jonathan Allen的观点。

如果你没有什么可以添加到你的"视图模型"(命令,特定于视图的属性,影响表示等),那么我肯定会在模型中实现INotifyPropertyChanged并直接暴露(如果你可以-"模型"可能不是你的)。你不仅要重复大量的样板代码,保持两者同步绝对是件痛苦的事。

INotifyPropertyChanged并不是一个特定于视图的接口,它所做的正是它的名字所暗示的——当属性发生变化时引发一个事件。WinForms、WPF和Silverlight恰好支持它的绑定——我当然在非表示目的中使用过它!

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

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

然而,在实践中,只有一小部分应用程序UI可以是数据直接绑定到模型,特别是如果模型是预先存在的类或数据模式,应用程序开发人员没有控制。

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

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

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

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

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