Understanding and Using MVVM Light in Silverlight and Blend - Part I

MVVM is an architecture that helps you design Silverlight apps right. If you want to simplify your move to MVVM then take a look at MVVM Light. This article is introduction to the MVVM Light Toolkit and how you can start to take advantage of its rich feature set


Introduction

If you are a fan of MVVM, as I am, then you absolutely need a tool like MVVM Light.  MVVM Light is a free open source project created by a guy named Laurent Bugnion of GalaSoft.   You can download the MVVM Light Toolkit free from code plex and start using it right away in your Silverlight Project.  Basically, MVVM Light fills in the frusterating gaps you may have encountered when trying to  use the MVVM Architecture with Silverlight and Blend. 

Installation

MVVM Light comes with Project Templates, Class Templates, and even snippets to make using the MVVM Light ToolKit easier.  Laurent has written an entire blog on just installing all of these nifty tools. This blog gives you prerequisistes (such as Expression Blend 4),  and steps for installing each of the goodies.


Architecture

MVVM Light consists of a few different components all related to the MVVM Architecture.  There are two components that I use all the time from the tool kit:  the ViewModelLocator and EventToCommand.  With these two components, in conjunction with components from the Blend SDK, you can pretty much get rid on 99% of your code behind and build clean MVVM Architected apps.


ViewModelLocator

In this first article we are going to talk about the first component of MVVM Light: the ViewModelLocator.  Once you've installed the many component templates in the toolkit, you can take advantage of having Visual Studio generate a ViewModelLocator for your project.  Just go to Project -> Add New Item and choose the MvvmViewModelLocator as shown in figure 1:

img4.jpg

Figure 1 - Picking a ViewModelLocator for your project

The ViewModelLocator allows you to declare one in your XAML file and let's you specify two different ViewModel instances:  One for DesignTime and One for Runtime.  This is very very useful when you want to make sure that your View can show up in blend so that you can manipulate the presentation design.  If you have bindings in your viewmodel that screw up blend, no problem, just return a bogus view model from the locator.  The view model locator generates a constructor that allows you to detect design time from run time.  If your bindings are solid, you can leave the ViewModelLocator alone and separate your runtime data from your design time data in the ViewModel itself.

Listing 1 - ViewModelLocator constructor

public MvvmViewModelLocator1()
{
if (ViewModelBase.IsInDesignModeStatic)
{
 Create design time view models
}
else
{
 Create run time view models
}
}

Note that your ViewModel needs to inherit from ViewModelBase inside of MVVMLight to take advantage of this feature.  That's no problem because MVVMLight gives you a class template for ViewModels as well shown in listing 2.  Now in your ViewModel you can also specify design time vs run time data.

Listing 2 - ViewModel Generated From MVVM Light Class Template

using GalaSoft.MvvmLight;

namespace SpellingGame.ViewModel
{

       /// <summary>
      
/// This class contains properties that a View can data bind to.
      
/// <para>
      
/// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
      
/// </para>
      
/// <para>
      
/// You can also use Blend to data bind with the tool's support.
      
/// </para>
      
/// <para>
      
/// See http://www.galasoft.ch/mvvm/getstarted
      
/// </para>
      
/// </summary>

       public class SpellingGameViewModel : ViewModelBase
      
{
             
/// <summary>
             
/// Initializes a new instance of the SpellingGameViewModel class.
             
/// </summary>
             
public SpellingGameViewModel()
             
{
                    
if (IsInDesignMode)
                    
{
                          
// Code runs in Blend --> create design time data.
                    
}
                    
else
                    
{
                          
// Code runs "for real": Connect to service, etc...
                    
}
             
}

               ////public override void Cleanup()
             
////{
             
////    // Clean own resources if needed
 
              ////    base.Cleanup();
             
////}
      
}

}

Let's look at an example on how this might be used.  I created an application in Blend 4 called Spelling Game.  It consists of 3 buttons and an image.  If you press the first button it will show you the first letter describing the image.  If you press the second button, it will show you the second letter describing the image and if you press the third button it will show you the third letter.  The design in Blend looks something like this as shown in Figure 2.

img5.jpg

Figure 2 - Spelling Game

What I'd really like to see in my design view is a sample design of the whole screen with a sample spelling coming from my view model and a sample image.  That's where the ViewModelLocator comes in.  Let's look at the steps to preparing the ViewModelLocator to do this:

First, use the mvvmlocatorproperty snippet to add a ViewModel to your ViewModelLocator class that you generated in listing 1. Change the type in the snippet to your ViewModel. The snippet will add the following code to your locator class:

Listing 3 - Code added by the snippet to the ViewModelLocator

              private static SpellingGameViewModel _viewModelPropertyName;
 

              /// <summary>
             
/// Gets the ViewModelPropertyName property.
             
/// </summary>
             
public static SpellingGameViewModel ViewModelPropertyNameStatic
             
{
                    
get
                    
{
                          
if (_viewModelPropertyName == null)
                          
{
                                 
CreateViewModelPropertyName();
                          
}
 

                           return _viewModelPropertyName;
                    
}
             
}

               /// <summary>
             
/// Gets the ViewModelPropertyName property.
             
/// </summary>
             
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
                    
"CA1822:MarkMembersAsStatic",
                    
Justification = "This non-static member is needed for data binding purposes.")]
             
public SpellingGameViewModel ViewModelPropertyName
             
{
                    
get
                    
{
                          
return ViewModelPropertyNameStatic;
                    
}
             
}

              /// <summary>
             
/// Provides a deterministic way to delete the ViewModelPropertyName property.
             
/// </summary>
             
public static void ClearViewModelPropertyName()
             
{
                    
_viewModelPropertyName.Cleanup();
                    
_viewModelPropertyName = null;
             
}

              /// <summary>
             
/// Provides a deterministic way to create the ViewModelPropertyName property.
             
/// </summary>
             
public static void CreateViewModelPropertyName()
             
{
                    
if (_viewModelPropertyName == null)
                    
{
                          
_viewModelPropertyName = new SpellingGameViewModel();
                    
}
             
}

 

              /// <summary>

              /// Cleans up all the resources.

              /// </summary>

              public static void Cleanup()

              {

                     ClearViewModelPropertyName();

              }

 

The snippet is nice, because it makes hooking up the locator fairly straightforward. If you don't have the snippet, you can just copy the code in this article and rename the ViewModel to your particular view model.  The next step is to put the locator into the app.xaml file so that the View can access it in XAML.

Listing 4 - Adding the locator to the App.Xaml file

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           
 x:Class="SpellingGame.App"
           
 >

   
<Application.Resources>
       
<vm:SpellingGameViewModelLocator xmlns:vm="clr-namespace:SpellingGame.ViewModel"
                                 
 x:Key="Locator" />

   
</Application.Resources>
</
Application>

Next, we want to hook up the ViewModelLocator into the data context of  our view.  The XAML for this step is shown in listing 5:

Listing 5 - Hooking up the Locator to the data context of the view

<UserControl x:Class="SpellingGame.MainPage"
  
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  
 mc:Ignorable="d"
  
 d:DesignHeight="300" d:DesignWidth="500" xmlns:my="clr-namespace:SpellingGame"
    xmlns
:ValueConverter="clr-namespace:SpellingGame.Converter"
    DataContext
="{Binding   Source={StaticResource Locator}, Path=ViewModelPropertyName}" >

Now we are ready to split the design and runtime implementation inside our ViewModel.  Let's see how we do it for our SpellingGame implementation.  Listing 6 shows our implementation of the SpellingGameViewModel.  There are two main properties:  WordToSpell and ImageUri.  The WordToSpell will be randomly chosen from a list, but in design view we just want to specify the  hard-coded word "CAT" .   One thing to also note about the ViewModel, is by inheriting from ViewModelBase, you get  the functionality of INotifyPropertyChanged for free.  MVVM Light gives you an easy method called RaisePropertyChanged to trigger your view to update itself from the model (much less painful then PropertyChanged(this, ....)

Listing 6 - The SpellingGame ViewModel that reflects both design and runtime views

using System;
using
System.Collections.Generic;
using
System.Linq;
using
GalaSoft.MvvmLight;
 

namespace
SpellingGame.ViewModel
{
      
/// <summary>
      
/// This class contains properties that a View can data bind to.
     
/// <para>
      
/// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
      
/// </para>
      
/// <para>
      
/// You can also use Blend to data bind with the tool's support.
      
/// </para>
      
/// <para>
      
/// See http://www.galasoft.ch/mvvm/getstarted
      
/// </para>
      
/// </summary>
      
public class SpellingGameViewModel : ViewModelBase
      
{
             
private string _wordToSpell;
             
public string WordToSpell
             
{
                    
get { return _wordToSpell; }
                    
set
                    
{
                          
_wordToSpell = value;
                          
RaisePropertyChanged("WordToSpell");
                    
}
             
}

              Random _random = new Random((int)DateTime.Now.Ticks);

               /// <summary>
             
/// Initializes a new instance of the SpellingGameViewModel class.
             
/// </summary>

              public SpellingGameViewModel()

              {

                     if (IsInDesignMode)

                     {

                           // Code runs in Blend --> create design time data.

                           // add phony data here

                           WordToSpell = "CAT";

                     }

                     else

                     {

                           // Code runs "for real": Connect to service, etc...

                           int len = threeLetterWordsMap.Keys.Count;

                           var randomVal = _random.Next(len);

                           var wordPairVal = threeLetterWordsMap.Keys.Select((word, index) => new

                                                                                                {

                                                                                                       Index = index,

                                                                                                      Word = word

                                                                                                   }).FirstOrDefault(val => val.Index == randomVal);

 

                           WordToSpell =  wordPairVal.Word;

 

                     }

 

                     ImageUri = threeLetterWordsMap[WordToSpell];

 

              }

              private string _imageUri;
             
public string ImageUri
             
{
                    
get { return _imageUri; }
                    
set { _imageUri = value; RaisePropertyChanged("ImageUri");}
             
}

              private Dictionary<string, string> threeLetterWordsMap = new Dictionary<string, string>()
             
                                                              {
             
                                                                     {"PIT", ""},
             
                                                                     {"SIT", ""},
            
                                                                     {"SAT", ""},
             
                                                                     {"FIT", ""},                                                                               {"CAT", "http://www.ncbi.nlm.nih.gov/projects/genome/guide/img/cat.jpg"},
             
                                                                     {"DOG", ""}

                                                                            };

 

              ////public override void Cleanup()
             
////{
             
////    // Clean own resources if needed
 
              ////    base.Cleanup();
             
////}

       }

} 

The result is that in Blend, you can now see that not only are your view components working, but you can also see your bindings working the way you would expect. This gives you the added advantage of being able to tweak your visual design with data present.  (Note: You can see the bound data in the Designer in Visual Studio 2010 as well).


img6.jpg

Figure 3 - Spellling Game Shown At Design Time in Blend

Conclusion

It does seem like a bit of work to hook up the ViewModelLocator, but it is a cool feature, especially if your view requires heavy visual design in Blend.  If you want to simplify this a bit, you may want to take a look at JohnPapa's Blog on the topic which takes advantage of MEF.  And if you are big into using IOC containers to get your ViewModel,  check out the blog by roboblob, probably one of the best implementations of the ViewModelLocator.  In any case,  MVVM Light is a Blend-Friendly library which can aid you in spelling out your concerns for Model-View separation.