Wire up your ViewModels using a Service Locator

No MVVM solution is complete without having the DataContext bound to a ViewModel, but this wouldn’t be a fun development community if there were not some disagreement on the specifics of how to achieve certain goals. I will be recommending how I like to wire up ViewModels. There are other ways of doing this, but I will explain some of the reasons I use this method.

You can start by building a View that needs to have certain traits in its ViewModel and then create a well-tested ViewModel separately. This ViewModel should have all of the properties and data required by the View. Make sure you also have any commands or other functionality the View will require. It is then your job to make the connection between these two objects. The way I like doing this is by using a Service Locator to give my View the ViewModel it needs. This also gives me a good centralized location where I can make sure that my ViewModels are wired up the way I need them to be.

To create our service model we are going to need to create a class which has methods returning the ViewModels we are using in our Views. We should have one getter per ViewModel to be requested. I tend to use names matching the name of the ViewModel for the getters. The service locator will look a bit like this when you’re done. (You can also use an IoC container in the service locator, which is what I do in all of my production code. In that case you would just use the IoC container rather than instantiating the object as is done in this example.)

Code Listing 1 – The Service Locator:

public class ServiceLocator
{
public AwesomeViewModel AwesomeViewModel
{
get { return new AwesomeViewModel(); }
}
}

Notice that I can pass in any parameters needed for the ViewModel constructor so my ViewModel can depend on abstractions. The service locator can be more complicated than this if other work needs to be done to create these ViewModels. An example of such a situation is if I have shared dependencies or I am using an IoC container to create my objects.

Now that we have our code written to get us our ViewModel object we need to make this class available to all of our Views. This can be achieved by creating an instance of one of these as a static resource in our App.xaml file. Static resources defined here are easily accessible.

Code Listing 2 – Declaring the Service Locator as a Resource in App.xaml:

<Application x:Class="Awesome.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Awesome="clr-namespace:Awesome"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Awesome:ServiceLocator x:Key="SvcLoc" />
</Application.Resources>
</Application>

The key that we have assigned to this resource is how we will reference it when we access it later. In other parts of our code we will just specify in our bindings that we are accessing this static resource and calling its properties to get the data that we need. In fact this is exactly how we can get our ViewModel into our View. When we bind our DataContext we will tell it that our source is going to be this ServiceLocator instance and that we are binding our DataContext to a specified path, which is a property of the ServiceLocator.

The binding can be done either of these two ways. I don’t really prefer one or the other. These are effectively the same things, so it really comes down to preference.

Code Listing 3 – Binding the DataContext to the ServiceLocator:

<Window.DataContext>
<Binding Path="AwesomeViewModel"
Source="{StaticResource SvcLoc}"/>
</Window.DataContext>

Code Listing 4 – Binding the DataContext to the ServiceLocator Inline:

DataContext="{Binding AwesomeViewModel, Source={StaticResource SvcLoc}}"

Now we can access this DataContext throughout our View by just binding to different paths on it.

I like this approach because it keeps the binding in the XAML and not in the code. It is also nice because we are able to bind easily to properties and have these properties do dependency injection for our ViewModels. The centralization of all of this ViewModel creation is also very convenient. We are able to visit this one class to make adjustments for how the entire application handles its ViewModel initialization.

Comments