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


165

我研究过的大多数MVVM示例都具有Model实现 INotifyPropertyChanged,但是在Josh Smith的CommandSink示例中 ,ViewModel实现了INotifyPropertyChanged

我仍然在认知上将MVVM概念放在一起,所以我不知道是否:

  • 您必须将其放入INotifyPropertyChangedViewModel才能开始CommandSink工作
  • 这只是规范的畸变,并不重要
  • 您应该始终具有Model实现,INotifyPropertyChanged并且这只是一个错误,如果将其从代码示例开发到应用程序,则可以纠正该错误

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


4
如果您确实实现了INPC,尝试一下github.com/Fody/PropertyChanged-这样可以节省数周的键入时间。
CAD bloke'Apr

Answers:


104

我要说的恰恰相反,我总是将自己INotifyPropertyChanged放在ViewModel上-您确实不想使用相当WPF的特定功能(例如INotifyPropertyChanged,应该放在ViewModel中)。

我敢肯定其他人会不同意,但这就是我的工作方式。


84
如果模型中的属性发生变化该怎么办?您需要以某种方式将其添加到视图模型。诚实的问题,我现在正在处理这个难题。
罗杰·利普斯科姆2010年

4
Prism代码中的EventAggregator是自定义属性更改的事件类型,是该模型上INotifyPropertyChanged的很好替代方案。该项目中的事件代码支持在后台线程和UI线程之间转发事件,有时这可能是个问题。
史蒂夫·米切姆

51
INotifyProperyChanged不是WPF特定的,它位于System.ComponentModel命名空间中,我在WinForms应用程序中使用过它,而且INotifyPropertyChanged从2.0开始就已在.Net中,WPF从3.0开始才存在
benPearce 2011年

40
我喜欢将INotifyPropertyChanged放在MODEL和VIEWMODEL中。我想不出没有这样做的理由。通知VIEWMODEL的一种优雅方法是,通知您的MODE何时发生了会影响VIEWMODEL的背景更改,就像它用来通知VIEW并且VIEWMODEL发生了更改一样。
ScottCher 2011年

6
@Steve-关于通知ViewModel模型的属性已更改,似乎INotifyPropertyChanged可以像“ viewmodel可以挂接到的事件”一样工作。为什么不使用它?
skybluecodeflier

146

我强烈不同意该模型不应实施的概念INotifyPropertyChanged。此界面不是特定于UI的!它只是通知更改。实际上,WPF大量使用它来识别更改,但这并不意味着它是UI界面。我将其与以下评论进行比较:“ 轮胎是汽车的附件 ”。当然可以,但是自行车,公共汽车等也可以使用它。总而言之,不要将该界面当作UI东西。

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

但是,有多种实现目标的方法,但是我总是会主张简单性并避免重复。

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

最重要的是,每当您看到有人声称“ 您无法做到这一点您不能这样做 ”时,这就是他们不知道自己在说什么的标志。

这实际上取决于您的情况,实际上MVVM是一个有很多问题的框架,我尚未看到MVVM的通用实施。

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


7
这样想吧。如果您作为开发人员使用了带有Models的.dll,那么您肯定不会重写它们以支持INotifyPropertyChanged。
李·特雷维伊

2
强烈同意你的看法。像我一样,您也可能会发现MVVM官方文档< msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx >(“模型”部分)与我们一致。:-)
Noldorin

“但是,这是实现事物的不同方式,但是我总是主张简化并避免重复。” 很重要。
巴斯蒂安·范丹姆

1
INotifyPropertyChangedSystem.ComponentModel名称空间的一部分,该名称空间用于“ 组件和控件的运行时和设计时行为 ”。不要 INotifyPropertyChanged在模型中使用,仅在ViewModels中使用。链接到文档:docs.microsoft.com/en-us/dotnet/api/system.componentmodel
Igor Popov

1
我知道以前的帖子,但是在使用MVVM启动新项目时,我经常回到它。我最近开始更加严格地执行“单一责任原则”。模式是一种责任。成为模特。将INotifyPropertyChanged添加到模型后,它就不再遵循“单一职责原则”。ViewModel存在的全部原因是让模型成为模型,让模型承担单一责任。
Rhyous


13

我认为MVVM的命名很差,将ViewModel称为ViewModel会使许多人错过设计良好的体系结构的重要功能,该体系结构是一个DataController,无论谁尝试触摸它,它都可以控制数据。

如果您将View-Model视为DataController的更多部分,并实现了其中DataController是唯一接触数据的项的体系结构,那么您将永远不会直接接触数据,而始终使用DataController。DataController对UI有用,但不一定仅对UI有用。它用于业务层,UI层等。

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

您最终得到了这样的模型。甚至企业也只能使用ViewModel来触摸数据。然后,您的难题就消失了。


3
如果您的数据仅在DataController更改时更改,那就太好了。如果数据来自数据库或其他可以提供其他更改途径的数据存储,则可能需要一种方法来通知VIEWMODEL(模式中的DataController)和VIEW何时发生。您可以使用DataController进行轮询,也可以将其从某个外部进程推送到您的DataModel,并允许您的DataModel将更改通知发送到您的DataController。
ScottCher 2011年

4
你说得很对。设计模式非常高。在大多数情况下,设计模式会使您做正确的事,但是有时它们会不时地使您误入歧途。您永远都不要避免做正确的事,因为它超出了设计模式。
罗伊斯2011年

您还将在控制数据模型和数据模型时推送到DataController,并告诉它进行更新。
Rhyous

同样,MVVM中的模型应根据UI(例如DTO)的需要保持特定。因此,任何数据库或复杂的业务逻辑都应在不同的层中进行,然后应通过视图模型提供原始数据。
代号杰克

9

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

我们的验证引擎在很大程度上依赖于通过该机制获得的属性更改通知,并且效果很好。显然,如果您使用的业务对象以外的其他实现(对变更的通知对操作而言并不那么重要),则可能有其他方法可以检测到业务模型中的变更。

我们还具有视图模型,可以在需要时传播来自模型的更改,但是视图模型本身正在侦听基础模型的更改。


3
您如何准确地将Model的OnPropertyChanged传播到ViewModel的OnPropertyChanged?当ViewModel的属性名称与Model的属性名称不同时,我遇到了问题-那么将需要某种名称到名称的映射,对吗?
Martin Konicek'2009年

这并不是什么真正复杂的事情,我们只是转发事件。我想如果名称不同,则可以使用查找表。如果更改不是一对一映射,则可以简单地钩住事件,然后在处理程序中触发必要的事件。
史蒂夫·米切姆

6

我同意Paulo的回答,INotifyPropertyChanged在模型中实施是完全可以接受的,甚至被Microsoft建议-

通常,模型实现了使绑定到视图变得容易的功能。这通常意味着它通过INotifyPropertyChangedINotifyCollectionChanged接口支持属性和集合更改通知。代表对象集合的模型类通常从ObservableCollection<T>该类派生, 该类提供了INotifyCollectionChanged接口的实现 。

尽管您可以自行决定是否要使用这种类型的实现,但是请记住-

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

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

摘自-http: //msdn.microsoft.com/zh-cn/library/gg405484(PandP.40).aspx

我曾在一些尚未INotifyPropertyChanged在模型中实现的项目中工作过,因此我们遇到了很多问题。VM中需要不必要的属性复制,同时我们必须在将基础对象传递给BL / DL之前更新基础对象(具有更新的值)。

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



3

如果您希望使用MV-VM,我认为答案很明确。

请参阅:http : //msdn.microsoft.com/en-us/library/gg405484(v=PandP.40).aspx

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

“视图通过数据绑定,命令和更改通知事件与视图模型进行交互。视图模型查询,观察和协调对模型的更新,并根据需要转换,验证和汇总数据以在视图中显示。”


4
此报价易于解释。我想你应该添加你的解释,使你的答案明确:-)
索伦Boisen

@John D:那篇文章只是对MVVM的一种解释,以及一种实现它的方法,它没有定义MVVM。
akjoshi

此外,如果您阅读全文,则它会如下定义Model类:“通常,模型实现了使绑定到视图更容易的功能。这通常意味着它通过INotifyPropertyChanged和INotifyCollectionChanged接口支持属性和集合更改通知。代表对象集合的模型类通常从ObservableCollection <T>类派生,该类提供INotifyCollectionChanged接口的实现。”
akjoshi

2

我会在您的ViewModel中说。它不是模型的一部分,因为模型与UI无关。该模型应“与业务无关”


2

如果模型在ViewModel中明确公开,则可以在模型中实现INPC。但是通常,ViewModel将模型包装起来是他自己的类,以减少模型的复杂性(这对绑定没有用)。在这种情况下,应在ViewModel中实现INPC。


1

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

我注意到了几个优点和缺点:

优点

通知程序处于业务模型中

  1. 按照域驱动,这是正确的。它应该决定何时加薪,何时不加薪。

缺点

该模型具有属性(数量,费率,佣金,总费用)。总费用是使用数量,费率,佣金变化来计算的。

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

  2. 如果在业务层中分配了速率,数量,则再次调用通知程序。

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


由于您列出的所有缺点,我们只是认为在我们较大的WPF项目中以模型实现INPC是一个错误的决定。他们应该只处理持久层。验证,更改通知和计算出的属性等所有其他内容都应在ViewModel中处理。现在,我清楚地了解到,在ViewModel中重复模型属性并不总是违反DRY原理。
try2fly.b4ucry

1

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

当您拥有带有大量属性的简单模型时,可以实现INPC。简单地说,我的意思是该模型看起来很像POCO

如果您的模型更复杂并且生活在交互式模型域中-模型引用模型,订阅其他模型的事件-将模型事件实现为INPC就是一场噩梦。

使自己处于一些必须与其他模型进行协作的模型实体的位置。您有各种活动要订阅。所有这些都实现为INPC。想象一下您拥有的那些事件处理程序。大量的if子句和/或switch子句。

INPC的另一个问题。您应将应用程序设计为依赖于抽象而非实现。通常使用接口完成此操作。

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

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的意图很明确:“我可以告诉您IsConnected属性的值可能会更改”。


1

只需使用 INotifyPropertyChange在您的视图模型中而不在模型中使用

该模型通常使用IDataErrorInfo来处理验证错误,因此只要保留在ViewModel中,您就可以走上MVVM的道路。


1
具有多个属性的模型呢?您正在VM中重复代码。
路易斯

0

假设视图中对象的引用已更改。您将如何通知所有要更新的属性以显示正确的值?OnPropertyChanged在我看来,调用视图中所有对象的属性都是垃圾。

所以我要做的就是让当一个属性的值改变,在我看来,我喜欢使用绑定的对象本身通知任何人Object.Property1Object.Property2而上。这样,如果我只想更改视图中当前维护的对象,就可以这样做OnPropertyChanged("Object")

为了避免在对象加载过程中产生数百条通知,我有一个专用的布尔值指示符,该指示符在加载过程中将其设置为true,这是从对象的对象中检查出来的OnPropertyChanged,什么也不做。


0

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

看这里


1
嗯..没有 模型不能是xml文件或数据库。并且模型不用于提供数据。否则,不应将其称为“模型”,而应称为“数据”。模型用于描述数据。很好的自我解释,不是吗?:)
塔拉斯

1
如果您有更好的答案,请分享!我们都是在这里分享知识而不是竞争
亚当

0

恕我直言,我认为viewmodel实现,INotifyPropertyChange并且该模型可以在不同的“级别”上使用通知。

例如,对于某些文档服务和一个文档对象,您有一个documentChanged事件,视图模型会监听该事件以清除并重建视图。在编辑视图模型中,您可以对文档的属性进行属性更改以支持视图。如果该服务对保存的文档执行了很多操作(更新更改日期,最后用户等),您很容易获得Ipropertychanged事件的重载,仅更改文档就足够了。

但是,如果您INotifyPropertyChange在模型中使用,我认为在您的视图模型中中继它而不是直接在您的视图中订阅它是一个好习惯。在这种情况下,当模型中的事件发生更改时,您只需更改视图模型,视图就保持不变。


0

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.