Simple ViewModel Locator for MVVM: The Patients Have Left the Asylum

I’ve been toying with some ideas for MVVM lately. Along the way I have been dragging some friends like Glenn Block and Ward Bell along for the ride. Now, normally its not so bad, but when I get an idea in my head to challenge everything I can be “interesting” to work with :). These guys are great and I highly encourage you all to get your own personal Glenn and Ward bobble head dolls for your home.

But back to MVVM … I’ve been exploring the world of View first again. The idea is simple: the View is created, and it creates the ViewModel in the View’s XAML. Very simple to do when you create the ViewModel as a static resource in the View’s XAMl, however it gets a little more complicated when you want to see the ViewModel bind at design time in Cider or Blend. This is important … I wanted to have the View create itself first, then the ViewModel, and have design time data. One option is to use a ViewModel locator  (using the service locator pattern). This is a fine option and is used by MVVM Light Toolkit (a nice light MVVM framework). The thing I did not like was how much maintenance was involve din the ViewModel locator. So out came my “challenge everything I know” trait.

I started talking to Glenn Block about how to use MEF to make the ViewModel locator completely generic. I wanted to take it out of the picture and make it dead simple. So I enticed Glenn with a few encouraging remarks and we were off to a pair programming spree for a little over an hour. What we came up with is in this blog post, warts and all. The source can be retrieved from the bottom of the post.

Here are the steps (after you reference the DLL with the ViewModelLocator):

  1. Create the ViewModelLocator in App.xaml as a static resource
  2. Export your View Model using the custom attribute (this tells the locator about it using metadata)
  3. Set the DataContext of your View to the ViewModeLocator and bind to the specific ViewModel using an indexer

 

STEP 1: Create the ViewModelLocator in App.xaml as a Static Resource

So what I really wanted was to be able to easily crank up ViewModels. I’ll start with how to use the ViewModelLocator. It is in its own dll so there is no need to touch its code (unless you want to). So here is what I have to do once in my app. This code creates the ViewModelLocator object in the App.xaml.

<Application.Resources>
    <MVVMLib:ViewModelLocator x:Key="VMLocator"/>
    <MVVMLib:IndexerConverter x:Key="VMIndexerConverter" />
</Application.Resources>

Notice I create a converter too. This is only here because there appears to be a known bug in the Silverlight 4 RC when you bind to a string indexer. The bug is that it actually binds 6 times. The workaround is to use a converter. So once this bug is fixed, this converter is no longer needed.

STEP 2: Export Your View Model Using the Custom Attribute

Below I have a ViewModel (a rather boring one) where the only additional thing I do is Export it using a string as the key/identifier. This tells MEF which View Model it is.

[ExportViewModel("MainPageViewModel")]
public class MainPageViewModel : ViewModelBase 
{
    private string _companyName;
    public string CompanyName
    {
        get { return "Contoso, Inc."; }
        set
        {
            _companyName = value;
            FirePropertyChanged("CompanyName");
        }
    }
 
}

STEP 3: Set the DataContext of your View to the ViewModeLocator and Bind to the Specific ViewModel Using an Indexer

The final step is to set the DataContext of the View to the ViewModelLocator instance and tell it which View Model to use.

DataContext="{Binding 
    Source={StaticResource VMLocator}, 
    Converter={StaticResource VMIndexerConverter}, 
    ConverterParameter=MainPageViewModel}"

Again, this code is slightly different than I’d like because of the possible string indexer binding bug I mentioned earlier. If the binding worked once (not 6 times) then I could have just done this and skipped the converter. The code sample below shows how much simpler it would be to bind to the indexer directly without the converter. Much much nicer.

DataContext="{Binding 
    Source={StaticResource VMLocator}, 
    Path=Find[MainPageViewModel]}"

So that is it! You can still use Blend in a number of ways. One of which is to use the code like this if you prefer.

d:DataContext="{d:DesignInstance 
    Type=VMLocatorDemo:MainPageViewModelDesign, 
    IsDesignTimeCreatable=True}"

What if I Have Lots of Views and ViewModels?

Good question. This is where I really like it. You just repeat steps 2 and 3. Export the ViewModel, and bind to it from the View. Very very simple.

Anything Else I Need to Know?

If you want to create a new instance of a ViewModel each time, you can do that.You can also create 1 ViewModel singleton and reuse it each time. With the code we wrote you just call the Find indexer for creating instances each time or call the FindShared for the Singleton.

Where is the Code?

You can grab the source for the ViewModelLocator that Glenn and I wrote tonight right here. I also included a sample demo so you can try it out. The intent is to see what is possible and how we can make the locator very easy and maintainable. We will be refactoring this and seeing if we can make it simpler or cleaner. We will also be looking into the string indexer options so we can get rid of the workaround with the converter.

But please take this code and leave your comments on it. We are eager to hear your thoughts!