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

时间:2009-04-21 11:57:01

标签: c# mvvm inotifypropertychanged

我使用过的大多数MVVM示例都有 Model 实现INotifyPropertyChanged,但在Josh Smith's CommandSink example 中,ViewModel实现了INotifyPropertyChanged

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

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

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

17 个答案:

答案 0 :(得分:131)

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

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

然而,实现目标有不同的方法,但我总是赞成简单并避免冗余。

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

每当你看到某人声称“你不能做到这一点或那个”时,这是一个底线,这表明他们不知道他们在谈论什么。

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

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

答案 1 :(得分:97)

我说的恰恰相反,我总是将INotifyPropertyChanged放在我的ViewModel上 - 你真的不想用像INotifyPropertyChanged这样的WPF特定功能来污染你的模型应该坐在ViewModel中。

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

答案 2 :(得分:27)

在M-V-VM中,ViewModel始终(Model not always)实现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项目的一个很好的起始模板。

答案 3 :(得分:10)

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

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

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

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

答案 4 :(得分:8)

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

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

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

答案 5 :(得分:3)

但有时(如本演示文稿link text)模型是服务,它在线为应用程序提供一些数据,然后你需要通过事件来实现新数据到达或数据发生变化的通知......

答案 6 :(得分:3)

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

  

通常,该模型实现了易于使用的设施   绑定到视图。这通常意味着它支持属性和   集合通过INotifyPropertyChanged更改了通知   INotifyCollectionChanged接口。表示的模型类   对象集合通常来自   ObservableCollection<T>类,它提供了一个实现   INotifyCollectionChanged界面。

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

  

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

     

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

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

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

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

答案 7 :(得分:3)

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

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

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

  

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

答案 8 :(得分:2)

我会在你的ViewModel中说。它不是模型的一部分,因为模型是UI不可知的。该模型应该是“除业务无关外的一切”

答案 9 :(得分:1)

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

模型通常使用IDataErrorInfo来处理验证错误,因此只需保留在ViewModel中,就可以了解MVVM。

答案 10 :(得分:1)

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

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

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

让自己处于某个模型实体的位置,必须与其他模型进行协作。您有各种要订阅的活动。所有这些都是作为INPC实现的。想象一下你拥有的那些事件处理程序。一个巨大的if-clauses和/或交换条款的级联。

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

答案 11 :(得分:0)

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

答案 12 :(得分:0)

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

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

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

答案 13 :(得分:0)

imho我认为viewmodel实现了INotifyPropertyChange,模型可以在不同的“级别”使用通知。

例如 对于某些文档服务和文档对象,您有一个viewChanged事件,viewmodel侦听该事件以清除并重建视图。 在编辑视图模型中,您具有用于支持视图的文档属性的属性更改。如果服务在保存文档(更新更改日期,最后一个用户等)上做了很多事情,那么您很容易获得Ipropertychanged事件的重载,只需一个文件就可以了。

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

答案 14 :(得分:0)

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

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

优势

通知程序属于商业模式

  1. 根据域名驱动,这是对的。它应决定何时加注,何时不加注。
  2. <强>缺点

    该模型具有属性(数量,费率,佣金,全权)。 Totalfrieght使用数量,费率,佣金变化计算。

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

    2. 如果在业务层中分配了rate,qty,则会再次调用通知程序。

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

答案 15 :(得分:0)

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

所以我所做的是让对象本身在属性中的值发生更改时通知任何人,并且在我的视图中我使用Object.Property1Object.Property2之类的绑定。这样,如果我只想更改我视图中当前维护的对象,我只需OnPropertyChanged("Object")

为了避免在加载对象期间发出数百条通知,我有一个私有布尔指示符,我在加载过程中将其设置为true,从对象的OnPropertyChanged检查并且什么都不做。

答案 16 :(得分:0)

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

see here