The Basics of MVVM
While training and consulting with clients around the country, I find that many developers still have trouble grasping the concept of Model-View-View Model (MVVM) as used in Silverlight or WPF. In this blog post I thought I would show two examples side-by-side to help you learn how to move from the more traditional model of development to MVVM.
Why use MVVM
The reasons why programmers are adopting MVVM or the Model View Controller (MVC) design patterns are the same reasons why programmers adopted Object Oriented Programming (OOP) almost 30 years ago: reusability, maintainability and testability. Wrapping the logic of your application into classes allows you to reuse those classes in many different applications. Maintaining logic in classes allows you to fix any bugs in just one place and any other classes using that class automatically get the fix. When you don’t use global variables in an application, testing your application becomes much simpler. Wrapping all variables and logic that operates upon those variables into one class allows you to create a set of tests to check each property and method in the class quickly and easily.
The thing to remember with MVVM and MVC is all you are doing is moving more of the logic out of the User Interface (UI) and into a class that simply has properties and methods that you will bind to the user interface. One example of the type of logic you move into a class is setting the DataContext property of a list box (in Silverlight/WPF) when you want to display a collection of objects in that list box control. Instead of setting the DataContext property directly you simply set a property in the class where you load that collection of objects. If you bind this property to the list box control then the list box will automatically redraw itself when this property changes.
To use MVVM you must be using classes and not just writing all your code in the code behind of your UI. For VB.NET programmers this also means you are NOT using modules. The whole key to MVVM or MVC is the use of classes with properties and methods that mimic the behavior you want in the UI. This means setting properties that are bound to UI controls and calling methods when you want to perform some action that you would normally write in the Click event of a button.
Load a List Box without MVVM
To start, let’s write code to load a list box control in Silverlight without using the MVVM design pattern. Create a new Silverlight project and to the MainPage.xaml add a ListBox control to the page and set the Name property to lstData. Add a Loaded event procedure to the XAML so it will create a UserControl_Loaded event procedure.
<UserControl ...
Loaded="UserControl_Loaded">
The sample project that comes with this posting (see the end of this blog for instructions on download) has a WCF service that will return a collection of Product objects. In the UserControl_Loaded event procedure you write code to load the Project data and give that data to the list box.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ProductServiceClient client = new ProductServiceClient();
client.GetProductsCompleted += new
EventHandler<GetProductsCompletedEventArgs>
(client_GetProductsCompleted);
client.GetProductsAsync();
client.CloseAsync();
}
void client_GetProductsCompleted(object sender,
GetProductsCompletedEventArgs e)
{
lstData.DataContext = e.Result;
}
The code in the Loaded event procedure instantiates a generated WCF service client object called ProductServiceClient. Next it hooks up the GetProductsCompleted event procedure that is called when the asynchronous call to the GetProductsAsync() method is complete. In the GetProductsCompleted event procedure you take the e.Result property and give it to the DataContext property of the list box called lstData.
In the list box named lstData you need to set the ItemsSource property to {Binding}. This tells the list box that you will be binding the data at runtime by setting the DataContext property. You also fill in the DisplayMemberPath property with a property of the Product object that you wish to be displayed in each row of the list box. In this case you are using the ProductName property.
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="ProductName"
Name="lstData" />
That is all there is to writing the code behind and having it load data from a Product WCF service. The problem with the above code is in order to test this code someone has to actually run the program and verify that this code works as it is supposed to. If you make a change to the service or to the UI you will then need to have someone run the application again to ensure it still works. You have to repeat this testing each time a change is made. This becomes very tedious and time consuming for the developer and the tester.
Load a List Box using MVVM
To take the previous sample and convert it into an MVVM design pattern, you simply need to create a class for your Silverlight user control to bind to. This class, called ProductViewModel recreates the UserControl_Loaded event procedure and the GetProductsCompleted event procedure as the LoadAll method in the class and the GetProductsCompleted event procedure.
When the data is returned from the WCF service in the GetProductsCompleted event, that data is placed into a property called DataCollection as opposed to setting the DataContext property of a list box directly. This property is of the type ObservableCollection<Product>. The only thing unique about this property is in the “set” procedure; you not only set the value to the _DataCollection field, you also call a method named RaisePropertyChanged. This method raises the PropertyChanged event procedure. This is a standard event in XAML whose sole job is to inform any UI elements bound to this property that they should call the “get” procedure because the data has just been updated.
I will let you look up the implementation of the PropertyChanged event procedure in the sample code that you download for this posting. Other than that, the complete View Model class is shown in the list below:
public class ProductViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged
/// <summary>
/// The PropertyChanged Event Informs the UI
/// when a Property in this class changes
/// </summary>
/// IMPLEMENTATION GOES HERE
#endregion
private ObservableCollection<Product> _DataCollection;
public ObservableCollection<Product> DataCollection
{
get { return _DataCollection; }
set
{
_DataCollection = value;
RaisePropertyChanged("DataCollection");
}
}
public void LoadAll()
{
ProductServiceClient client = new ProductServiceClient();
client.GetProductsCompleted += new
EventHandler<GetProductsCompletedEventArgs>
(client_GetProductsCompleted);
client.GetProductsAsync();
client.CloseAsync();
}
void client_GetProductsCompleted(object sender,
GetProductsCompletedEventArgs e)
{
DataCollection = e.Result;
}
}
The above class is not that much more code that you wrote in the code behind. In fact, the implementation of the PropertyChanged event can be put into a base class that all your view models inherit from. This will avoid duplicating this code in each view model class.
Bind ProductViewModel Class to XAML
Once you have the ProductViewModel class created; you now bind your User Control to this class. Any Silverlight user control may create an instance of any class in your application within XAML. First you create an XML namespace as shown by the arrow next to “Callout 1” in Figure 1.
Figure 1: XAML can create an instance of a class in the UserControl.Resources section of your user control.
The XML namespace is given an alias and references a .NET namespace from your Silverlight application. In this XAML the alias for the namespace is called “vm”. Next, you create a UserControl.Resources section in the XAML and using an XML element you specify the XML namespace followed a colon and then by the class name you wish to instantiate as shown by “Callout 2” in Figure 1.
The following line of code in the XAML is what creates an instance of the ProductViewModel class and assigns it to the static resource variable name listed in the x:Key attribute.
<vm:ProductViewModel x:Key="viewModel" />
The above line is the equivalent of the following in .NET:
using vm = SL_MVVM_Easy;
vm.ProductViewModel viewModel = new vm.ProductViewModel();
Bind the instance of this class to the list box control you created on your Silverlight user control using the standard {Binding} syntax as shown in Figure 2.
Figure 2: Bind the ListBox to the resource you created
In the ItemsSource of the list box you specify the source of the data as coming from the instance of the ProductViewModel class you created in the resources section. The Path property in the Binding specifies the property in the class you wish to get the collection of data from. In this case the name of the property in the ProductViewModel class is called DataCollection (Figure 3).
Figure 3: The Path attribute refers to the property where the data is coming from in the class to which this UI object is bound
Write the Code Behind
In your User Control you still need the UserControl_Loaded event procedure just like you did when you were not using the MVVM design pattern. The only difference is you will only have one line of code to call the LoadAll() method in the ProductViewModel class.
Since the XAML created the ProductViewModel class you need to get a reference to that specific instance so you can use it in the code behind. In the constructor you will use the Resources collection to access the x:Key you assigned to the view model. In this case the key name is “viewModel”. So you access the resources on this page using this.Resources[“viewModel”] and casting the result as a ProductViewModel class and putting it into a field variable on this user control called _ViewModel. You can now call any method or access any property in the ProductViewModel class through this field.
public partial class ucMVVM : UserControl
{
ProductViewModel _ViewModel;
public ucMVVM()
{
InitializeComponent();
_ViewModel = (ProductViewModel)this.Resources["viewModel"];
}
private void UserControl_Loaded(object sender,
System.Windows.RoutedEventArgs e)
{
_ViewModel.LoadAll();
}
}
That is all there is to migrating your code into an MVVM design pattern. The primary advantage of this design pattern is you can now write a unit test to test the view model class. You do not need to have a tester run the application in order to verify that everything worked. In addition you also now have a view model class that you can reuse in a WPF or Windows Phone application. You could even reuse this view model class in an ASP.NET application.
Summary
Hopefully this post helped you see how easy it is to move code from the code behind of your user interface and put it into a class. That is the whole key to MVVM; simply moving code into a class. Do not worry about being 100% “code behind free”. That is an almost impossible goal and most often requires you to write more code! That is certainly not a good thing. If your event procedures in your UI are simply doing UI code or making a single call to a method in your View Model, then you have accomplished the goals of MVVM; namely reusability, maintainability and testability. You also get one more benefit; having event procedures in your UI makes it easier to follow the flow of the application.
NOTE: You can download this article and many samples like the one shown in this blog entry at my website. http://www.pdsa.com/downloads. Select “Tips and Tricks”, then “The Basics of MVVM” from the drop down list.
Good Luck with your Coding,
Paul Sheriff
** SPECIAL OFFER FOR MY BLOG READERS **
We frequently offer a FREE gift for readers of my blog. Visit http://www.pdsa.com/Event/Blog for your FREE gift!
Past Blog Content
Blog Archive
-
2015
-
2014 (18)
-
2013 (11)
-
2012 (19)
-
2011 (29)
-
2010 (19)
-
2009 (28)
-
2008 (0)
-
2007 (14)
-
2006 (6)