在MVVM中,ViewModel或Model应该实现INotifyPropertyChanged吗?

Edw*_*uay 155 c# mvvm inotifypropertychanged

我经历过的大多数MVVM示例都使用了Model实现INotifyPropertyChanged,但在Josh Smith的CommandSink示例中 ,ViewModel实现了INotifyPropertyChanged.

我还在认知上把MVVM概念放在一起,所以我不知道是否:

  • 你必须将INotifyPropertyChanged放在ViewModel中以使CommandSink工作
  • 这只是常态的失常,并不重要
  • 你应该总是有Model实现INotifyPropertyChanged,这只是一个错误,如果从代码示例开发到应用程序,这将被纠正

您曾参与MVVM项目的其他经历是什么?

小智 137

我强烈反对模型不应该实现的概念INotifyPropertyChanged.此界面不是特定于UI的!它只是告知变化.实际上,WPF大量使用它来识别变化,但这并不意味着它是一个UI界面.我会将它与下面的评论进行比较:" 轮胎是汽车配件 ".当然可以,但自行车,公共汽车等也使用它.总之,不要将该接口作为UI事物.

话虽如此,但这并不一定意味着我相信模型应该提供通知.实际上,根据经验,除非有必要,否则模型不应实现此接口.在大多数情况下,没有服务器数据被推送到客户端应用程序,模型可能是陈旧的.但如果听取金融市场数据,那么我不明白为什么模型无法实现界面.例如,如果我有非UI逻辑(例如服务),当它收到给定值的买价或卖价时会发出警报(例如通过电子邮件)或下订单,该怎么办?这可能是一个可能的清洁解决方案.

但是,有不同的实现方式,但我总是赞成简单并避免冗余.

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

每当你看到有人声称" 你不能做到这一点或那样 " 时,底线就是他们不知道他们在说什么的标志.

这实际上取决于你的情况,事实上MVVM是一个有很多问题的框架,我还没有看到一个全面的MVVM实现.

我希望我有更多的时间来解释MVVM的各种风格以及常见问题的一些解决方案 - 主要由其他开发人员提供,但我想我将不得不再做一次.

  • 这样想吧.如果您作为开发人员使用模型中的.dll,那么您肯定不会重写它们来支持INotifyPropertyChanged. (7认同)
  • @Rhyous即使模型被包装,模型也有能力改变自身以响应模型中的其他一些变化。例如,假设更改 Employee 对象的 Salary 也会导致 Employee 的 TaxBracket 更改。包装器(如 ViewModel)如何知道 TaxBracket 发生了变化? (3认同)
  • 强烈同意你的看法。像我一样,您也可能会发现MVVM官方文档<http://msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx>(“模型”部分)同意我们。:-) (2认同)
  • `INotifyPropertyChanged` 是 `System.ComponentModel` 命名空间的一部分,用于“**组件和控件的运行时和设计时行为**”。**请勿在模型中使用** `INotifyPropertyChanged`,仅在 ViewModel 中使用。链接到文档:https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel (2认同)
  • 老帖子,我知道,但是当我使用 MVVM 开始一个新项目时,我经常会回顾它。我最近开始更加严格地执行单一责任原则。榜样就是有一种责任。成为一名模特。一旦将 INotifyPropertyChanged 添加到模型中,它就不再遵循单一职责原则。ViewModel 存在的全部原因是让模型成为模型,让模型承担单一责任。 (2认同)

Ste*_*ins 101

我说的恰恰相反,我总是把我INotifyPropertyChanged放在我的ViewModel上 - 你真的不想用一个相当WPF特定的功能来污染你的模型,比如INotifyPropertyChanged那些东西应该放在ViewModel中.

我相信别人会不同意,但这就是我工作的方式.

  • 如果模型中的属性发生变化,您会怎么做?你需要以某种方式将它带到视图模型.诚实的问题,我现在正处理这个难题. (80认同)
  • INotifyProperyChanged不是特定于WPF的,它存在于System.ComponentModel命名空间中,我在WinForms应用程序中使用它,也是INotifyPropertyChanged自2.0以来一直在.Net中,WPF自3.0以来一直存在 (50认同)
  • 我喜欢将INotifyPropertyChanged放在MODEL和VIEWMODEL中.我想不出不这样做的理由.它是一种优雅的方式,可以告知VIEWMODEL您的MODE中发生的背景更改何时影响VIEWMODEL,就像它用于通知VIEW并且VIEWMODEL已发生更改一样. (40认同)
  • @Steve - 关于通知ViewModel模型的属性已经改变,看起来像INotifyPropertyChanged可以正常工作,因为"视图模型可以挂钩的事件".为什么不用它? (6认同)
  • Prism代码中的EventAggregator是模型上INotifyPropertyChanged的一个很好的替代品,具有自定义属性更改事件类型.该项目中的事件代码支持在后台和UI线程之间转发事件,这有时可能是一个问题. (4认同)
  • @Roger - 作为对您使用的一个小小让步,您可以在您的UI更新时调用viewmodel可以挂钩的事件. (2认同)
  • @Roger 和其他对此感到疑惑的人:我最终在我的模型和视图模型中实现了 INotifyPropertyChanged。然后我从我的 ViewModel 订阅模型的 PropertyChanged 事件,这样它也可以引发 PropertyChanged 事件,以便相应地更新 GUI 上的绑定。有关更多详细信息,请参阅 http://stackoverflow.com/questions/5821956/mvvm-model-to-viewmodel-communication 上的答案。 (2认同)
  • 如果您的模型被多个不同的视图使用,因此有多个不同的 ViewModel,该怎么办?然后,当 ViewModel 更改模型时,该更改需要以某种方式传播到其他 ViewModel。这就是为什么我认为在模型中实现通知机制也是可以的。 (2认同)

Son*_*Ali 28

在MV-VM中,ViewModel始终(Model并不总是)实现INotifyPropertyChanged

查看来自http://blogs.msdn.com/llobo/archive/2009/05/01/download-mv-vm-project-template-toolkit.aspx的MV-VM项目模板/工具包.它使用DelegateCommandfor命令,它应该是你MV-VM项目的一个很好的起始模板.


Rhy*_*ous 12

我认为MVVM命名很差,并且调用ViewModel是一个ViewModel会导致许多人错过精心设计的架构的一个重要特性,这是一个控制数据的DataController,无论是谁试图触摸它.

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

DataModel -------- DataController ------ View
                  /
Business --------/
Run Code Online (Sandbox Code Playgroud)

你最终得到了这样的模型.甚至企业也应该只使用ViewModel触摸数据.然后你的难题就消失了.

  • 你是完全正确的.设计模式非常高.大多数时候,设计模式会让你做正确的事情,但是时不时会让你误解.你永远不应该避免做正确的事情,因为它超出了你的设计模式. (4认同)
  • 如果您的数据仅在DataController更改时更改,那就太棒了.如果数据来自数据库或其他可以提供另一种更改途径的数据存储区,则可能需要一种方法来通知VIEWMODEL(模式中的DataController)和VIEW何时发生.您可以使用DataController进行轮询,也可以从某个外部进程推送到DataModel,并允许DataModel向DataController发送更改通知. (3认同)

Ste*_*ham 9

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

我们的验证引擎在很大程度上依赖于通过此机制通知属性发生变化,并且它非常有效.显然,如果您使用的是业务对象以外的其他实现,其中更改通知对操作不是那么重要,那么您可能还有其他方法可以检测业务模型中的更改.

我们还有视图模型,可以根据需要传播模型中的更改,但视图模型本身正在监听基础模型更改.

  • 你究竟如何将Model的OnPropertyChanged传播到ViewModel的OnPropertyChanged?我有一个问题,当ViewModel具有不同的属性名称而不是模型 - 然后需要某种名称到名称的映射,对吧? (3认同)

akj*_*shi 7

我同意 Paulo 的回答,INotifyPropertyChanged在 Models 中实施是完全可以接受的,微软甚至建议 -

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

虽然由您决定是否需要这种类型的实现,但请记住 -

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

有时候,你需要与不执行模型对象的工作INotifyPropertyChangedINotifyCollectionChangedIDataErrorInfo,或INotifyDataErrorInfo接口。在这些情况下,视图模型可能需要包装模型对象并向视图公开所需的属性。这些属性的值将由模型对象直接提供。视图模型将为它公开的属性实现所需的接口,以便视图可以轻松地将数据绑定到它们。

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

我曾参与过一些我们没有INotifyPropertyChanged在模型中实现的项目,因此我们遇到了很多问题;VM 中需要不必要的属性重复,同时我们必须在将它们传递给 BL/DL 之前更新底层对象(具有更新的值)。

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


dze*_*ras 5

我认为这一切都取决于用例。

当您有一个带有大量属性的简单模型时,您可以让它实现 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;}
}
Run Code Online (Sandbox Code Playgroud)

现在看看他们两个。IConnectionManagerINPC 告诉您什么?它的一些属性可能会改变。你不知道他们中的哪一个。事实上,设计中只有 IsConnected 发生变化,因为其余部分都是只读的。

相反,IConnectionManager 的意图很明确:“我可以告诉你,我的 IsConnected 属性的值可能会改变”。