Chapter 15. MVVM: Model-View-ViewModel

MVVM: Model-View-ViewModel

What this chapter covers:

  • MVVM Features in Silverlight 4

  • Create your first MVVM application

  • Project creation, structure, and the model

  • The ViewModel

  • MVVM and Blend

  • MVVM Toolkits for Silverlight

The goal for this chapter is to provide an overview of MVVM and what new features in Blend 4 have been added to support developing applications using this software pattern. I first take a brief look at what a software pattern is and why it is useful. I then create a small sample application, leveraging the new project template and further clarifying the MVVM concept.

Overview

A design pattern refers to a reusable solution to a software design problem. Patterns have been used successfully in various industries for years. Applied to software, they enable us to apply tested development techniques to solve various problems that we encounter in software development. MVVM (which stands for Model-View-ViewModel) is one of those patterns; it separates what we see, the View, from how we get the data, our Model. The ViewModel is the glue that ties it all together.

History of MVVM

John Gossman, architect for Silverlight and WPF, came up with the MVVM pattern in 2005 as a way to leverage the Presentation Model (PM) pattern and apply it to the features in both WPF and Silverlight to simplify the creation of UIs. MVVM allows us to apply some basic UI patterns and take advantage of the great binding system in WPF and Silverlight. Microsoft is even using this pattern internally: Blend was created using MVVM, and the new version of Visual Studio. This separation allows for a much smoother workflow between designers and developers. The tooling coming out of Microsoft is starting to reflect this trend.

Read more on the Presentation Model at http://martinfowler.com/eaaDev/PresentationModel.html.

There are several benefits to using the MVVM development pattern:

Independent development:

Development of the View and the Model can be done independently and tied together with the ViewModel at a later time.

Easier testing:

Automated testing of the UI has always been rather difficult to perform and get right. Testing a ViewModel, which should contain all the interactions for a View, is easier because you are not dealing with any UI components.

Multiple UIs:

You can create multiple Views that take advantage of the same ViewModel for displaying information in different ways or for different users.

Separation of responsibility:

Everything is broken out and clearly defined. The View contains the Visuals for the application and consumes the ViewModel. The ViewModel glues the view and Model together. The Model contains all the interaction to retrieve and save data for the application.

Data Binding, DataTemplates, and Commanding

Data Binding allows us to bind properties from the ViewModel to our View. DataTemplates can be used to specify how items in a list should be viewed. Commanding can be used to wire up actions from your View to your ViewModel.

Blend 4 adds some great new features to support MVVM. These include a project specifically for creating an MVVM-type application (Figure 15-1).

New project type in Blend supporting MVVM.

Figure 15.1. New project type in Blend supporting MVVM.

Model

What is the Model part? The Model represents the data that an application will use to populate the various views. It has the responsibility of getting the data from a web service, file system, or database. The Model also has the responsibility of presenting the data in a manner that is easily used by the ViewModel.

View

The View represents the UI portion of the pattern. The View binds to a ViewModel and displays the information for the user either to view or to edit. It represents the visual side of the Model. It defines all of your visual elements and bindings needed to tie our View and ViewModels together. You can also store any needed storyboards or animations in your View. It is common practice to set the DataContext of the View equal to an instance of your ViewModel. This can be done as a resource in XAML, or as a variable created in the code-behind. I have found it beneficial to have a 1:1 correspondence of View to ViewModel to begin with. If at a later date you need to change the visuals of your ViewModel, you can create a new View and take advantage of your existing ViewModel.

ViewModel

Finally, what is the ViewModel? The ViewModel glues everything together. The View consumes the ViewModel for Data Binding and command execution (event execution from the UI). The ViewModel also pushes data back to the Model for CRUD (Create, Read, Update, Delete) operations. The Model for this sample application will be created with sample data. The binding syntax in Silverlight allows us to tie the View and ViewModel together so that updates to the View are pushed to the Model without any extra coding we do this using two way bindings.

MVVM Features in Silverlight 4

One of the major new features in Silverlight 4 for MVVM is improved support for Commanding.

The interface for Commanding, ICommand, was first presented in Silverlight 3, but lacked any implementation out of the box. ICommand is found in the System.Windows.Input namespace. In Silverlight 4, a command property was added to ButtonBase and Hyperlink to enable you to hook your buttons up to your ViewModel. Here is what ICommand Interfaces look like:

Public interface ICommand
        {
                event EventHandler CanExecuteChanged;
                bool CanExecute(object parameter);
                void Execute(object parameter);
        }

Creating Your First MVVM Application in Blend 4

We are going to apply MVVM and the new template in Blend 4 to create our portion of the MVVM Masters Tracker application. Our project will display a list of users on one side and detailed information about each MVVM master on the other side. I cover the various aspects of the project template and create a working implementation of MVVM. I will point out the various pieces of MVVM as we progress and provide some more detailed explanation as we move forward. First let's go over the project template.

  1. Open Blend 4.

  2. Click File

    Creating Your First MVVM Application in Blend 4
    The New Project dialog box.

    Figure 15.2. The New Project dialog box.

  3. Select Silverlight as the Project type (Figure 15-3).

    Select Silverlight as the project type.

    Figure 15.3. Select Silverlight as the project type.

  4. Select Silverlight Databound Application. You can accept the default name for the application or name the Application MvvmApplication1, as I have.

  5. Press OK to continue.

The solution should look like Figure 15-4:

Project structure.

Figure 15.4. Project structure.

Overview of the MVVM Template Project

The MVVM project includes the following examples files: MainViewModelSampleData.xaml, MainViewModel.cs, and MainView.xaml. MainViewModelSampleData contains sample data consumed by the MainViewModel and is presented to the MainView for display. These files are samples to get you started on your first MVVM application and are a good reference for understanding MVVM in general.

The first directory of note is the SampleData directory. This directory should contain any sample data that you create for the project. The template used to create this project contains one item called MainViewModelSampleData.xaml. This file contains the sample data used by the MainView.xaml.

Next is the ViewModels directory. Place any new ViewModels you create in here. This directory contains one sample file called MainViewModel.cs. This ViewModel will be bound to the MainView. Remember the ViewModel contains the information from the Model in a form that the View can bind to.

The final folder of interest is called Views. This folder contains MainView.xaml. This is the view that the user interacts with to view and manipulate information in our application. MainPage.xaml is the container application which contains our navigation and content area where the views will be loaded. App.xaml contains application level resources and app startup and shutdown events.

First I address the plumbing pieces of the application. We are going to use a combination of SampleData for design time support, and static data for display when we run the application. We will approach our MVVM application in the following order: create the User class, set up the Model, set up the ViewModel, set up the SampleData, and finally set up the View.

First up is the User class. This class will contain properties for the information we want to display, and implement INotifyPropertyChanged. INotifyPropertyChanged is an important interface that allows a class or object to participate in TwoWay Binding. TwoWay Binding means if we change the data displayed in the UI or the User object via some other means, it will update in both places. This saves a lot of plumbing code to keep the two values in sync.

More information on INotifyPropertyChanged can be found here:

http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx

Creating Supporting Objects

  1. Right-click the project and select Add New Folder (Figure 15-5). Name the folder BusinessObjects.

    Add New Folder.

    Figure 15.5. Add New Folder.

  2. Right-click the BusinessObjects folder and again add a new class item. Name it User (Figure 15-6).

    Add new class the name is User.

    Figure 15.6. Add new class the name is User.

  3. The User.cs file should open automatically; if it doesn't, double-click the file to open it.

  4. Following is the code for the User object. Here we can see an implementation of the INotifyPropertyChanged interface. By implementing the interface we agree to provide code that implements the event.

    public event  PropertyChangedEventHandler PropertyChanged;

    What this event does is tell the binding system where to say, "Hey, notify me if you change any part of your object." We do this in the NotifyPropertyChanged event where we first check to see if we have any subscribers to be notified and then fire off the EventHandler.

    if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }

    Notice anytime we set a property we call our NotifyPropertyChanged event. Now anytime the properties of our object are changed, the UI will automatically update. We also need to add the following properties to our object: Name, Active, and Birthday.

    Here is a complete listing of the code:

    public class User : INotifyPropertyChanged
            {
                public User()
                {
    // Insert code required on object creation below this point.
                }
            private string _Name;
            public string Name
            {
                get { return _Name; }
                set
                {
                    _Name = value;
                    NotifyPropertyChanged("Name");
                }
            }
            private bool _Active;
            public bool Active
            {
                get { return _Active; }
                set
                {
                    _Active = value;
                    NotifyPropertyChanged("Active");
                }
            }
            private string _Birthday;
            public string Birthday
            {
                get { return _Birthday; }
                set
                {
                    _Birthday = value;
                    NotifyPropertyChanged("Birthday");
                }
            }
    
    
            public event PropertyChangedEventHandler PropertyChanged;
            private void NotifyPropertyChanged(String info)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(info));
                }
            }
        }

    Add another new folder called Model. We will use this folder to supply sample data at runtime.

    We can simulate getting data from some type of service by populating the model with some static data. We'll create a model class that will return an ObservableCollection of User objects.

    What is an ObservableCollection? It is a list that implements the INotifyPropertyChanged under the hood. What does this do? Well, put simply it allows the collection to notify the UI anytime there are changes so that the UI can update the visual elements to reflect those changes. So anytime our list changes the UI will re-bind all the items in the list to our ItemsControl or ListBox.

    Note

    You have to set the bindings to TwoWay if you want to receive updates from the source to the binding target.

  5. Right-click the Model folder and add a new Class Item (choose Add New Item

    Add new class the name is User.
    using System.Collections.ObjectModel;
    
          public class UserModel
            {
        public UserModel()
                {
                    // Insert code required on object creation below this point.
                }
                public static ObservableCollection<User> GetUserList()
                {
    List<User> _Users = new List<User>();
    _Users.Add(new User() { Name = "Bill Moore", Birthday = "06/22/1978", Active = true
    Add new class the name is User.
    }); _Users.Add(new User() { Name = "Victor Gad", Birthday = "10/22/1999", Active = true
    Add new class the name is User.
    }); _Users.Add(new User() { Name = "Paul Gebo", Birthday = "08/15/1935", Active = true }); _Users.Add(new User() { Name = "Jason Rain", Birthday = "02/10/1979", Active = true
    Add new class the name is User.
    }); _Users.Add(new User() { Name = "Thomas Train", Birthday = "05/19/1967", Active =
    Add new class the name is User.
    true }); return _Users; } }

Setting Up the ViewModel

Open up the MainViewModel.cs by doubleclicking the file so we can make the modification to support our application. We need to discuss some of the important aspects of this code file. Listed here is the file in its unmodified state:

using System;
using System.ComponentModel;
namespace MvvmApplication1
{
        public class MainViewModel : INotifyPropertyChanged
        {
            public MainViewModel()
            {
                // Insert code required on object creation below this point.
            }

            private string viewModelProperty = "Runtime Property Value";
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its
Setting Up the ViewModel
value using a Binding. /// </summary> /// <returns></returns> public string ViewModelProperty { get { return this.viewModelProperty; } set { this.viewModelProperty = value; this.NotifyPropertyChanged("ViewModelProperty"); } } /// <summary> /// Sample ViewModel method; this method is invoked by a Behavior that is
Setting Up the ViewModel
associated with it in the View. /// </summary> public void ViewModelMethod() { if(!this.ViewModelProperty.EndsWith("Updated Value", StringComparison.Ordinal)) { this.ViewModelProperty = this.ViewModelProperty + " –
Setting Up the ViewModel
Updated Value"; } } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } }
#endregion
        }
}

We want to modify the code so it looks like the class that follows. To accomplish this, all we need to do is remove some of the sample code and add a Collection of Users. We modified the constructor to supply data when you run the application.

public class MainViewModel : INotifyPropertyChanged
    {
        private List<User> _Users = new List<User>();
        public List<User> Users
        {
            get { return _Users; }
            set
            {
                _Users = value;
            }
        }
        public MainViewModel()
        {
            // Insert code required on object creation below this point.
            _Users = UserModel.GetUserList();
}

      #region INotifyPropertyChanged
      public event PropertyChangedEventHandler PropertyChanged;
      private void NotifyPropertyChanged(String info)
      {
          if (PropertyChanged != null)
          {
              PropertyChanged(this, new PropertyChangedEventArgs(info));
          }
      }
      #endregion
    }

Setting Up the Sample Data Source

  1. Double-click MainView.xaml and then select the Data tab. By default, it is next to the Properties and Resources tabs on the right-hand side (Figure 15-7).

    The Data tab.

    Figure 15.7. The Data tab.

  2. Right-click the MainViewModelSampleData Source and select "Delete data source."

  3. Make sure your project builds by pressing Ctrl+Shift+B.

  4. Select Project and click the Create Sample Data button.

  5. Select Create Sample Data from Class (Figure 15-8).

    Select project Create Sample Data button Create Sample Data from Class.

    Figure 15.8. Select project

    Select project Create Sample Data button Create Sample Data from Class.
    Create Sample Data button
    Select project Create Sample Data button Create Sample Data from Class.
    Create Sample Data from Class.

  6. In the dialog box for Data Source Name, type MainViewModelSampleData. In the Select Class search box, type MainViewModel. Select the MainViewModel class under MVVMApplication1 (Figure 15-9). Click OK.

    Select the MainViewModel.

    Figure 15.9. Select the MainViewModel.

    Now the Data tab should look like Figure 15-10.

    The Data tab.

    Figure 15.10. The Data tab.

  7. Select the icon next to Birthday and change the Format to Date (Figure 15-11).

    Set the Birthday Format to Date.

    Figure 15.11. Set the Birthday Format to Date.

  8. Set the Format for Name to Name (Figure 15-12).

    Set the Format for Name to Name.

    Figure 15.12. Set the Format for Name to Name.

Setting Up the View

Now we need to setup our View. We are going to have a little fun with this View and use the pathlistbox.

  1. Delete the TextBlock and Button from the LayoutRoot.

  2. Divide the Grid into two Columns (see Figure 15-13).

    Divide the Grid into two Columns.

    Figure 15.13. Divide the Grid into two Columns.

  3. Let's use the PathListBox. Create a Path in the first Column using the Pen or Shape tool. It can be any type of Path that you want. See Figure 15-14.

    Add a Path to the first Column.

    Figure 15.14. Add a Path to the first Column.

  4. Set the Path opacity to 0%. Add a PathListBox (Figure 15-15). It doesn't matter where you place it. You can place it in the second Column near the bottom so it's out of the way.

    Selecting the PathListBox.

    Figure 15.15. Selecting the PathListBox.

  5. Set the Foreground color for the PathListBox to black (Figure 15-16).

    Make the foreground black.

    Figure 15.16. Make the foreground black.

    Placing the PathListBox.

    Figure 15.17. Placing the PathListBox.

    Remember, the location of the PathListBox doesn't matter. The items in the ListBox will be rendered along the Path that we select in the next step.

  6. Assign the Path created to the PathListBox using the Layout Paths property panel (Figure 15-18).

    Using the LayoutPaths panel.

    Figure 15.18. Using the LayoutPaths panel.

  7. Select the circle reticule. Then select the Path we created in the Object and Timelines panel (Figure 15-19).

    Select the path as the Layout Path for the PathListBox.

    Figure 15.19. Select the path as the Layout Path for the PathListBox.

  8. Rename the PathListBox to UserList.

  9. Switch to the Data tab. Select the MainViewModel and drag it over to the LayoutRoot. This will bind the DataContext of LayoutRoot to the MainViewModel (Figure 15-20).

    Bind the DataContext of LayoutRoot to the MainViewModel.

    Figure 15.20. Bind the DataContext of LayoutRoot to the MainViewModel.

  10. Using the Data Context window, select the User's chevron. Drag Name onto the PathListBox (Figure 15-21). Adjust the Grid Column to fit the content. You may also want to set the Capacity on the PathListBox to 8 instead of the default 10.

    Binding the PathListBox to Name.

    Figure 15.21. Binding the PathListBox to Name.

  11. Drag a new Canvas onto the second Column (Figure 15-22). Name it detailsCanvas.

    Add another Canvas control to the second Column.

    Figure 15.22. Add another Canvas control to the second Column.

  12. We need to add another property to our MainViewModel that will hold the SelectedUser from the PathListBox. Notice the call to NotifyPropertyChanged. This allows the binding system to automatically update our fields every time the SelectedUser property is changed. Add the code just below the class declaration.

    public partial class MainView : UserControl
            {
                    private User _selectedUser;
                    public User SelectedUser
                    {
                            get {return _selectedUser;}
                            set {_selectedUser = value;
                                    NotifyPropertyChanged("SelectedUser");
                            }
                    }
    
                    public MainView()
                    {
                            // Required to initialize variables
    InitializeComponent();
                    }
  13. We need to create a ViewModel that will be used by our View. Add this code right under the class declaration.

    MainViewModel _viewmodel = new MainViewModel();

    Here is the new code in context:

    public partial class MainView : UserControl
            {
                    MainViewModel _viewmodel = new MainViewModel();
    
                    private User _selectedUser;
                    public User SelectedUser
                    {
                            get {return _selectedUser;}
                            set {_selectedUser = value;
                                    NotifyPropertyChanged("SelectedUser");
                            }
                    }
    
    
                    public MainView()
                    {
                            // Required to initialize variables
                            InitializeComponent();
                    }
  14. Click the MainView.xaml tab. Then select LayoutRoot in the Objects and Timeline panel. Click the Advanced Options button next to DataContext. Then go to Reset. This will remove the DataContext for LayoutRoot. We are setting the DataContext in the code.

    We need to wire up some events in our MainView. To do this we will need to open up the MainView.xaml.cs file. Find this file in the Project panel and double-click to open it. We will add the following line at the bottom of the constructor. This will allow us to handle the event when someone selects a User from our list.

    UserList.SelectionChanged += new SelectionChangedEventHandler
    Add another Canvas control to the second Column.
    (UserList_SelectionChanged);
  15. The new constructor for MainView.xaml.cs should look like the one here.

    public MainView()
                    {
                            // Required to initialize variables
                    InitializeComponent();
                    UserList.SelectionChanged += new
    SelectionChangedEventHandler(UserList_SelectionChanged);
                    }
  16. Set the DataContext to the instance of our ViewModel we just created. We will add this line to the bottom of our constructor. Setting the DataContext tells our View where to pull the data from. All the bindings we set up in the View are properties or collections of our ViewModel. Once we set the DataContext to an instance of our ViewModel, we will see our user data appear if our bindings are correct.

    this.DataContext = _viewmodel;

    The constructor should look like this now.

    public MainView()
                    {
                            // Required to initialize variables
                    InitializeComponent();
                    UserList.SelectionChanged += new
                    SelectionChangedEventHandler(UserList_SelectionChanged);
                    this.DataContext = _viewmodel;
                    }
  17. When the user selects an item from our PathListBox we need to update the Selected User field in our ViewModel. The code that follows first checks to see if we have something selected. The code should always have something selected, but we want to err on the side of caution. It then sets the SelectedUser property in our ViewModel to the currently selected item.

    private void UserList_SelectionChanged(object sender,Controls
    Add another Canvas control to the second Column.
    .SelectionChangedEventArgs e) { // TODO: Add event handler implementation here. if (UserList.SelectedItem != null) { _model.SelectedUser = UserList.SelectedItem as User; } }
  18. Select detailsCanvas in the Objects and Timeline panel. Find the DataContext property and click the Advanced Options button. Then select the DataContext tab.

  19. Click the dropdown for MainViewModel. Then pick the SelectedUsers property. See Figure 15-23.

    Set the DataContext of the Canvas to SelectedUser.

    Figure 15.23. Set the DataContext of the Canvas to SelectedUser.

  20. Add a CheckBox for Active. Name it chkActive and for its Content property set it to Active. The IsChecked property should be bound to the Active field of our User object. See Figure 15-24.

    IsChecked should be bound to the Active property of SelectedUser.

    Figure 15.24. IsChecked should be bound to the Active property of SelectedUser.

  21. Add a TextBlock beneath Active and set its Content to Name. Add a TextBox and bind the Text property to the Name field of our SelectedUser.

  22. Add another TextBlock and set its Content to Birthday. Add a DatePicker next to it and bind the SelectedDate to the Birthday field of the SelectedUser. See Figure 15-25.

    The final look for our View.

    Figure 15.25. The final look for our View.

Assuming you have done everything correctly, when you run the application you should see a list of Users, and as you select the Users the detail form will populate.

This sample shows how to create a basic application and how to apply the MVVM pattern. We created a list of Users in our Sample Model class. We then took that list into our ViewModel and bound to it in our View.

One question that may come up is: what use is the ViewModel? It looks like it just passes the data along. This is true in this simple situation, but in more complicated projects the ViewModel may combine multiple Models together and expose them to a View. Every piece of the pattern plays an important role.

MVVM Toolkits and Frameworks for Silverlight

Let's talk about some of the MVVM toolkits, and new technologies in .NET 4.0 that can be used in conjunction with the MVVM pattern for even more extensibility. Following is a list of MVVM toolkits and tools that support and provide functionality for using MVVM in your application. MEF is mentioned in this list for its composition capabilities. With MEF, new in .NET 4.0, you can have MEF supply your Views at runtime. The tool with the best support for Blend is the MVVM Light Toolkit. More information can be found with the links provided.

  • MVVM Light Toolkit: This open source project provides some features to help designers adopt MVVM. The toolkit includes a Messenger class, Commanding implementation, and many more features.

    Also as of the writing this is the most Blend-friendly MVVM toolkit.

    http:// mvvmlight.codeplex.com
  • Caliburn Micro: Another open source project. This one was born from a talk at MIX called "Build Your Own MVVM Framework." Extremely lightweight with some great features, including automatically wiring up your View and ViewModels. It also includes automatic event wiring.

    http://caliburnmicro.codeplex.com
  • MEF: This stands for Management Extensibility Framework. This framework allows you to add extensibility and plug-in support to your application.

    http://msdn.microsoft.com/en-us/library/dd460648.aspx

Summary

MVVM is great once you wrap your head around it. You can create different views based on customer demand while leveraging your existing ViewModels. It lets you take advantage of binding and helps you separate out the various pieces of your application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.119.114