The Basics of MVVM in WPF
This is a follow-up to my previous blog post on the Basics of MVVM in which I used Silverlight to illustrate how to use a basic MVVM design. I had so many emails asking for a WPF version, I decided to post this sample using WPF.
Load a WPF List Box without MVVM
To start, let's write code to load a list box control in WPF without using the MVVM design pattern. Create a new WPF project and add a User Control named ucNoMVVM to this project. Add a ListBox control to the user control and set the Name property to lstData. Add a Loaded event procedure to the XAML so it will create a Window_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) uses a Product table in a database to load data in the UserControl_Loaded event procedure.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
GetProducts();
}
private void GetProducts()
{
Products coll = new Products();
string sql = string.Empty;
SqlDataAdapter da = null;
DataTable dt = new DataTable();
sql = "SELECT ProductName ";
sql += " FROM Product ";
// Create Data Adapter
da = new SqlDataAdapter(sql,
ConfigurationManager.
ConnectionStrings["ConnectString"].
ConnectionString);
// Fill DataTable with Products
da.Fill(dt);
// Loop thru all rows
// Create a new Product object each time thru
foreach (DataRow dr in dt.Rows)
{
Product prod = new Product();
prod.ProductName =
Convert.ToString(dr["ProductName"]);
coll.Add(prod);
}
// Fill in ListBox
lstData.DataContext = coll;
}
The code in the GetProducts method is fairly straight forward ADO.NET code to retrieve data from a Product table, create a DataTable and loop through each row of the DataTable to create a new Product object. Each Product object is added to a Products collection and when all rows have been loaded, that collection of product objects is assigned to the DataContext property of the list box named lstData.
In the XAML of the list box you 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 table. 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 database 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 in WPF
To take the previous sample and convert it into an MVVM design pattern, you simply need to create a class for your WPF User Control to bind to. This class, called ProductViewModel creates a public method named GetProducts. In addition a public property called DataCollection is created. In the "set" of this property you will raise a PropertyChanged event to inform the ListBox control that will be bound to this property to refresh its data by calling the "get" procedure in this property.
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 to raise to any UI object
/// </summary>
#endregion
private Products _DataCollection;
public Products DataCollection
{
get { return _DataCollection; }
set
{
_DataCollection = value;
RaisePropertyChanged("DataCollection");
}
}
public void GetProducts()
{
Products coll = new Products();
string sql = string.Empty;
SqlDataAdapter da = null;
DataTable dt = new DataTable();
sql = "SELECT ProductName ";
sql += " FROM Product ";
// Create Data Adapter
da = new SqlDataAdapter(sql,
ConfigurationManager.
ConnectionStrings["ConnectString"].
ConnectionString);
// Fill DataTable with Products
da.Fill(dt);
// Loop thru all rows
// Create a new Product object each time thru
foreach (DataRow dr in dt.Rows)
{
Product prod = new Product();
prod.ProductName =
Convert.ToString(dr["ProductName"]);
coll.Add(prod);
}
// Assign to DataCollection property to
// raise PropertyChanged event
DataCollection = coll;
}
}
The above class is not that much more code than 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 WPF user control (or Window) 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 WPF 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 = WPF_MVVM_Easy;
vm.ProductViewModel viewModel = new vm.ProductViewModel();
Bind the instance of this class to the list box control you created on your WPF 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 GetProducts() 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 user control 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, 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 ASP.NET or Windows Forms 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 in WPF" 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)