Freigeben über


Model-View-ViewModel (MVVM) Applications: General Introduction

MVVM is a central concept in WPF, Silverlight, WindowsPhone and Windows 8 development, so I decided to take some time and bubble up some of the content in the WPF MVVM Toolkit , which we created some time ago.

Model-View separation is by no means a novel idea in the software engineering industry—the idea has been around for at least 25 years. In the past few years, there has been a lot of interest in model-view architectures, fuelled both by the growing complexity of modern software systems and by the necessity to display UI on various devices, while reusing the same underlying business logic.

This post outlines the benefits of model-view separation. It is essentially the same content as the content included in Part 1 of the documentation, which goes with the WPF MVVM Toolkit.

1. Introduction to the Model-View-X Paradigm

A well-designed application is an application that is easy to develop, test, maintain, and evolve. Creating such an application typically involves some form of separation and encapsulation of responsibilities. A common approach involves separation of the UI (the views) from the business logic (the model). In fact, that approach is so common that there are a number of stock solutions, collectively known as the model-view design patterns:

  • Model-View-Controller (MVC) is the granddaddy of them all, dating back to 1979. In MVC, the Controller creates the Views and the Model. The Views register with the Controller, which also has a reference to the Model. A View notifies the Controller of user interactions, and the Controller in turn queries the View for its state and updates the Model appropriately. The Controller notifies the Views when there are changes in the Model, and the Views query the controller to update their states.

  • Model-View-ViewModel (MVVM) is a derivative of MVC that takes advantage of particular strengths of the Windows Presentation Foundation (WPF) architecture to separate the Model and the View by introducing an abstract layer between them: a “Model of the View,” or ViewModel. The pattern was first introduced by John Gossman – the architect on the Blend/WPF/Silverlight team – and derives from the Smalltalk ApplicationModel pattern, as does the PresentationModel pattern described by Martin Fowler. It was first utilized by the Expression team (John was part of that team at the time) as they developed version 1 of Expression Blend. Without the WPF/Silverlight-specific aspects, the Model-View-ViewModel pattern is identical to PresentationModel.

    In MVVM, the View and the ViewModel are typically instantiated by the container application. The View keeps a reference to the ViewModel. The ViewModel exposes commands and observable entities (“bindables”) that the View binds to (or to put it in programming terms, view.DataContext = viewModel;). User interactions with the View trigger commands on the ViewModel, and updates in the ViewModel are propagated to the View through data binding.

 

2. Justification

2.1. Simplistic Application Design

To demonstrate the use of the Model-View-X architecture, let’s consider a simple contacts application. The application has two windows (views): a table view, where you can see all entries in the contact book in a table, and a record view, where you can see individual records.

clip_image002

Fig. 1 UI of the contacts application

The Model-View separation for this particular problem feels natural, where the Model is the contact book and the Views are the two windows shown in Figure 1. So the trivial design (which is a two-tier Model-View-only design) would look as shown on Figure 2.

clip_image002

Fig. 2 Simplistic application design – the views are directly connected to the model

As shown, ContactBook keeps a list of views (which are added and removed with AttachView and DetachView). When a contact changes, ContactBook notifies all views by calling their Update methods, and all views update themselves by calling the model's GetContacts method. Upon instantiation, RecordView and TableView are given a reference to ContactBook, which they keep as a field (contactBook).

Notice how the two views are tightly coupled with the ContactBook class. Addition of business logic would only increase the level of coupling.

 

2.2. Simplistic Application Design – WPF/Silverlight Version

Before examining the design further, let’s convert the design to a WPF/Silverlight-friendly design. WPF/Silverlight provides the following native facilities that can be utilized directly:

  • Databinding is the ability to bind UI elements to any data.
  • Commands provide the ability to notify the underlying data of changes in the UI.

Using these facilities, the WPF/Silverlight-friendly design is changed as follows:

clip_image002[6]

Fig. 3 WPF/Silverlight-friendly simplistic application design

In the modified design in Figure 3, the views derive from UserControl (there is no need for an abstract View class) and ContactBook no longer needs to maintain a list of views—ContactBook no longer needs to know about the views. Instead, the views point to ContactBook as their DataContext and use WPF data-binding to bind to the list of contacts.

You will also notice that List<Contact> has been replaced with ObservableCollection<Contact> to allow the views to bind to the ContactBook using WPF data-binding mechanisms.

Notice how WPF’s data-binding mechanism allows us to create a much more a loosely coupled design (at least until further addition of business logic).

 

2.3. Problems with the Simplistic Design

Things get more complicated if we introduce the following new functionality:

  1. Track the selected item, so that we update RecordView whenever the selection in TableView changes, and vice versa.
  2. Enable or disable parts of the UI of RecordView and TableView based on some rule (for example, highlight any entry that has an e-mail in the live.com domain).

Feature (a) can be implemented by adding aCurrentEntry property directly in ContactBook. This solution breaks down, however, if you have more than one instance of the UI connected to the same ContactBook. Feature (b) can be implemented in the views (RecordView and TableView). The problem if we do that, however, is that if we want to change the rule, then we would need to change both views.

Gradually it becomes apparent that we need a third layer in our application. We need a layer between the views and ContactBook that stores state shared by the views. We need a meta-view of sorts.

 

2.4. Enter Model-View-ViewModel (MVVM)

A ViewModel can serve as the third layer, providing an abstraction that acts as a meta-view(a model of a view), storing state and policy that are shared by a group of views. By introducing the concept of a ViewModel, our design changes to the following:

clip_image002[8]

Fig. 4 Application design using a ViewModel

In this design, the views know of the ViewModel and bind to its data, to be able to reflect any changes in it. The ViewModel has no reference to the views—it holds only a reference to the model.

For the views, the ViewModel acts both as a façade to the model, but also as a way to share state between views (selectedContacts in the example). In addition, the ViewModel often exposes commands that the views can bind to and trigger.This use of commands is covered in the next section.

 

2.5. The use of commanding in MVVM Applications

WPF and Silverlight introduce a new input programming mechanism: commanding. Commands allow loose coupling between the origin and the handling of an action. Multiple sources can invoke the same command, and the same command can be handled differently depending on the target. All commands implement the ICommand interface.

For UI actions, WPF provides a specialized RoutedCommand class. A RoutedCommand is routed through the UI element tree, so the target of a RoutedCommand must be part of the UI element tree. In MVVM designs, however, command targets are often implemented in the ViewModels, which in turn are not typically part of the UI element tree. This requires the introduction of a different kind of command—the DelegateCommand—that implements ICommand and can have non-UI elements as its target.

clip_image002[10]

Fig. 5 Application design using a ViewModel, exposing bindable commands

The DelegateCommand construct was originally introduced during the development of Microsoft Expression Blend version 1. A generic version of DelegateCommand was later released as part of the Composite Application Guidance for WPF on MSDN. Note that some MVVM sources refer to the same construct as RelayCommand.

A DelegateCommand is an ICommand that allows the views to bind to the ViewModel. Even though the diagram above does not show this explicitly, all ICommands exposed by the ViewModel are DelegateCommands. For example, the code for the AddContactCommand in the ViewModel may look as follows:

image

 

3. Benefits and Consequences of Using MVVM

The benefits of MVVM are the following:

  • A ViewModel provides a single store for presentation policy and state, thus improving the reusability of the Model (by decoupling it from the Views) and the replaceability of the Views (by removing specific presentation policy from them).
  • The Model-View-ViewModel design improves the overall testability of the application. One can easily create unit tests that target the Model and the ViewModel layers. The MVVM design also improves the “mockability” of the application, by allowing easy run-time simulation of the tiers, which is crucial to testing complex software products.
  • The Model-View-ViewModel design is a very loosely coupled design. The View holds a reference to the ViewModel, and the ViewModel holds a reference to the Model. The rest is done by the data-binding and commanding infrastructure of WPF.

The consequences of MVVM are the following:

  • The typical relationship between a ViewModel and the corresponding Views is one-to-many, but there are also situations where that is not necessarily true. In general, any business logic and input-handling business logic (selection tracking, and so on) is kept in the ViewModel.
  • There are situations when one ViewModel is aware of another ViewModel within the same application. Such situations arise when there is a master-subordinate relationship between two ViewModels or when a ViewModel represents a single item (for example, the visual representation state of a single contact). When this happens, one ViewModel can represent a collection of ViewModels. Such a case in demonstrated in the 2nd part of documentation that comes with the WPF MVVM Toolkit.

 

4. Next Steps and References

Hopefully this post provides a simple introduction to the pattern. As a next step, download the WPF MVVM Toolkit, review the walk-through and the provided code and go write some apps.

It is important to note that there are a number of other (more full-featured) MVVM toolkits out there. Perhaps the most popular one is Laurent Bugnion’s MVVM Light, but there are many others on Codeplex. You should be using those for your WPF / Silverlight / Windows Phone and Windows 8 applications. Here are a few additional references that may help too:

Comments

  • Anonymous
    March 20, 2012
    Thank You The given information is very effective i will keep updated with the same <a href ="http://www.sweetball.in">industrial automation</a>

  • Anonymous
    May 28, 2012
    Nice explanation!! Thanks for sharing Regards, Jalpesh Vadgama (http://www.dotnetjalps.com)

  • Anonymous
    August 21, 2012
    Very well articulated post. Thank you!

  • Anonymous
    March 04, 2014
    How can we share the same ViewModel to different views when every view will have their own instance of ViewModel. Please elaborate?

  • Anonymous
    March 05, 2014
    @Furqan: Typically, you have a ViewModelLocator class, which allows you to declaratively locate specific view models (in your XAML).  The VML class may look as follows: namespace YourApp {    public class ViewModelLocator    {        public MainViewModel MainViewModel        {            get            {                if (_mainViewModel == null)                {                    _mainViewModel = new MainViewModel();                }                return _mainViewModel;            }        }        private MainViewModel _mainViewModel = null;    } } Then, you instantiate the VML in your App.xaml: <Application    xmlns:vm="clr-namespace:YourApp.ViewModels"    ...>    <Application.Resources>       <vm:ViewModelLocator x:Key="ViewModelLocator"/>    </Application.Resources>    ... </Application> And in your XAML file, you use the VML to locate and bind to your view model (in this case, the MainViewModel): <SomeXamlPageOrUserControl    ...    DataContext="{Binding MainViewModel, Source={StaticResource ViewModelLocator}}" > ... </SomeXamlPageOrUserControl> Now you can do that from several views. You'll get the same instance of the VM in all of them, based on the code above.