Logo
PREGNANCY
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
 
 
Windows Phone

The Model-View-ViewModel Architecture (part 2) - GalaSoft MVVM Light Toolkit

10/29/2011 4:42:14 PM

2. Pick a MVVM Helper SDK

Because Silverlight for Windows Phone 7 is based on Silverlight 3, it falls short of the MVVM capabilities available in Silverlight 4 and WPF, such as full Commanding support. Luckily, there are quite a few third-party MVVM frameworks available to choose from that provide Commanding support and more. The following lists a few in no particular order:

  • Caliburn

  • nRoute

  • Composite Application Block/PRISM

  • SilverlightFX

  • GalaSoft's MVVM Light Toolkit

The major benefit that these frameworks have in varying degrees is increasing separation of concerns, unit testing support, and support for Commanding. You can find arguments for and against the available frameworks, so please investigate the available options. For this example, let's take MVVM Light Toolkit for a spin, as it is this author's opinion that MVVM Light provides a nice balance of power and simplicity that is a great fit for phone application development. Many others would suggest Caliburn instead for similar reasons. The most important thing is to pick a helper SDK, learn it, and use it.

3. GalaSoft MVVM Light Toolkit

For Windows Phone 7 development, my preference is GalaSoft's MVVM Light Toolkit. In my opinion it has the right combination of power and as the name says, lightness, for a mobile phone application.

The MVVM Light Toolkit works across WPF and desktop Silverlight as well, which greatly aids in porting code to another screen such as a Windows Slate application.


The MVVM Light Toolkit is up to version 3 SP1. It was originally developed to address the Commanding shortfalls in Silverlight 2 and Silverlight 3 that I highlighted in the previous section when covering the BasicMVVM sample project. The MVVM Light Toolkit also includes customized Visual Studio 2010 templates to help you get started right away. First, download the MVVM Light Toolkit and follow the instructions at this page:

http://galasoft.ch/mvvm/getstarted/

If you like the MVVM Light toolkit, I encourage you to click the Donate button at the bottom of the above page, which goes towards the cost of running the site and the rest goes to charity. The source code is available in CodePlex here:

http://mvvmlight.codeplex.com/
The entire range of features of the MVVM Light Toolkit are not described end-to-end in this book, but the next couple of sections cover the features of MVVM Light used to migrate the BasicMVVM to MVVM Light.
3.1. MVVM Light Sample

In Visual Studio 2010, you can select File => New Project, and click on Silverlight for Windows Phone to filter to the WP7 projects. Select MvvmLight (WP7) as the project template to get started. If you don't see that option, check to ensure that you installed the toolkit correctly before proceeding.

Once the project is created, run it and the screen in Figure 3 should appear, which indicates that all is configured correctly.

Figure 3. Testing your MVVM Light installation

The page title, subtitle, and text are all data bound to the MainViewModel class. Let's go through the architecture of MVVM Light works so that you have a basis as we migrate the BasicMVVM app to MVVM Light. After creating a new project, the project includes the default App.xaml and MainPage.xaml as well as the other items expected in a Silverlight for Windows Phone 7 application but with a couple of additions. There is an empty Model folder that we populate shortly. There is also a ViewModel folder that contains two ViewModel classes named MainViewModel and ViewModelLocator. I cover these in the next section.

3.2. ViewModelLocator

The ViewModelLocator class contains a reference to every ViewModel class in the project. This provides a centralized way to manage creation and allow XAML configuration via a single application resource. By default, in App.xaml a resource is added for the ViewModelLocator class. A namespace is added to the <Application> object that hosts the PhoneApplicationFrame that contains the XAML pages or View classes as they are navigated:

xmlns:vm="clr-namespace:MvvmLightSample.ViewModel"

The ViewModelLocator class is configured as an application level resource, as in the following:

<Application.Resources>
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>

This resource is available through the entire application, just like any other application-level resource, making the referenced ViewModel objects available now. Now we move on to explain how MainPage.xaml data binds to MainViewModel within this architecture. In the XAML, the <PhoneApplicationPage> element's DataContext property data binds to the Locator resource discussed above via this XAML code:

DataContext="{Binding Main, Source={StaticResource Locator}}"

The Path property for the Binding object is configured with the value of Main using the default syntax (it can also be written as Path=Main). This configuration makes an instance of the MainViewModel available within MainPage.xaml and allows the page title, sub-title, and text to data bind to properties available on the MainViewModel. Here's an example of one of the Application Title Bindings:

<TextBlock x:Name="ApplicationTitle"
Text="{Binding ApplicationTitle}"
Style="{StaticResource PhoneTextNormalStyle}" />

Listing 3 shows the default ViewModelLocator class.

Example 3. Default ViewModelLocator Class Code File
namespace MvvmLightSample.ViewModel
{
public class ViewModelLocator
{
private static MainViewModel _main;

/// <summary>
/// Initializes a new instance of the ViewModelLocator class.
/// </summary>
public ViewModelLocator()
{
//if (ViewModelBase.IsInDesignModeStatic)
//{
// // Create design time view models
//}
//else
//{
// // Create run time view models
//}

CreateMain();
}

/// <summary>
/// Gets the Main property.
/// </summary>
public static MainViewModel MainStatic
{
get
{

if (_main == null)
{
CreateMain();
}

return _main;
}
}

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

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

/// <summary>
/// Provides a deterministic way to create the Main property.
/// </summary>
public static void CreateMain()
{
if (_main == null)
{
_main = new MainViewModel();
}
}

/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
ClearMain();
}
}
}


Notice in Listing 3 how the MainViewModel is made available. It is created in the ViewModelLocator constructor via a call to the CreateMain() method. There is also a ClearMain() method to provide a garbage collection safe way to clear the resource from memory, which is important on Windows Phone 7 with the goal of keeping application memory usage under 90MB for best performance.

The MainViewModel is available via a static property, which makes it more straightforward to find ViewModel objects by simply typing ViewModelLocator.MainStatic. MainStatic contains the reference to the MainViewModel object but for data binding purposes it must be a non-static variable. A second non-static property is declared named Main that can be configured in data binding. The Main property simply returns the static instance under the covers.

To add additional ViewModel objects, manually edit the ViewModelLocator class to include two properties for the additional ViewModel objects. There is a code snippet available to automate this process. Type mvvmlocatorproperty and tap the Tab key twice to quickly add the property using Visual Studio 2010's code snippet template UI. Essentially, type new values for the default and click the Tab key to move through the template to quickly add the property combination in the correct format.

3.3. MvvmLightSample – Model

The model class Vendor is copied from the BasicMVVM class. Edit the namespace at the top to be MVVMLightSample.Models so it makes sense for this project. A reference is added to System.Runtime.Serialization assembly. That's all that's required to add it to the MVVM Light Toolkit version.

3.4. MvvmLightSample – VendorsViewModel

The VendorsViewModel class is copied over from the BasicMVVM project, and the namespaces are fixed up for the MvvmLightSample project. To take advantage of as much of the framework as possible, replace INotifyPropertyChanged interface with the ViewModelBase class instead. Add a using clause for GalaSoft.MvvmLight. Remove the INotifyPropertyChangedPropertyChanged event and method from VendorsViweModel since we get it for free by inheriting from ViewModelBase class. Change NotifyPropertyChanged("") calls to base.RaisePropertyChanged("") to fix up compile errors.

Add a reference to Sytem.ServiceModel.Web to make the DataContractJsonSerializer class available within the VendorsViewModel. Remove the InDesignTime property and replace it with a call to GalaSoft.MvvmLight.IsInDesignMode instead. At this point, the migration is complete for the ViewModel.

3.5. MvvmLightSample – VendorsView

A new folder named View is added to the MVVMLightSample project, and the VendorsView.xaml page is copied from the BasicMVVM project to the MVVMLightSample view folder. Do a Find / Replace with BasicMVVM to replace it with MvvmLightSample with the "Look in" set to Current Project. That fixes up namespace references to compile the project successfully.

Next MainViewModel is modified to return a more appropriate page title and sub-title as well as string text for the Welcome message property, now configured to point to a StoreLocatorPage property. Clicking on that text will navigate you to the VendorsView.xaml page. Fix up the margins so that everything aligns at 24px on the left and then add a NavigateToPageAction behavior to the TextBlock containing the text Store Locator Page and configure it to point to the VendorsView page.

If you run the project, it works as before, but let's configure the VendorsView to take advantage of the MVVM Light toolkit capabilities. First, add a property combination to the ViewModelLocator via the mvvmlocatorproperty code snippet. By using the code snippet, it quickly generates this code for you in the ViewModelLocator class:

private static VendorViewModel _vendors;

/// <summary>
/// Gets the Vendors property.
/// </summary>
public static VendorViewModel VendorsStatic
{
get
{
if (_vendors == null)
{
CreateVendors();
}

return _vendors;
}
}

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

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

/// <summary>
/// Provides a deterministic way to create the Vendors property.
/// </summary>
public static void CreateVendors()
{
if (_vendors == null)
{
_vendors = new VendorViewModel();
}
}


As you can see, if you are not familiar with Visual Studio 2010 the code snippet is quite handy! One item to note is that the snippet adds another Cleanup() method so just copy ClearVendors() to the existing Cleanup() method and delete the one added.

Now that we have the VendorsView added to the ViewModelLocator, we can configure the VendorsView to data bind the MVVM Light Toolkit way using Expression Blend. First compile the project to make sure everything is up to date, and then remove the DataContext binding on the LayoutRootGrid. Also remove the VendorViewModelDataSource from the PhoneApplicationPage.Resources section in the VendorsView.xaml file.

In Expression Blend, select the PhoneApplicationPage root item in the Objects and Timeline tool window. Find the DataContext property, click the Advanced Options... button, and select Data Binding... to bring up the Create Data Binding Dialog Window. Select the Locator data source and then select Vendors, as shown in Figure 4.

Figure 4. Data bind DataContext to the VendorsViewModel

Be sure to select the non-static property so the data binding work correctly. Run the application and navigate to the VendorsView and it displays the data as before. We still have the event handlers in the code-behind. In the next subsection the event handlers are removed and instead the application takes advantage Commanding support provided by the MVVM Light toolkit.

3.6. Commanding and RelayCommand

The MVVM Light Toolkit supports Commanding, or data binding events to ViewModel methods, via the RelayCommand and RelayCommand<T> classes. In the VendorViewModel class, two RelayCommand instances are added in a region named Commanding, one that is parameter-less and one that takes a parameter. Here is the declaration:

#region Commanding
public RelayCommand AddAVendorCommand
{
get;
private set;
}

public RelayCommand<Vendor> RemoveAVendorCommand
{
get;
private set;
}
#endregion

The commands are instantiated in the VendorViewModel() constructor in this code:

//Instantiate Commands
AddAVendorCommand = new RelayCommand(
() => AddVendor());

RemoveAVendorCommand = new RelayCommand<Vendor>(
param => RemoveVendor(param));

The RelayCommand objects bridge between the ViewModel methods and the UI events. RelayCommand has support for one parameter only so you need to pass more info, consider encapsulating into an object. Now it's time to data bind the commands in the UI. Currently the application uses the application bar to execute Add and Remove. Unfortunately the ApplicationBarIconButton class does not inherit from FrameworkElement so the ButtonBaseExtension cannot be attached to a DependencyObject. You can still call the relay in code behind as before. Here is an example from the sample:

private void insertVendorAppBarBtn_Click(object sender, EventArgs e)
{
var vm = DataContext as VendorViewModel;
if (vm != null)
{
vm.AddAVendorCommand.Execute(null);
}
}

For this sample, the two Buttons are added so that we can demonstrate the EventToCommand Expression Blend behavior, as well as via the code-behind for the application bar buttons. In Expression Blend, switch to the Assets tab and select Behaviors to filter the list. Drag the EventToCommand behavior on to the Button objects and configure the correct command on each button. For the Remove Button, data bind the EventToCommandCommand property to the RemoveAVendorCommand RelayCommand. Also data bind the CommandParameter for the RemoveEventToCommand object to the vendorsListBox.SelectedItem method, which returns a Vendor object. Here is the resulting markup for Remove:

<Button x:Name="RemoveButton" Content="Remove" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,8,18">

<Custom:Interaction.Triggers>
    <Custom:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command=
"{Binding RemoveAVendorCommand, Mode=OneWay}"
CommandParameter="{Binding SelectedItem, ElementName=vendorsListBox}"/>
</Custom:EventTrigger>
</Custom:Interaction.Triggers>
</Button>


The RelayCommand class also supports CanExecute and CanExecuteChanged members as well to determine whether or not to enable or disable the element, in this case a Button object. The CanExecute method can be passed in to the constructor as a second parameter. Here's an example:

AddAVendorCommand = new RelayCommand(
() => AddVendor(), () => CheckEnabled);

We have now completely migrated the sample over to MVVM. Listing 4 shows the source code for the updated VendorViewModel class.

Example 4. Updated VendorViewModel Code File
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using MvvmLightSample.Models;

namespace MvvmLightSample.ViewModel
{
public class VendorViewModel : ViewModelBase
{
public VendorViewModel()
{
if (IsInDesignMode)
{
LoadSampleData();
}
else
{
LoadData();
}

//Instantiate Commands
AddAVendorCommand = new RelayCommand(
() => AddVendor());

RemoveAVendorCommand = new RelayCommand<Vendor>(
param => RemoveVendor(param));

}



#region Design-time support
private void LoadSampleData()
{
_vendors = new ObservableCollection<Vendor>()
{
new Vendor(){AccountNumber="111111", CreditRating=65,
Name="DesignTime - Fabrikam Bikes" },
new Vendor(){AccountNumber="222222", CreditRating=40,
Name="Contoso Sports" },
new Vendor(){AccountNumber="333333", CreditRating=30,
Name="Duwamish Surfing Gear" },
new Vendor(){AccountNumber="444444", CreditRating=65,
Name="Contoso Bikes" },
new Vendor(){AccountNumber="555555", CreditRating=40,
Name="Fabrikam Sports" },
new Vendor(){AccountNumber="666666", CreditRating=30,
Name="Duwamish Golf" },
new Vendor(){AccountNumber="777777", CreditRating=65,
Name="Fabrikam Sun Sports" },
new Vendor(){AccountNumber="888888", CreditRating=40,
Name="Contoso Lacross" },
new Vendor(){AccountNumber="999999", CreditRating=30,
Name="Duwamish Team Sports" },
};
}
#endregion

#region Vendors Data Load
HttpWebRequest httpWebRequest;
private void LoadData()
{
httpWebRequest =
HttpWebRequest.CreateHttp("http://localhost:9191/AdventureWorksRestJSON.svc/Vendors");
httpWebRequest.BeginGetResponse(new AsyncCallback(GetVendors), null);
}

//add a reference to System.Servicemodel.web to get DataContractJsonSerializer
void GetVendors(IAsyncResult result)
{
HttpWebResponse response = httpWebRequest.EndGetResponse(result) as HttpWebResponse;
DataContractJsonSerializer ser = new
DataContractJsonSerializer(typeof(ObservableCollection<Vendor>));
_vendors = ser.ReadObject(response.GetResponseStream()) as ObservableCollection<Vendor>;
//Vendors is read-only so cannot set directly
//Must call NotifyPropertyChanged notifications on UI thread
//to update the UI and have data binding work properly
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
base.RaisePropertyChanged("Vendors");
});
}
#endregion

#region Vendors Business Logic

private ObservableCollection<Vendor> _vendors;
public ObservableCollection<Vendor> Vendors
{
get
{
return _vendors;
}
//set
//{
// _vendors = value;
// NotifyPropertyChanged("Vendors");
//}
}

public Vendor GetVendorByAccountNumber(string accountNumber)
{
var vendor = from v in _vendors
where v.AccountNumber == accountNumber
select v;

return vendor.First<Vendor>();
}

public void AddVendor()
{
Vendors.Add(new Vendor()
{
AccountNumber = "111111",
CreditRating = 65,
Name = "Fabrikam Bikes - Added"
});
}

public void RemoveVendor(object vendor)
{
if (null != vendor)
Vendors.Remove((Vendor)vendor);
}
#endregion

#region Commanding
public RelayCommand AddAVendorCommand
{
get;
private set;
}

public RelayCommand<Vendor> RemoveAVendorCommand
{
get;
private set;
}
#endregion
}
}


In the next couple of sections, I cover additional features of the MVVM Light Toolkit.

3.7. MVVM Light Messenger Class

The Messenger class provides a means to communicate within an application in a decoupled way. Classes can register to receive messages of different types. The message can be anything from simple values to complex objects. Likewise, messages can specify a target type that should receive the message for fined tuned control.

MVVM Light includes multiple message classes. The following is a list of possible messages from the docs:

  • MessageBase: A simple message class, carrying optional information about the message's sender.

  • GenericMessage<T>: A simple message with a Content property of type T.

  • NotificationMessage: Used to send a notification (as a string) to a recipient. For example, define notifications as constants in a Notifications class, and then send Notifications. Save to recipients.

  • NotificationMessage<T>: Same as the previous, but with a generic Content property. It can be used to pass a parameter to the recipient together with the notification.

  • NotificationMessageAction: Sends a notification to a recipient and allows the recipient to call the sender back.

  • NotificationMessageAction<T>: Sends a notification to a recipient and allows the recipient to call the sender back with a generic parameter.

  • DialogMessage: Used to request that a recipient (typically a View) displays a dialog, and passes the result back to the caller (using a callback). The recipient can choose how to display the dialog, either with a standard MessageBox, with a custom popup, or something similar.

  • PropertyChangedMessage<T>: Used to broadcast that a property changed in the message sender. Fulfills the same purpose as the PropertyChanged event, but in a decoupled manner.

The Messenger class provides a powerful means to pass data and notifications between application layers and within ViewModels in a decoupled way.

Other -----------------
- Developing for Windows Phone and Xbox Live : Graphics Performance
- Developing for Windows Phone and Xbox Live : General Performance
- Windows Phone 7 : Media Management - Taking a Photo from Your Phone Camera
- Windows Phone 7 : Sensors - Indicating the User's Position via Coordinates
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 3)
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 2) - Creating the Content Processor
- Developing for Windows Phone and Xbox Live : Custom Avatar Animations (part 1) - Building the Custom Animation Type
- Windows Phone 7 : Sensors - Displaying Sunset and Sunrise
- Windows Phone 7 : Sensors - Indicating the User's Position?
- Windows Phone 7 : Sensors - Creating a Seismograph
- Developing for Windows Phone and Xbox Live : Avatars Using Render Targets
- Developing for Windows Phone and Xbox Live : Interacting with Objects
- Windows Phone 7 : Resetting a form by shaking the phone!
- Developing for Windows Phone and Xbox Live : Blending Between Animations
- Managing Gestures from the Silverlight for Windows Phone 7 Toolkit
- Windows Phone 7 : Handling Gestures in a Graphical Context Such as a Game Menu
- Developing for Windows Phone and Xbox Live : Modifying Avatar Lighting & Playing Multiple Animations
- Windows Phone 7 : Adding Gestures Management to Click-less Silverlight Controls
- Managing Gestures in a Silverlight Windows Phone 7 Application
- Developing for Windows Phone and Xbox Live : Introduction to Avatars (part 2) - Loading Avatar Animations with AvatarAnimation & Drawing the Avatar Using AvatarRenderer
 
 
Most view of day
- Windows Server 2008 R2 high-availability and recovery features : Installing and Administering Failover Clustering (part 4) - Verifying cluster configuration using the Cluster Validation Wizard
- Communicating with Internet Email : Filtering Incoming Messages - Blocking Senders, Creating a Mail Rule
- SQL Server 2008 R2 : Performance Monitoring Tools (part 12) - Viewing Data Collector Set Results in Performance Monitor
- Managing SharePoint 2010 with Windows PowerShell : Managing SharePoint 2010 Sites (part 1)
- Sharepoint 2013 : Client-side Programming - Working with the REST API (part 3)
- Client Access to Exchange Server 2007 : Getting the Most Out of the Microsoft Outlook Client - Understanding RPC Over HTTPS in Outlook 2007
- Microsoft Project 2010 : Accounting for Project Costs - Comparing Actual Cost and Work Values with the Project Budget
- Securing the Workstation : Applying the Castle Defense System (part 1) - Protecting information, Working with protection
- Sharepoint 2013 : Managing Security - See What Permissions Are Set (part 1) - Check Permissions on Files and List Items
- Windows Phone 8 : Configuring Basic Device Settings - Backing Up Your Phone (part 3) - Backing Up Text Messages
Top 10
- Microsoft Exchange Server 2013 : Working with cmdlets (part 2) - Understanding cmdlet errors, Using cmdlet aliases
- Microsoft Exchange Server 2013 : Working with cmdlets (part 1) - Using Windows PowerShell cmdlets, Using cmdlet parameters
- Microsoft Exchange Server 2013 : Using Windows PowerShell (part 2) - Running and using cmdlets, Running and using other commands and utilities
- Microsoft Exchange Server 2013 : Using Windows PowerShell (part 1) - Running and using Windows PowerShell
- Troubleshooting Stop Messages : Being Prepared for Stop Errors - Prevent System Restarts After a Stop Error
- Troubleshooting Stop Messages : Memory Dump Files (part 3) - Using Memory Dump Files to Analyze Stop Errors - WinDbg Debugger
- Troubleshooting Stop Messages : Memory Dump Files (part 2) - Using Memory Dump Files to Analyze Stop Errors - Using Problem Reports And Solutions
- Troubleshooting Stop Messages : Memory Dump Files (part 1) - Configuring Small Memory Dump Files, Configuring Kernel Memory Dump Files
- Troubleshooting Stop Messages : Stop Message Overview - Identifying the Stop Error, Finding Troubleshooting Information
- Deploying IPv6 : Planning for IPv6 Migration - Understanding ISATAP, Migrating an Intranet to IPv6
 
Windows XP
Windows Vista
Windows 7
Windows Azure
Windows Server
Windows Phone
2015 Camaro