Monday 1 April 2013

MVVM - ViewModelBase

If you are developing a WPF application then you really need to be using the MVVM(Model-View-ViewModel) design pattern. I'm sure the vast majority of prospective employers searching for a WPF developer well expect some familiarity with MVVM. I'm not going to spend hours explaining this design pattern as many people have already done that rather well such as here and here.

For those of you already familiar with MVVM you will be aware that certain steps one needs to take can be rather repetitive and fairly boring. There are a number of frameworks available that aim to simplify the development of WPF-MVVM applications, such as the MVVM Light Toolkit. I've found them useful myself many times, but what if you're developing an application that you don't want relying on third party code?

I'm going to be adding some posts over the next few weeks that take inspiration from these frameworks and can help to minimise the tedious tasks without using any third party code. I'll be starting with the most basic ViewModelBase.

Often your ViewModel needs to be able to update the View via a binding. This is achieved by implementing the INotifyPropertyChanged interface. Microsoft provided us with this interface to give us a mechanism to notify a client(View) of a property value change.

An application could have many ViewModels, imagine if we had to implement this interface in each one of these! This is one of the first tedious tasks you are likely to come across when implementing MVVM. So what we can do is define an abstract ViewModelBase class that implements the INotifyPropertyChanged interface for us then all our ViewModels can inherit from this so as to automatically gain this functionality. The code below shows our ViewModelBase class.

  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4.  
  5. namespace JourneyIntoCode
  6. {
  7.     public abstract class ViewModelBase : INotifyPropertyChanged
  8.     {
  9.         #region INotifyPropertyChanged Members
  10.  
  11.         public event PropertyChangedEventHandler PropertyChanged;
  12.  
  13.         public void OnPropertyChanged(string propertyName)
  14.         {
  15.             this.CheckPropertyName(propertyName);
  16.  
  17.             if (this.PropertyChanged != null)
  18.             {
  19.                 this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  20.             }
  21.  
  22.         }
  23.  
  24.         #endregion // INotifyPropertyChanged Members
  25.  
  26.         #region Check property name
  27.  
  28.         [Conditional("DEBUG")]
  29.         [DebuggerStepThrough]
  30.         public void CheckPropertyName(string propertyName)
  31.         {
  32.             if (TypeDescriptor.GetProperties(this)[propertyName] == null)
  33.                 throw new Exception(String.Format("Could not find property \"{0}\"", propertyName));
  34.         }
  35.  
  36.         #endregion // Check property name
  37.     }
  38. }

This is what our ViewModel will look like when we inherit ViewModelBase.

  1. class ViewModelTest : ViewModelBase
  2. {
  3.     private string _testProperty;
  4.  
  5.     public string TestProperty
  6.     {
  7.         get { return _testProperty; }
  8.  
  9.         set
  10.         {
  11.             if (value != _testProperty)
  12.             {
  13.                 _testProperty = value;
  14.                 OnPropertyChanged("TestProperty");
  15.             }
  16.         }
  17.     }
  18.  
  19. }

To implement INotifyPropertyChanged in ViewModelBase we need to provide two things, the PropertyChanged event and the OnPropertyChanged method. The OnPropertyChanged method needs to raise the PropertyChanged event which will then notify our View for us. You will also notice we have a CheckPropertyName method. This is because, in .NET 4 and earlier, when calling OnPropertyChanged from our ViewModel we have to pass the property name as a string and we all know how easily typos can occur. The CheckPropertyName method is decorated with two attributes, the CondtionalAttribute and the DebuggerStepThroughAttribute. These attributes mean that our CheckPropertyName method will only be included in the Debug build of our assembly and when we're debugging, the debugger will step over and not into this method whilst still executing it. We then use TypeDescriptor to check the property name really exists on our ViewModel.

You may have noticed in the previous paragraph I said in .Net 4 and earlier we have to pass the property name as a string to our OnPropertyChanged method. That's because .Net 4.5 gives us a cool new attribute, CallerMemberName. This allows us to modify our OnPropertyChanged method like so:

  1. public void OnPropertyChanged([CallerMemberName]string propertyName = null)
  2. {
  3.     if (this.PropertyChanged != null)
  4.     {
  5.         this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  6.     }
  7. }

Meaning our ViewModel now looks like this:

  1. class ViewModelTest : ViewModelBase
  2. {
  3.     private string _testProperty;
  4.  
  5.     public string TestProperty
  6.     {
  7.         get { return _testProperty; }
  8.  
  9.         set
  10.         {
  11.             if (value != _testProperty)
  12.             {
  13.                 _testProperty = value;
  14.                 OnPropertyChanged();
  15.             }
  16.         }
  17.     }
  18. }

This is much neater, no need for our CheckPropertyName method and one less potential bug. However, there is one drawback. .Net 4.5 is not supported in Windows XP, you need Vista or later so if you're going to be developing your application in .Net 4.5 you need to be aware you will be disregarding XP users.

No comments:

Post a Comment