Silverlight MVVM + dependency injection

Update 2009/03/04: Here are 2 links that may help you with your understanding of MVVM

first 2 videos about implementing MVVM, one in WPF and the other in Silverlight http://weblogs.asp.net/craigshoemaker/archive/2009/02/26/hands-on-model-view-viewmodel-mvvm-for-silverlight-and-wpf.aspx and an article that delves into creating a fully functional app http://www.codeproject.com/KB/smart/Sonic.aspx

I attended a talk Jonas did last week on MVVM and it got me excited enough to explore it myself. His blog has a wealth of information, and it has all been summarised in his latest post. By reading his blog and many other bits and pieces around the web i was able to get this working myself. I’m going to put it all together into one blog post to give a complete look at how to incorporate all of it together. I aim to show how it all hooks in together and not just show a diagram saying that you should split things out.

Presumably you already have an idea of what Model/View/ViewModel is. The background and reasoning behind it is beyond the scope of this article, but here is an article I recommend. And also all of Jonas’s articles. But basically MVVM + Dependency injection allows your applications to be loosely coupled with the GREAT advantage that all of your code can now be unit tested, since the UI is just databinding to your classes.

Architecture

MVVM

The application is going to look like the diagram. It will be split up into the usual Model/View/ViewModel components, but I aim to show all of the supporting code that is needed to get it all working.

The application will be a HR system which just show a list of all the employees. I am just going to implement this single method to try and keep this example as simple as possible

View

Page.Xaml. This is a normal silverlight XAML page with our UI components. However we are going to get it databind to our ViewModel and have it auto refresh itself when the data changes rather than doing this by ourselves manually

Model

This is our underlying data that we want to our application to somehow show. Here it will be our HR system. This worries about how to retrieve/add/modify the data.

ViewModel

This sits between the Model and the View. The point of this is to give a specific ‘View and state’ of our Model  to the view. The View just wants to display data, it doesn’t want to care about how to retrieve things, so the ViewModel handles all of this for us.

Dependency Injection

Rather than hard coding all of the connections between the Model, View & ViewModel. We can use ‘Dependency Injection’ to insert the class for us. This allows us to be able to use other classes when unit testing, or at designtime vs. runtime.

We will use DI to connect the ViewModel to the View(Page). And to connect the Model to our ViewModel. I have created a few versions of the model which we can swap around.

Implementation

Employee

Just a simple class that contains age/name/etc.

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Occupation { get; set; }
    public int Age { get; set; }
}

IHRSystem

We are programming against an interface, this way we can swap the class that does the actual work.

public interface IHRSystem
{
    ObservableCollection<Employee> GetEmployees();
}

This is going to be implemented 3 times as:

  • HRSystemSQL
  • HRSystemMemory
  • MockHRSystem

They all should be self explanatory. But HRSystemSQL retrieves the results from a SQL database. HRSystemMemory gets it from a list in memory. And MockHRSystem is used for sending through precanned data for testing purposes or for our designtime experience.

So we can see that we can switch out the actual implementation of our Model and it will still work as we are programming against an interface. We don’t care if we are retriving from a database, or from a webservice, etc.

Here is the implementation of the mock class that will be used to display data for us at design time in blend, or when we are running unit tests

public class MockHRSystem : IHRSystem
{
    public ObservableCollection<Employee> GetEmployees()
    {
        return new ObservableCollection<Employee>{ new Employee{Id=1, Name="John Smith", Age=31, Occupation="IT Manager"},
            new Employee{Id=2, Name="Jane Smith", Age=43, Occupation="Help desk"}};
    }
}

PageViewModel

The this is the ‘ViewModel’ for Page.Xaml, hence the name PageViewModel… This needs to do 3 things.

  1. Expose as properties the data that we want the view to databind against
  2. Have the constructor setup to pass in the implementation of the model class that we want to use (by using dependency injection)
  3. make our properties raise PropertyChanged events to tell the View to update when the data changes
public class PageViewModel : INotifyPropertyChanged
{
    private IHRSystem hrSystem; //Where do we get the data from?

    private ObservableCollection<Employee> employees;   //The data we are holding so that the View can be databound
    public ObservableCollection<Employee> Employees     //It is exposed as a property that fires property changed events
    {
        get{return employees;}
        set
        {
            if (value != employees)
            {
                employees = value;
                RaisePropertyChanged("Employees");
            }
        }
    }

    //Using dependency injection to tell us which implementation of the model we are using
    [Inject]
    public PageViewModel(IHRSystem HRSystem)
    {
        hrSystem = HRSystem;
        employees = hrSystem.GetEmployees();
    }

    public event PropertyChangedEventHandler PropertyChanged;
    //Raise events when our data changes so that the view knows to update itself.
    protected void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

MVVMTestModule

We’re done with most of the code required to actually get the data that we want. Now we need to say how to wire it all up, since we haven’t used any code in the code behind files. First we are going to define how the dependency injection is setup in the application. I am using Ninject to handle this, so it will look like this

public class MVVMTestModule : StandardModule
{

    public override void Load()
    {
        bool isBrowser = HtmlPage.IsEnabled;
        bool isBlend = !isBrowser;

        Bind<IHRSystem>().To<HRSystemMemory>().OnlyIf(c => (isBrowser));
        Bind<IHRSystem>().To<MockHRSystem>().OnlyIf(c => (isBlend));

        Bind<PageViewModel>().ToSelf();
    }
}

To simplify things, I’m avoiding using the SQL implementation and will just be holding a list in memory by using the HRSystemMemory implementation.

ServiceLocator

We are using dependency injection, but we still need to tell silverlight once where to look for the class that will handle all of the dependency injection niceness for us. Jonas does a great job explaining why we have one, but here is what we will use in this example

Silverlight is now able to get Ninject to pass to us our new PageViewModelClass that has been setup with the correct Model being passed in (Mock or a proper implementation) based on if we are in blend or running the app.

public class ServiceLocator
{
    private static IKernel kernel;

    public PageViewModel PageViewModel
    {
        get{return kernel.Get<PageViewModel>();}
    }

    public static T Get<T>()
    {
        return kernel.Get<T>();
    }

    static ServiceLocator()
    {
        if (kernel == null)
        {
            kernel = new StandardKernel(new MVVMTestModule());
        }
    }
}

App.Xaml

The ServiceLocator class we just created, we need to set it up as an application wide resource, so that any part of our application can access the DI framework.

<Application ....
         xmlns:MVVMTest="clr-namespace:SilverlightMVVMTest1">
    <Application.Resources>
        <MVVMTest:ServiceLocator x:Key="serviceLocator"/>
    </Application.Resources>

Page.Xaml

Finally we are at the end!

Create our View using a ListBox to display our List of data. Format it how we like with a datatemplate. but now we bind our entire view to our PageViewModel class and have the UI display it for us.

<UserControl ...
    DataContext="{Binding Path=PageViewModel, Source={StaticResource serviceLocator}}">
    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox ItemsSource="{Binding Path=Employees}">

Further work

This was just a quick rundown on how to create a complete MVVM application that is fully testable. New concepts weren’t meant to be introduced here, just showing how it all works together. As shown in the initial diagram, converters can be used to do more complex mappings between the ViewModel and the View. We still haven’t said how to send events through (like clicking on a button to refresh the data, or adding new data). If there is enough interest, I’ll tie that into this example in another blog post

By David Burela

About these ads

12 Responses to “Silverlight MVVM + dependency injection”

  1. JZ Says:

    Nice post. Have been looking for good examples of MVVM that aren’t to complicated or leaves 50% to your imagination. If you wanna throw in some examples of how you use converters, by all means go right ahead :-). Some examples of how to communicate between usercontrols using MMVM would be nice since I haven’t found anything useful i.e. can we communicate between usercontrols and still keep our codebehid clean?

    /J

  2. Jordan Says:

    Mate, sweet article. How sweet is M-V-VM in SL???

    Glad to see you are enjoying SL!!

  3. Jordan Says:

    @JZ Check out the command pattern in SL Extensions project on CodePlex. Jonas has some examples of using command pattern to communicate between stuff from XAML!

    http://jonas.follesoe.no/YouCardRevisitedImplementingTheViewModelPattern.aspx <- stuff on command pattern about half way through.

  4. Jonas Follesø Says:

    Hi David,

    Great post connecting all the dots of the MVVM puzzle nicely! Silverlight and WPF are still young technologies, and best practices is yet to be discovered. So early thinking and exploring like this is the way forward.

    Looking forward to catch up at the Silverlight UG meeting in Nov if you’re going :)

    Cheers,
    Jonas

  5. Rich Says:

    This post is exactly what I’ve been looking for…a simple example to build off of. I can’t wait for your next post on converters and events.

  6. Bob Baker Says:

    Thanks for a great article. Would love to see some more on events in the context of MVVM.

  7. Bob Baker Says:

    I wonder if you would take a moment and comment on a screenshot of a solution I built based on your article:

    http://www.microapplications.com/SilverlightMVVMTest1.png

    This builds and runs just fine after I adjusted all of the namespaces.

    I’m new to Silverlight, and new to MVVM, so my thinking is that Model ends up being in a class library project and a second Silverlight Class Library project with classes linked to the main class library for the ViewModel stuff to reference, and the HSSystem stuff ends up being in the Web project (or a separate project) as a web service. Am I getting warm ? Where should the NInject related stuff (MVVMTestModule and ServiceLocator) go? Thanks.

  8. Klaus Says:

    Hi David, can you provide the complete source code / visual studio solution, for us dummies.

    Thanks Klaus

  9. Jakob Lanstorp Says:

    Very nice article. If you map the fields of the Employee class with a template, you get the data shown in the listbox and not just the name of the object.

  10. Livia Rollison Says:

    This is very interesting, You are a very skilled blogger. I have joined your feed and look forward to seeking more of your wonderful post. Also, I have shared your site in my social networks!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: