Basic concepts of MVVM-- what should a ViewModel do?
Trying to grasp the concepts of MVVM, I have already read several blogs and looked at a few projects.
From what I understand, a View is dumb, it just knows how to present something that is passed to it.
Models are just the plain data, and a ViewModel is something that acts like a padding in between the two, that it should get information from the Model and pass it onto the View, and the View should know how to present it. Or the other way around, if the information in the View changes, it should pass on the change to the Model.
But I still have no idea how to apply the concept. Can someone explain a very simple scenario so I can grasp the concept? I've already looked at several projects, but it still doesn't make complete sense, so if someone could write it out in plain English, that would be nice.
I like to think of it this way:
Views, as you say, are dumb. Josh Smith, writer of the seminal and often linked MSDN article on MVVM, has said that views are "the clothes that data wears." Views never actually contain data or directly manipulate it, they are just bound to properties and commands of your viewmodels.
Models are objects that model the domain of your application, as in business objects. Is your application a music store? Perhaps your model objects will be artists, albums and songs. Is your application an org-chart browser? Perhaps your model objects will be managers and employees. These model objects are not related to any kind of visual rendering, and they aren't even directly related to the application you're putting them into - your model objects should make sense completely on their own as a family of objects that represent some kind of domain. The model layer also typically includes things like service accessors.
This brings us to Viewmodels. What are they? They are objects that model a GUI application, meaning they provide data and functionality to be used by views. They are what define the structure and behavior of the actual application you are building. For the model objects, the domain is whatever domain you choose (music store, org-chart browser, etc.), but for the viewmodel, the domain is a graphical application. Your viewmodels are going to encapsulate the behavior and the data of everything your application does. They are going to expose objects and lists as properties, as well as things like Commands. A command is just a behavior (at its simplest, a method call) wrapped up into an object that carries it around - this idea is important because views are driven by databinding, which attaches visual controls to objects. In MVVM, you don't give a button a Click handler method, you bind it to a command object (served up from a property in a viewmodel) that contains the functionality you want to run when you click it.
For me, the most confusing bits were the following:
- Even though the viewmodels are models of a graphical application, they don't directly reference or use visual concepts. For example, you don't want references to Windows controls in your ViewModels - those things go in the view. ViewModels simply expose data and behaviors to controls or other objects that will bind to them. For example - do you have a view with a ListBox in it? Your viewmodel is almost certainly going to have some kind of collection in it. Does your view have buttons? Your viewmodel is almost certainly going to have some commands in it.
- There are a few kinds of objects that could be considered "viewmodels". The simplest kind of viewmodel to understand is one that directly represents a control or a screen in a 1:1 relationship, as in "screen XYZ has a textbox, a listbox, and three buttons, so the viewmodel needs a string, a collection, and three commands." Another kind of object that fits in the viewmodel layer is a wrapper around a model object that gives it behavior and makes it more usable by a view - this is where you get into the concepts of "thick" and "thin" viewmodel layers. A "thin" viewmodel layer is a set of viewmodels that expose your model objects directly to the views, meaning the views end up binding directly to properties on the model objects. This can work for things like simple, read-only views, but what if you want to have behavior associated with each object? You don't want that in the model, because the model isn't related to the application, it's only related to your domain. You can put it in an object that wraps your model object and offers up more binding-friendly data and behaviors. This wrapper object is also considered a viewmodel, and having them results in a "thicker" viewmodel layer, where your views never end up directly binding to anything on a model class. Collections will contain viewmodels that wrap models instead of just containing models themselves.
The rabbit hole goes deeper - there are lots of idioms to figure out like ValueConverters that keep MVVM working, and there's a lot to apply when you start thinking about things like Blendability, testing, and how to pass data around in your app and ensure that each viewmodel has access to the behavior it needs (this is where dependency injection comes in), but hopefully the above is a good start. The key is to think about your visuals, your domain, and the structure and behavior of your actual application as three different things.
Using this incredibly helpful article as the source, here is a summary for View, ViewModel, and Model.
View:
The view is a visual element, such as a window, page, user control, or data template. The view defines the controls contained in the view and their visual layout and styling.
The view references the view model through its
DataContext
property. The controls in the view are data bound to the properties and commands exposed by the view model.The view may customize the data binding behavior between the view and the view model. For example, the view may use value converters to format the data to be displayed in the UI, or it may use validation rules to provide additional input data validation to the user.
The view defines and handles UI visual behavior, such as animations or transitions that may be triggered from a state change in the view model or via the user's interaction with the UI.
The view's code-behind may define UI logic to implement visual behavior that is difficult to express in XAML or that requires direct references to the specific UI controls defined in the view.
NOTE:
Because the view model should have no explicit knowledge of the specific visual elements in the view, code to programmatically manipulate visual elements within the view should reside in the view's code-behind or be encapsulated in a behavior.
View Model:
The view model is a non-visual class and does not derive from any WPF or Silverlight base class. It encapsulates the presentation logic required to support a use case or user task in the application. The view model is testable independently of the view and the model.
The view model typically does not directly reference the view. It implements properties and commands to which the view can data bind. It notifies the view of any state changes via change notification events via the
INotifyPropertyChanged
andINotifyCollectionChanged
interfaces.The view model coordinates the view's interaction with the model. It may convert or manipulate data so that it can be easily consumed by the view and may implement additional properties that may not be present on the model. It may also implement data validation via the
IDataErrorInfo
orINotifyDataErrorInfo
interfaces.The view model may define logical states that the view can represent visually to the user.
NOTE:
Anything that is important to the logical behavior of the application should go into the view model. Code to retrieve or manipulate data items that are to be displayed in the view through data binding should reside in the view model.
Model:
Model classes are non-visual classes that encapsulate the application's data and business logic. They are responsible for managing the application's data and for ensuring its consistency and validity by encapsulating the required business rules and data validation logic.
The model classes do not directly reference the view or view model classes and have no dependency on how they are implemented.
The model classes typically provide property and collection change notification events through the
INotifyPropertyChanged
andINotifyCollectionChanged
interfaces. This allows them to be easily data bound in the view. Model classes that represent collections of objects typically derive from theObservableCollection<T>
class.The model classes typically provide data validation and error reporting through either the
IDataErrorInfo
orINotifyDataErrorInfo
interfaces.The model classes are typically used in conjunction with a service or repository that encapsulates data access and caching.