在 MVVM 中 ViewModel 或 Model 是否应该实现 INotifyPropertyChanged?

大多数 MVVM 例子,我已经通过有 模特实现 INotifyPropertyChanged,但在 Josh Smith 的 CommandSink 例子 ViewModel 实现了 INotifyPropertyChanged

我仍然在认知地整合 MVVM 的概念,所以我不知道是否:

  • 您必须将 INotifyPropertyChanged放在 ViewModel 中才能使 CommandSink工作
  • 这只是一个常态的偏差,这并不重要
  • 您应该始终有模型实现 INotifyPropertyChanged,这只是一个错误,如果这是从一个代码示例开发到一个应用程序,将纠正这个错误

在你从事的 MVVM 项目中,其他人有什么经验?

54658 次浏览

我会说完全相反,我总是把我的 INotifyPropertyChanged在我的视图模型-你真的不想污染你的模型相当 WPF 特定的功能,如 INotifyPropertyChanged,这东西应该坐在视图模型。

我相信其他人不会同意,但这就是我的工作方式。

我会说在你的视图模型里。它不是模型的一部分,因为模型是 UI 不可知的。该模型应该是“除了业务不可知的一切”

这取决于您如何实现您的模型。我的公司使用类似于 Lhotka 的 CSLA 对象的业务对象,并在整个业务模型中广泛使用 INotifyPropertyChanged

我们的验证引擎在很大程度上依赖于通过这种机制得到属性更改的通知,并且它工作得非常好。显然,如果您使用的是业务对象以外的其他实现,而业务对象中的更改通知对于操作来说并不那么重要,那么您可能还有其他方法来检测业务模型中的更改。

我们还有视图模型,它在需要的地方从模型传播更改,但是视图模型本身正在监听底层的模型更改。

在 M-V-VM 中,ViewModel 总是(模型并不总是)实现 INotifyPropertyChanged

查看 http://blogs.msdn.com/llobo/archive/2009/05/01/download-m-v-vm-project-template-toolkit.aspx中的 M-V-VM 项目模板/工具包。 它使用的 DelegateCommand命令,它应该是一个伟大的 M-V-VM 项目的开始模板。

但是有时(如在这个表示 链接文本)模型是服务,它为应用程序提供一些在线数据,然后您需要实现新数据到达或数据使用事件更改的通知..。

我强烈反对模型不应该实现 INotifyPropertyChanged的概念。这个界面不是特定于 UI 的!它只是通知了一个变化。实际上,WPF 大量使用它来识别更改,但这并不意味着它是一个 UI 界面。 我会将它与下面的评论进行比较: “ 轮胎是汽车配件”。当然是,但是自行车、公共汽车等也使用它。总之,不要把那个界面当作 UI。

尽管如此,这并不一定意味着我相信模型应该提供通知。事实上,根据经验,模型不应该实现这个接口,除非有必要。在大多数情况下,没有服务器数据被推送到客户端应用程序,模型可能过时。但是,如果听取金融市场数据,那么我不明白为什么模型不能实现接口。例如,如果我有一个非 UI 逻辑,例如一个服务,当它收到一个给定值的出价或询价时,它会发出一个警告(例如。还是下订单?这可能是一个干净的解决方案。

然而,实现目标的方法有很多种,但我总是主张简单化,避免冗余。

什么更好?定义集合上的事件或视图模型上的属性更改,并将其传播到模型,还是让视图本质上更新模型(通过视图模型) ?

每当你看到有人声称“ 你不能这样或那样”这是一个迹象,他们不知道他们在谈论什么底线。

这真的取决于你的情况,事实上 MVVM 是一个有很多问题的框架,我还没有看到一个通用的 MVVM 实现。

我希望我有更多的时间来解释 MVVM 的各种风格和一些常见问题的解决方案——大多数是由其他开发人员提供的,但我想我将不得不在另一个时间来做这件事。

我认为 MVVM 的命名非常糟糕,将 ViewModel 称为 ViewModel 会导致许多人错过一个设计良好的体系结构的重要特性,即 DataController,它控制数据,无论谁试图触摸它。

如果您认为 View-Model 更像是一个 DataController,并且实现了一个架构,其中 DataController 是唯一接触数据的项,那么您将永远不会直接接触数据,而是始终使用 DataController。DataController 对 UI 有用,但不一定只对 UI 有用。它适用于业务层、 UI 层等。.

DataModel -------- DataController ------ View
/
Business --------/

你最后得到了这样一个模特。即使是企业也应该只使用 ViewModel 来接触数据。然后你的难题就消失了。

通常 ViewModel 将实现 INotifyPropertyChanged。模型可以是任何东西(xml 文件、数据库甚至对象)。模型用于将数据提供给视图模型,视图模型传播到视图。

看这里

假设视图中对象的引用发生了更改。您将如何通知所有属性进行更新以显示正确的值?在你的视图中为所有对象的属性调用 OnPropertyChanged在我看来是垃圾。

因此,我所做的就是让对象本身在属性中的值发生变化时通知任何人,在我看来,我使用了 Object.Property1Object.Property2等绑定。这样,如果我只是想改变目前维护在我的视图中的对象,我只需要做 OnPropertyChanged("Object")

为了避免在加载对象期间出现成百上千的通知,我有一个私有的布尔指示器,我在加载期间将其设置为 true,这是从对象的 OnPropertyChanged检查的,并且什么也不做。

我在模型中使用 INotifyPropertyChange接口。实际上,模型属性更改应该只由 UI 或外部客户端触发。

我注意到了一些优点和缺点:

优势

通知器在业务模型中

  1. 根据每个域驱动,它是正确的。它应该决定什么时候提高和什么时候不提高。

缺点

该模型具有数量、比率、佣金、总运费等属性,总运费是用数量、比率、佣金变化来计算的。

  1. 在从 db 加载值时,总运费计算被称为3次(数量、费率、佣金)。它应该是一次。

  2. 如果速率、数量分配在业务层,则再次调用通知器。

  3. 应该有一个选项来禁用它,可能在基类中。但是,开发人员可能会忘记这样做。

我认为视图模型实现了 INotifyPropertyChange,模型可以在不同的“层次”上使用通知。

例句 通过一些文档服务和一个文档对象,您就拥有了一个 documentChanged 事件,视图模型可以通过该事件来清除和重新构建视图。 在编辑视图模型中,文档的属性有一个属性更改,以支持视图。如果服务在保存时对文档做了很多工作(更新更改日期、最后一个用户等等) ,那么很容易就会出现大量的 Iproperty 修改事件,仅仅一个文档修改就足够了。

但是如果你在你的模型中使用 INotifyPropertyChange,我认为在你的视图模型中转播它是一个很好的实践,而不是在你的视图中直接订阅它。在这种情况下,当模型中的事件发生变化时,您只需要更改视图模型,并且视图保持不变。

只要在视图模型中使用 INotifyPropertyChange,而不是在模型中,

模型通常使用 IDataErrorInfo来处理验证错误,所以只要保持在 ViewModel 中就可以了。

我认为这完全取决于用例。

当您有一个具有大量属性的简单模型时,您可以让它实现 INPC。我说的简单是指这个模型看起来很像 POCO

如果您的模型更加复杂,并且位于交互式模型领域——模型引用模型,那么订阅其他模型的事件——将模型事件实现为 INPC 就是一场噩梦。

把自己放在一个必须与其他模型协作的模型实体的位置上。你有各种各样的事件要订阅。它们都作为 INPC 实现。想象一下您拥有的事件处理程序。一个巨大的 if 子句和/或 switch 子句级联。

INPC 的另一个问题。你应该依靠抽象而不是实现来设计你的应用程序。这通常是使用接口完成的。

让我们看看同一抽象的两种不同实现:

public class ConnectionStateChangedEventArgs : EventArgs
{
public bool IsConnected {get;set;}
}


interface IConnectionManagerINPC : INotifyPropertyChanged
{
string Name {get;}
int ConnectionsLimit {get;}
/*


A few more properties


*/
bool IsConnected {get;}
}


interface IConnectionManager
{
string Name {get;}
int ConnectionsLimit {get;}
/*


A few more properties


*/
event EventHandler<ConnectionStateChangedEventArgs> ConnectionStateChanged;
bool IsConnected {get;}
}

现在看看他们俩。IConnectionManagerINPC 告诉你什么?它的一些属性可能会改变。你不知道是哪一个。实际上,这种设计只改变了 IsConnected,因为其余部分都是只读的。

相反,IConnectionManager 的意图很明确: “我可以告诉您,我的 IsConnectedproperty 的值可能发生了变化”。

我认为答案很清楚,如果你想坚持 MV-VM。

参见: Http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx

在 MVVM 模式中,视图封装 UI 和任何 UI 逻辑,视图模型封装表示逻辑和状态,模型封装业务逻辑和数据。

”视图通过数据绑定与视图模型交互, 命令,并更改通知事件, 观察并协调对模型的更新, 根据需要验证和聚合数据以便在视图中显示。 "

我同意保罗的回答,在模型中实现 INotifyPropertyChanged是完全可以接受的,甚至是微软建议的

通常,该模型实现的工具使得 绑定到视图。这通常意味着它支持属性和 集合通过 INotifyPropertyChangedINotifyCollectionChanged接口 对象的集合通常从 类,该类提供了 INotifyCollectionChanged接口。

虽然这取决于您是否想要这种类型的实现,但是请记住-

如果您的模型类没有实现所需的接口怎么办?

有时候,您需要处理不需要的模型对象 实施 INotifyPropertyChangedINotifyCollectionChanged、, IDataErrorInfoINotifyDataErrorInfo接口。在这些情况下, 视图模型可能需要包装模型对象并公开 视图中必需的属性。这些属性的值将 由模型对象直接提供。视图模型将 为其公开的属性实现所需的接口 视图可以轻松地将数据绑定到它们。

取自 http://msdn.microsoft.com/en-us/library/gg405484(PandP.40).aspx

我曾经在一些项目中工作过,在我们的模型中没有实现 INotifyPropertyChanged,由于这个原因,我们面临很多问题; 在 VM 中需要不必要的属性重复,同时我们必须在将它们传递给 BL/DL 之前更新底层对象(使用更新的值)。

如果您需要处理模型对象的集合(比如在可编辑的网格或列表中)或复杂的模型,那么您将特别面临问题; 模型对象不会自动更新,您必须在 VM 中管理所有这些。

所有绑定到我的视图的属性都在我的 ViewModel 中。因此,它们应该实现 INotifyPropertyChanged 接口。因此视图获得所有更改。

[使用 MVVM Light 工具箱,我让它们从 ViewModelBase 继承。]

模型包含业务逻辑,但与视图无关。因此不需要 INotifyPropertyChanged 接口。

如果模型在 ViewModel 中公开,则可以在模型中实现 INPC。但是一般来说,ViewModel 包装模型是为了降低模型的复杂性(这对于绑定来说不应该是有用的)。在这种情况下,应该在 ViewModel 中实现 INPC。