Sunday, September 6, 2009

[2009.09.06] A Gentle Introduction to Model-View-ViewModel (MVVM)

I have seen some demos of the Model-View-ViewModel (MVVM) pattern in the past. I was supposed to start another WPF project, but instead, given its importance in designing and coding WPF applications, I took time out to learn more about this pattern. Diving into the details does take some effort, but in this post I will simply outline the most rudimentary aspects of the pattern and go from there. This is by no means an exhaustive look at this pattern. I am just documenting my learning process and if anyone is interested, can follow along. What I will be doing in this post is to give a generalization of the pattern, without any of the WPF Commands and tests portions. Basically, it is to show how the subsections hook up to each other.

Download the Project

MVVM is a more WPF specific form of the Model-View-Presenter (MVP) pattern, by taking advantage of some of the features of the platform, such as data binding, data templates, resources, commands and routed events, to name a few. Of course you can dig out the details of these features from MSDN. In addition, there are some specific MSDN Magazine sections you can refer to:

The whole idea behind this pattern is the separation of concerns. That is, you can completely separate the presentation logic from the business logic of the application. This alone demonstrates the flexibility of the pattern in that you can have one group dedicated to the front end look/feel aspect of the app, whereas another team can tackle the more technical aspects of the project.

Let's take a brief look at each subsection of the MVVM pattern as shown in Fig. 1.

[2009.09.06].01.Basic.MVVM.Pattern
Fig. 1. Model-View-ViewModel Pattern

Model

The Model is the section responsible for retrieving and exposing data to WPF in a way that it can understand. This data can come from a database, XML, RSS, or custom object. For example, given a XML file, you can create a custom object with public properties to model the elements of that file.

ViewModel

The task of this section is to abstract away some of the interaction logic from the View (UI) so that it remains purely just that - UI. In addition, since it stands between the View and the Model, the View never directly communicated with the Model. Instead, the View delegates the commands of the user to the ViewModel, which in turn performs the necessary logic to fulfill the user request. The ViewModel basically hold the presentation logic and state that is shared by the View(s). For example, imagine you have a custom class (our Model in this case) that exposes three public properties. However, the ViewModel only exposes two of those properties to all the Views of the application. Since the Views never have direct interaction with the Model, all it can do is interact with the two properties that the ViewModel exposes. I will go into more detail about it soon.

The ViewModel is also where you can implement the INotifyPropertyChanged and/or INotifyCollectionChanged since WPF does not provide automatic change notifications (the ObservableCollection class does support change notifications though). Since the ViewModel is data bound to its View, then any property that changes will be automatically propagated to the View. The ViewModel interacts with the View using the WPF Commands infrastructure. Also, each View will have its own corresponding ViewModel.

View

The View is the graphical front end of the application. It is basically a set of controls that is bound to a corresponding ViewModel object and should be entirely coded in XAML. No other code should be put in the accompanying code behind other than what was generated by Visual Studio. The View contains a reference to the ViewModel via its DataContext property. In order to render the ViewModel data to the user, the View uses Data Templates

Example

I am using Visual Studio 2008 SP1 + .NET 3.5 SP1

Time for an example. If you download the MVVM files from CodePlex, it comes with a Visual Studio template to create a MVVM WPF project as in Fig. 2. The MVVM project will provide you with some additional boilerplate code, such as the DelegateCommand class and a test project (this is optional). In addition, the MVVM project has folders to separate the sections of the pattern as shown in Fig. 3.

[2009.09.06].02.Mvvm.Project.01
Fig. 2. MVVM Project using VS Template from CodePlex

[2009.09.06].03.Mvvm.Project.02
Fig. 3. Folders and files generated for a MVVM project

For this example, you can use the CodePlex template or create a WPF project and add the Models, ViewModels and Views folders shown in Fig. 3. Since I have the template installed, I will just use that to generate the folders and for now, I will ignore the Commands and Tests parts of the solution.

The Model

I will start off with the Model by creating a super simple class called Person.cs and putting it in the Models folder. It will construct a Person object using firstName and lastName as arguments and expose two public properties. Note that this class is just an ordinary class and it has nothing to do with the MVVM pattern per say. You can rip this out and replace it if need be.

Since I am not pulling in any data from a file or database, I have added a method to this class to create some data points to test the example out with. This is shown in Listing 1.

  1: using System;
  2: using System.Collections.Generic;
  3: 
  4: namespace MvvmDemo01.Models
  5: {
  6:     public class Person
  7:     {
  8:         // Private members
  9:         private static List<Person> persons = new List<Person>();
 10:         
 11:         // Public constructor to create an instance
 12:         //  of the class
 13:         // This is the general Model of the data I will
 14:         //  be using which exposes the FirstName
 15:         //  and LastName properties
 16: 
 17:         // The PersonViewModel class in the ViewModels 
 18:         //  folder 
 19:         public Person(String firstName, String lastName)
 20:         {
 21:             this.FirstName = firstName;
 22:             this.LastName = lastName;
 23:         }
 24:         // Public properties 
 25:         public String FirstName { get; set; }
 26:         public String LastName { get; set; }
 27: 
 28:         // Method to create a List of Person objects
 29:         // NOTE: Since I am not getting my data from
 30:         //  any other sources (XML, etc), I am just making
 31:         //  some up
 32:         public static List<Person> GeneratePersons()
 33:         {
 34:             persons.Add(new Person("Bugs", "Bunny"));
 35:             persons.Add(new Person("Daffy", "Duck"));
 36:             return persons;
 37:         }
 38:     }
 39: }

Listing 1. The data model used in this example

This is basically all that is needed for the Model portion.

The View

This is a very straightforward section as well. The MVVM Visual Studio template will create an initial view called MainView.xaml for you. This is where you will put the Views that is responsible for rendering your data. The pattern basically states that you should create UserControls to generate the necessary views. In simple cases you can probably just use the View that was automatically created.

For now, follow the pattern and in the Views folder, create a new UserControl called PersonView.xaml as shown in Listing 2. This control will be used to set the DataTemplate property of a ListBox control in the MainView.xaml file. The code in the PersonView UserControl is actually the code needed to create a template to represent the data. If you want you can delete this file and copy its contents to the data template section of the ListBox.ItemTemplate property in the MainView.xaml file as shown in Listing 3 and still get the same result.

  1: <UserControl x:Class="MvvmDemo01.Views.PersonView"
  2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  4:     <StackPanel Orientation="Horizontal">
  5:         <Border CornerRadius="3" BorderBrush="Blue" 
  6:                 BorderThickness="1" Margin="5">
  7:             <StackPanel Orientation="Horizontal">
  8:                 <TextBlock Text="First Name: " 
  9:                            Margin="5" FontStyle="Italic"/>
 10:                 <TextBlock Text="{Binding Path=FirstName}" 
 11:                            Margin="5" Foreground="Red"/>
 12:             </StackPanel>
 13:         </Border>
 14:     </StackPanel>
 15: </UserControl>

Listing 2. The PersonView.xaml UserControl

  1: <Window x:Class="MvvmDemo01.Views.MainView"
  2:     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4:     xmlns:v="clr-namespace:MvvmDemo01.Views"
  5:     Title="Characters" Width="200" Height="220">
  6:     <StackPanel>
  7:         <TextBlock Text="List of Characters" Margin="4" />
  8:         <Border Margin="3" BorderBrush="Black" 
  9:                 BorderThickness="2" CornerRadius="3">
 10:             <!-- use the PersonView UserControl to generate
 11:                 the template-->
 12:             <ListBox ItemsSource="{Binding Persons}" 
 13:                      Margin="3">
 14:                 <ListBox.ItemTemplate>
 15:                     <DataTemplate>
 16:                         <v:PersonView />
 17:                     </DataTemplate>
 18:                 </ListBox.ItemTemplate>
 19:             </ListBox>
 20:         </Border> 
 21:         
 22:         <!-- OR just create the template inline-->
 23:         <Border Margin="3" BorderBrush="Black" 
 24:                 BorderThickness="2" CornerRadius="3">
 25:             <!-- use the PersonView UserControl to generate
 26:                 the template-->
 27:             <ListBox ItemsSource="{Binding Persons}" 
 28:                      Margin="3">
 29:                 <ListBox.ItemTemplate>
 30:                     <DataTemplate>
 31:                         <StackPanel Orientation="Horizontal">
 32:                             <Border CornerRadius="3" BorderBrush="Blue" 
 33:                                     BorderThickness="1" Margin="5">
 34:                                 <StackPanel Orientation="Horizontal">
 35:                                     <TextBlock Text="First Name: " 
 36:                                                 Margin="5" FontStyle="Italic"/>
 37:                                     <TextBlock Text="{Binding Path=FirstName}" 
 38:                                                 Margin="5" Foreground="Red"/>
 39:                                 </StackPanel>
 40:                             </Border>
 41:                         </StackPanel>
 42:                     </DataTemplate>
 43:                 </ListBox.ItemTemplate>
 44:             </ListBox>
 45:         </Border>
 46:     </StackPanel>
 47: </Window>

Listing 3. The UI that the user sees. It uses a UserControl and an inline data template to display the same information.
The ViewModel

This is where the majority of the fun stuff happens. Again, if you are using the template from CodePlex, there will be at least two ViewModel files generated for you - ViewModelBase.cs which is an abstract class that implements the INotifyPropertyChanged interface and the MainViewModel.cs which derives from ViewModelBase and is the View Model that is associated with the corresponding MainView.xaml View file.

In this example, I am creating a PersonViewModel.cs file in the ViewModels folder which will associate with the Person.cs Model. Basically, I am using this class (PersonViewModel.cs) to expose properties from my data class. Notice that in the Person class, I have two properties defined - FirstName and LastName. However, in the PersonViewModel class, even though I am creating Person objects, I am only exposing the FirstName property. This means that Views that are attached to the PersonViewModel will only see the FirstName property, which it exposes and not both properties as exposed by the original Model (Person.cs). This is to show that the View does not directly interact with the Model.

My PersonViewModel simply creates a Person object and exposes only a single property of the Person class. This is shown in Listing 4.


  1: using System;
  2: using MvvmDemo01.Models; // to access Person
  3: 
  4: namespace MvvmDemo01.ViewModels
  5: {
  6:     // This class will expose the
  7:     //  necessary fields of the Person
  8:     //  model. Even though Person exposes
  9:     //  two public fields, as an example,
 10:     //  I will only expose the FirstName here
 11:     public class PersonViewModel
 12:     {
 13:         // Get a reference to the Person object
 14:         private Person _person;
 15: 
 16:         // Constructor to create a Person object
 17:         public PersonViewModel(Person person)
 18:         {
 19:             this._person = person;
 20:         }
 21: 
 22:         // Expose only one property from the Person object
 23:         // Make it readonly since I will not be adding
 24:         //  anything back to the data model 
 25:         public String FirstName 
 26:         {
 27:             get { return (String)_person.FirstName; }
 28:         }
 29:     }
 30: }

Listing 4. The PersonViewModel class exposes the properties of the Model that the View can bind to

The MainViewModel class basically sets up a property that will be used to set the DataContext property of the View that binds to this ViewModel. What this means is that in this class, I will create a collection of PersonViewModel objects which in turn represents a Person (Model) object. This is an ObservableCollection which supports change notifications.

NOTE: The way I did this is not necessary here. I only used an ObservableCollection because I am trying to be as close to the MVVM pattern as possible. Since in this project I am not too concerend with changes in the collection, instead, I could have simply created a normal list of Person objects (List<Person>) and expose a property of the same type rather than exposing a property whose type is ObservableCollection<PersonViewModel> as shown in Listing 5.

The Persons property exposed by the MainViewModel class will be used to set the DataContext of the MainView which in turn has elements that data bind to whatever properties are exposed by each object of the collection.


  1: using System.Collections.Generic;
  2: using System.Collections.ObjectModel;
  3: using MvvmDemo01.Models;
  4: 
  5: namespace MvvmDemo01.ViewModels
  6: {
  7:     public class MainViewModel : ViewModelBase
  8:     {
  9:         // Create ObservableCollection from the
 10:         //  System.Collections.ObjectModel namespace to 
 11:         //  hold a collection of PersonViewModel 
 12:         //  objects
 13:         private ObservableCollection<PersonViewModel> _persons = 
 14:             new ObservableCollection<PersonViewModel>();
 15: 
 16:         public MainViewModel() { }
 17: 
 18:         // Method to "load" and create the collection of
 19:         //  PersonViewModel
 20:         public void LoadPersons()
 21:         {
 22:             // call the static GeneratePersons method to 
 23:             //  fill the list
 24:             List<Person> _person = Person.GeneratePersons();
 25:             foreach (var person in _person)
 26:             {
 27:                 this._persons.Add(new PersonViewModel(person));
 28:             }
 29:         }
 30: 
 31:         // After the data has been loaded, expose the collection
 32:         //  as a public property so that the View can bind to it
 33:         //  i.e. the View used to display the data will set its
 34:         //  DataContext property to this collection. A data template
 35:         //  in the view will configure each object for display
 36:         public ObservableCollection<PersonViewModel> Persons 
 37:         {
 38:             get { return this._persons; }
 39:         }
 40:     }
 41: }

Listing 5. The MainViewModel which exposes a collection type property that is used to set the data context of the corresponding View

The setting of the View's DataContex is done in the App.xaml file as shown in Listing 6.

  1: using System.Windows;
  2: using MvvmDemo01.ViewModels; // to acceess MainViewModel
  3: 
  4: namespace MvvmDemo01
  5: {
  6:     public partial class App : Application
  7:     {
  8:         private void OnStartup(object sender, StartupEventArgs e)
  9:         {
 10: 
 11:             Views.MainView view = new Views.MainView();
 12: 
 13:             // create an instance of the MainViewModel
 14:             //  and 'load' the data into a collection
 15:             MainViewModel mvm = new MainViewModel();
 16:             mvm.LoadPersons();
 17: 
 18:             // set the data context of MainView to the
 19:             //  loaded collection
 20:             view.DataContext = mvm;
 21:            
 22:             view.Show(); // display main window
 23:         }
 24:     }
 25: }

Listing 6. Setting the DataContext of the View

A subset of the Class Diagram for the project is given in Fig. 4.

[2009.09.06].04.Mvvm.Class.Diagram
Fig .4. Class Diagram

Now all you have to do is compile and run the application and you will get something like that in Fig. 5.


[2009.09.06].05.Mvvm.App.Running
Fig. 5. Mvvm application running

Download the Project

2 comments:

Le_Bedel said...

Nice introduction to MVVM. it is helpful. First of all thanks for the time you spend in order to help others. From here, what happen if i want to new views like Add/Edit Person or show person view? I try many times to catch that by reading PeriodicTable which help me as well about MVVM and XML. But i cannot understand or deduce how to navigate from on usercontrol to another via a button in WPF. Thanks

Sparky Dasrath said...

I am glad you learned something. Without going into too much detail, to, I think what you are looking for is a Delegate Command. Commands in WPF work along the Visual Tree so in order to communicate beyond that, you will need the commanding structure of MVVM. If you get the toolkit and documentation from codeplex (http://wpf.codeplex.com/wikipage?title=WPF%20Model-View-ViewModel%20Toolkit), it goes into commands. I do apologize as I don't recall using it in the Periodic Table example.

In the sample from codeplex, they provide you with the DelegateCommand class that does most of the heavy lifting for you. In addition, you can take a peek at Prism (Composite Application Guidance or something of the sort) as they have an implementation of a delegate command as well - either one will work for you. I hope this helps and feel free to ask any other questions. If I don't know the answer, maybe I can point you in the right direction of the answers.