简体   繁体   English

MVVM - Model或ViewModel中的PropertyChanged?

[英]MVVM - PropertyChanged in Model or ViewModel?

I have gone through a few MVVM tutorials and I have seen this done both ways. 我已经完成了一些MVVM教程,我已经看到了两种方式。 Most use the ViewModel for PropertyChanged (which is what I have been doing), but I came across one that did this in the Model. 大多数人使用ViewModel for PropertyChanged(这是我一直在做的),但我遇到了一个在模型中做到这一点。 Are both methods acceptable? 两种方法都可以接受吗 If so, what are the benefits/drawbacks of the different methods? 如果是这样,不同方法的好处/缺点是什么?

Microsoft's Patterns and Practices, the inventor of MVVM, and I all disagree with the chosen answer. 微软的模式和实践,MVVM的发明者,我都不同意所选择的答案。

Typically, the model implements the facilities that make it easy to bind to the view. 通常,模型实现了可以轻松绑定到视图的工具。 This usually means it supports property and collection changed notification through the INotifyPropertyChanged and INotifyCollectionChanged interfaces. 这通常意味着它通过INotifyPropertyChanged和INotifyCollectionChanged接口支持属性和集合更改通知。 Models classes that represent collections of objects typically derive from the ObservableCollection class, which provides an implementation of the INotifyCollectionChanged interface. 表示对象集合的模型类通常派生自ObservableCollection类,该类提供INotifyCollectionChanged接口的实现。

-- Microsoft Patterns and Practices: http://msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx#sec4 - Microsoft模式和实践: http//msdn.microsoft.com/en-us/library/gg405484%28v=pandp.40%29.aspx#sec4

At this point data binding comes into play. 此时数据绑定开始发挥作用。 In simple examples, the View is data bound directly to the Model. 在简单示例中,View是直接绑定到Model的数据。 Parts of the Model are simply displayed in the view by one-way data binding. 模型的各个部分通过单向数据绑定简单地显示在视图中。 Other parts of the model can be edited by directly binding controls two-way to the data. 可以通过直接将控件双向绑定到数据来编辑模型的其他部分。 For example, a boolean in the Model can be data bound to a CheckBox, or a string field to a TextBox. 例如,Model中的布尔值可以是绑定到CheckBox的数据,也可以是绑定到TextBox的字符串字段。

-- John Gossman, inventor of MVVM: http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx - MVVM的发明者John Gossman: http//blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx

My own article: http://www.infoq.com/articles/View-Model-Definition 我自己的文章: http//www.infoq.com/articles/View-Model-Definition


It is an anti-pattern to have a "view-model" that just wraps a model and exposes the same list of properties. 具有“视图模型”的反模式只是包装模型并公开相同的属性列表。 The view-model's job is to call external services and expose the individual and collections of models that those services return. 视图模型的工作是调用外部服务并公开这些服务返回的个体和模型集合。

Reasons: 原因:

  1. If the model is updated directly, the view-model won't know to fire a property changed event. 如果直接更新模型,则视图模型将不知道触发属性更改事件。 This causes the UI to go out of sync. 这会导致UI不同步。
  2. This severely limits your options for sending messages between parent and child view-models. 这严重限制了在父视图模型和子视图模型之间发送消息的选项。
  3. If the model has its own property changed notification, #1 and 2 aren't a problem. 如果模型有自己的属性更改通知,则#1和2不是问题。 Instead, you have to worry about memory leaks if the wrapper VM goes out of scope but the model doesn't. 相反,如果包装器VM超出范围但模型没有,则必须担心内存泄漏。
  4. If your models are complex, with lots of children objects, then you have to walk the entire tree and create a second object graph that shadows the first one. 如果你的模型很复杂,有很多子对象,那么你必须遍历整个树并创建第二个对象图,它会遮蔽第一个。 This can be quite tedious and error prone. 这可能非常繁琐且容易出错。
  5. Wrapped collections are particularly difficult to work with. 包裹的集合特别难以使用。 Any time something (UI or backend) inserts or removes an item from a collection, the shadow collection needs to be updated to match. 任何时候(UI或后端)从集合中插入或删除项目,都需要更新影子集合以匹配。 This kind of code is really hard to get right. 这种代码真的很难搞定。

That isn't to say you will never need a view-model that wraps a model. 这并不是说你永远不需要一个包装模型的视图模型。 If your view-model exposes properties that are significantly different from the model and can't just be papered over with a IValueConverter, then a wrapping view-model makes sense. 如果您的视图模型公开了与模型明显不同的属性,并且不能仅使用IValueConverter进行包装,那么包装视图模型是有意义的。

Another reason you may need a wrapping view-model is that your data classes don't support data binding for some reason. 您可能需要包装视图模型的另一个原因是您的数据类由于某种原因不支持数据绑定。 But even then, it is usually better to just create a normal, bindable model and copy the data from the original data classes. 但即便如此,通常最好只创建一个普通的可绑定模型并从原始数据类中复制数据。

And of course your view-model is going to have UI specific properties such as which item in a collection is currently selected. 当然,您的视图模型将具有UI特定属性,例如当前选择集合中的哪个项目。

The INotifyPropertyChanged (INPC) interface is used for Binding . INotifyPropertyChanged (INPC)接口用于Binding

So, in the average case, you want to implement it in your ViewModel . 因此,在一般情况下,您希望在ViewModel实现它。

The ViewModel is used to decouple the Model from your View , so there is no need to have INPC in your Model , as you do not want Bindings to your Model . ViewModel用于将ModelView分离,因此不需要在Model使用INPC,因为您不希望BindingsModel

In most cases, even for smaller properties, you still have a very small ViewModel . 在大多数情况下,即使对于较小的属性,您仍然拥有非常小的ViewModel

If you want a solid base for MVVM , you are probably going to use some kind of MVVM Framework like caliburn.micro . 如果你想要一个坚实的MVVM基础,你可能会使用某种MVVM框架,如caliburn.micro Using it will give you a ViewModelBase (or here NotifyPropertyChangedBase ) so that you do not have to implement those interface members yourself and can just use NotifyOfPropertyChange(() => MyProperty) , which is way easier and less error prone. 使用它将为您提供ViewModelBase (或此处为NotifyPropertyChangedBase ),这样您就不必自己实现这些接口成员,只需使用NotifyOfPropertyChange(() => MyProperty) ,这样更容易,更不容易出错。

UPDATE As there seem to be many Windows Forms developers out there, here is an excellent article that will give deeper understanding of what MVVM is about: MSDN Magazine on MVVM 更新由于似乎有许多Windows窗体开发人员,这里有一篇很好的文章,将更深入地了解MVVM的内容: MVVM上的MSDN杂志

I have linked especially the part about the datamodel, that the question is about. 我特别关注了关于数据模型的部分,问题是关于。

Would absolutely agree with Jonathan Allen. 绝对会同意Jonathan Allen的观点。

If you have nothing to add to your 'View-Model' (Commands, view-specific properties that affect presentation etc.) then I would definitely implement INotifyPropertyChanged in the model and expose that directly (if you can - the 'model' may not be yours). 如果您没有任何内容可以添加到“视图模型”(命令,特定于视图的属性,影响演示等),那么我肯定会在模型中实现INotifyPropertyChanged并直接公开(如果可以的话 - '模型'可能不会是你的)。 Not only do you end up repeating a lot of boilerplate code, keeping the two in sync is an absolute pain. 你最终不仅重复了很多样板代码,保持两者同步是一个绝对的痛苦。

INotifyPropertyChanged isn't a view-specific interface, it only does exactly what the name suggests - raises an event when a property changes. INotifyPropertyChanged不是特定于视图的界面,它只是完全按照名称建议 - 在属性更改时引发事件。 WinForms, WPF and Silverlight just happen to support it for Binding - I've certainly used it in for non-presentational purposes! WinForms,WPF和Silverlight恰好支持它进行绑定 - 我当然用它来进行非表示目的!

As a rule of thumb, any object that you will bind to (even if you don't need Two-Way binding and Property Change Notification), must implement INotifyPropertyChanged . 根据经验,您将绑定到的任何对象(即使您不需要双向绑定和属性更改通知)也必须实现INotifyPropertyChanged This is because failing to do so May cause memory leaks 这是因为如果不这样做可能会导致内存泄漏

INotifyPropertyChanged should be implemented by all the types that are consumed by the view (unless if it only has constant values of course). INotifyPropertyChanged应该由视图使用的所有类型实现(除非它当然只有常量值)。

Do you return models (not viewmodels) to a view? 你是否将模型(不是视图模型)返回给视图? If yes, then it should implement INotifyPropertyChanged. 如果是,那么它应该实现INotifyPropertyChanged。

While I'm generally in favor of a model implementing INPC the call for INPC in a composite view model is when it exposes inferred properties that are bindable to the view. 虽然我通常赞成实现INPC的模型,但在复合视图模型中调用INPC时,它会公开可绑定到视图的推断属性。 IMO since INPC is baked into System.dll, a model implementing it may be considered POCO. IMO因为INPC被编入System.dll,实现它的模型可能被认为是POCO。 For collections there is a performance benefit of model based INPC. 对于集合,基于模型的INPC具有性能优势。 On a 64 bit platform a wrapper VM would have an 8 factor multiplier on the byte size (load the SOS debugger extension for actual size) of ObservableCollection<ViewModel> compared to ObservableCollection<Model>. 在64位平台上,与ObservableCollection <Model>相比,封装器VM在ObservableCollection <ViewModel>的字节大小(加载实际大小的SOS调试器扩展)上将具有8因子乘数。

The creator of MVVM, JohnGossman, states in this blog article (mentioned by @Jonathan Allen) that: MVVM的创建者JohnGossman在这篇博客文章(@Jonathan Allen提到)中指出:

In simple examples, the View is data bound directly to the Model. 在简单示例中,View是直接绑定到Model的数据。 Parts of the Model are simply displayed in the view by one-way data binding. 模型的各个部分通过单向数据绑定简单地显示在视图中。 Other parts of the model can be edited by directly binding controls two-way to the data. 可以通过直接将控件双向绑定到数据来编辑模型的其他部分。 For example, a boolean in the Model can be data bound to a CheckBox, or a string field to a TextBox. 例如,Model中的布尔值可以是绑定到CheckBox的数据,也可以是绑定到TextBox的字符串字段。

In practice however, only a small subset of application UI can be data bound directly to the Model, especially if the Model is a pre-existing class or data schema over which the application developer has no control. 但实际上,只有一小部分应用程序UI可以直接绑定到模型,特别是如果模型是应用程序开发人员无法控制的预先存在的类或数据模式。

I prefer to follow practices that are still applicable when the app scales. 我更喜欢遵循应用扩展时仍然适用的做法。 If "In practice [...], only a small subset of application UI can be data bound directly to the Model", this doesn't seem to be a good practice to follow as I don't plan to tackle only "simple cases" or "a small subset of application UI". 如果“在实践中[...],只有一小部分应用程序UI可以直接绑定到模型”,这似乎不是一个好的做法,因为我不打算只解决“简单”案例“或”应用程序UI的一小部分“。
For "simple cases" I wouldn't even be using MVVM to begin with. 对于“简单的情况”,我甚至不会使用MVVM开始。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM