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.
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.
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:
Development of the View and the Model can be done independently and tied together with the ViewModel at a later time.
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.
You can create multiple Views that take advantage of the same ViewModel for displaying information in different ways or for different users.
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 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).
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.
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.
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.
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); }
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.
Open Blend 4.
Click File
Select Silverlight as the Project type (Figure 15-3).
Select Silverlight Databound Application. You can accept the default name for the application or name the Application MvvmApplication1
, as I have.
The solution should look like Figure 15-4:
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
Right-click the project and select Add New Folder (Figure 15-5). Name the folder BusinessObjects
.
Right-click the BusinessObjects
folder and again add a new class item. Name it User
(Figure 15-6).
The User.cs
file should open automatically; if it doesn't, double-click the file to open it.
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.
You have to set the bindings to TwoWay if you want to receive updates from the source to the binding target.
Right-click the Model
folder and add a new Class Item (choose Add New Item
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 }); _Users.Add(new User() { Name = "Victor Gad", Birthday = "10/22/1999", Active = true }); _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 }); _Users.Add(new User() { Name = "Thomas Train", Birthday = "05/19/1967", Active = true }); return _Users; } }
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 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 associated with it in the View. /// </summary> public void ViewModelMethod() { if(!this.ViewModelProperty.EndsWith("Updated Value", StringComparison.Ordinal)) { this.ViewModelProperty = this.ViewModelProperty + " – 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 User
s. 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 }
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).
Right-click the MainViewModelSampleData
Source and select "Delete data source."
Make sure your project builds by pressing Ctrl+Shift+B.
Select Project and click the Create Sample Data button.
Select Create Sample Data from Class (Figure 15-8).
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.
Now the Data tab should look like Figure 15-10.
Select the icon next to Birthday and change the Format to Date (Figure 15-11).
Set the Format for Name to Name (Figure 15-12).
Now we need to setup our View. We are going to have a little fun with this View and use the pathlistbox
.
Delete the TextBlock
and Button
from the LayoutRoot
.
Divide the Grid into two Column
s (see Figure 15-13).
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.
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.
Set the Foreground color for the PathListBox
to black (Figure 15-16).
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.
Assign the Path created to the PathListBox
using the Layout Paths property panel (Figure 15-18).
Select the circle reticule. Then select the Path we created in the Object and Timelines panel (Figure 15-19).
Rename the PathListBox
to UserList
.
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).
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.
Drag a new Canvas onto the second Column
(Figure 15-22). Name it detailsCanvas
.
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(); }
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(); }
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 (UserList_SelectionChanged);
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); }
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; }
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 .SelectionChangedEventArgs e) { // TODO: Add event handler implementation here. if (UserList.SelectedItem != null) { _model.SelectedUser = UserList.SelectedItem as User; } }
Select detailsCanvas
in the Objects and Timeline panel. Find the DataContext
property and click the Advanced Options button. Then select the DataContext tab.
Click the dropdown for MainViewModel
. Then pick the SelectedUsers
property. See Figure 15-23.
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.
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
.
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.
Assuming you have done everything correctly, when you run the application you should see a list of User
s, and as you select the User
s 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 User
s 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.
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
3.142.119.114