I have gone through a few MVVM tutorials and I have seen this done both ways. Most use the ViewModel for PropertyChanged (which is what I have been doing), but I came across one that did this in the Model. Are both methods acceptable? If so, what are the benefits/drawbacks of the different methods?
问题:
回答1:
The INotifyPropertyChanged
(INPC) interface is used for Binding
.
So, in the average case, you want to implement it in your 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
.
In most cases, even for smaller properties, you still have a very small ViewModel
.
If you want a solid base for MVVM
, you are probably going to use some kind of MVVM Framework like 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.
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
I have linked especially the part about the datamodel, that the question is about.
回答2:
Microsoft's Patterns and Practices, the inventor of MVVM, and I all disagree with the chosen answer.
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. Models classes that represent collections of objects typically derive from the ObservableCollection class, which provides an implementation of the INotifyCollectionChanged interface.
-- Microsoft Patterns and Practices: 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. 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.
-- John Gossman, inventor of MVVM: http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx
My own article: 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:
- 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.
- This severely limits your options for sending messages between parent and child view-models.
- If the model has its own property changed notification, #1 and 2 aren't a problem. Instead, you have to worry about memory leaks if the wrapper VM goes out of scope but the model doesn't.
- 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.
- 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. 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.
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.
回答3:
Would absolutely agree with 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). 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. WinForms, WPF and Silverlight just happen to support it for Binding - I've certainly used it in for non-presentational purposes!
回答4:
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
. This is because failing to do so May cause memory leaks
回答5:
INotifyPropertyChanged should be implemented by all the types that are consumed by the view (unless if it only has constant values of course).
Do you return models (not viewmodels) to a view? If yes, then it should implement INotifyPropertyChanged.
回答6:
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. IMO since INPC is baked into System.dll, a model implementing it may be considered POCO. For collections there is a performance benefit of model based 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>.
回答7:
The creator of MVVM, JohnGossman, states in this blog article (mentioned by @Jonathan Allen) that:
In simple examples, the View is data bound directly to the 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.
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.
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".
For "simple cases" I wouldn't even be using MVVM to begin with.