Silverlight Blendability with MVVM

This post is to summarise my investigations on how to wire up the View in Silverlight with the ViewModel using the MVVM pattern, and still have Blendability – the name given to being able to see test data in your Blend or Cider XAML view at design time.

There are many different approaches seen in various MVVM samples and videos. Having been confused by all the different approaches I decided to boil down the essentials into this post.

Brief Outline

The MVVM pattern separates the View from the form behaviour and data interaction. This post won’t discuss the ViewModel approach – if you are not familiar with MVVM you can check out these resources:

Instead I want to look at the way in which a ViewModel is connected or wired into the application and into the view.

Part 1 – Data Source Abstraction

We could just create a ViewModel that uses the WebService to get the data, and bind this directly to our View.

View –> ViewModel –> WebService (for data) –> Model

This approach has two problems:

  • It prevents us from unit testing the ViewModel code. Unless the WebService runs somewhere and can be accessed from the unit test code [very tricky to do!] you cannot run a unit test on a ViewModel.
  • The ViewModel would not work at design time, since the View isn’t running in a browser context, so isn’t able to call the webservice. We would have no Blendability.

So we want an MVVM technique that will support runtime, design-time and unit-test data sources. The solution to this is to introduce an abstraction layer using either an interface or base class that defines the methods used to retrieve the data, e.g. GetCustomers, etc., but it does not actually have the implementation.

I will assume here we define an interface but the base class approach is just as good: all you need is something that is interchangeable for live/design/test.

 

Part 2 – Data Source Selection

Having abstracted the data source in the ViewModel we now have the system:

View –> ViewModel –> AbstractDataSourceInterface –> Model

So this is much better,:we now have a ViewModel that doesn’t go directly to a data source, but has an abstract interface for getting the data.

The interface is a bit complicated. We can’t have a simple GetCustomers() method that returns a list of customers – we have to support the asynchronous nature of webservice calls and Silverlight calls to such services have to be asynchronous.

This is where the new C#/VB Async CTP comes to our rescue. We can actually code a method in a single call which returns the data, e.g.

public Task<List<Customer>> GetCustomers()
{ /* code here */ }

So the next problem is how to select the data source? The simplest solution is to provide a data source in the constructor. Let’s say ICustomerService is our data interface:

public class CustomerViewModel
{
public CustomerViewModel(ICustomerService service) {…}

This looks great. One small issue: we cannot reference the CustomerViewModel directly as a static resource in Silverlight as it would need a parameterless constructor.

There are two ways around this.

One is to also create a parameterless constructor. This would have to check whether the code was running in the designer or in live, and setting the data source. This means that the constructor that takes a data source would only be used for the unit test case.

Although this technique works it may cause problems: firstly you’ve now limited the control to operate with only two possible data sources (live or design). In the majority of cases this is probably not an issue, but it is worth knowing about.

Approach two is to have a factory or static method that instantiates the ViewModel for us. Some people prefer to use IoC or MEF for such. You can read more on these in the next section.

Part 3 – Linking the View to the ViewModel

The final step, having defined the ViewModel is to bind it to the view. This means creating an instance of the ViewModel (or getting an existing instance, when a ViewModel is shared).

Many examples of data binding set the DataContext in code. This is fine and will work when the code is executed. In design mode however the code behind the form does not get executed so it would not bind. Hence, no design-time data.

To get design time data, there are two main techniques.

A) Static Resource Method

A simple way to set the data context in a View is to use a static resource in the page. This approach will work only if the ViewModel has a parameterless constructor, since the designer can only create static resources where this is the case.

There are three steps to doing this

1. Import the namespace containing the ViewModel in the control header so you can access it

xmlns:local="clr-namespace:MyApplication.CustomerViewModels"

2. Create a static resource of the ViewModel (usually in the UserControl resources section). The Key is how it’s referred to in the control.

<UserControl.Resources>
<local:CustomerViewModel x:Key="vm" />
</UserControl.Resources>

3. Set the DataContext using a binding, with the Source being the StaticResource. Often the ViewModel is the DataContext for the LayoutRoot control.

<Grid x:Name="LayoutRoot" Background="White" DataContext="{BindingSource={StaticResource vm}}">

The only drawback to doing this is that the ViewModel cannot be shared or retained in memory, since a new one is created each time a control instance is created.

B) Service Locator Method

The second approach is to have a service locator or factory class which creates and manages instances. John Papa has a good article on how these work.

Rather than creating an instance itself, the locator method returns the instance, which is created once and then shared.

The simplest way to implement such a pattern is to create a static resource in the application (rather than the control) for the ViewModelLocator, and then use a property of the ViewModelLocator as

Advertisements