Wednesday, March 17, 2010

ViewModel binding with the Managed Extensibility Framework

This is just a short post to share an "ah-hah" moment I experienced building out a large Managed Extensibility Framework (MEF) Silverlight application. Traditionally, I've wired in the view models using the code behind. I'm not one of those who believes code behind is evil no matter what, so I haven't taken issue with something like this:

[Import] 
public MainViewModel ViewModel 
{
   get { return LayoutRoot.DataContext as MainViewModel; }
   set { LayoutRoot.DataContext = value; }
}

This simply imports the view model that I need and then passes it along to the data context. The issue with XAML is that it doesn't allow me to pass in constructor information and always creates a "new" instance, so if the view model is part of a larger dependency chain, I'd get a new instance in every page that needed a copy.

This is an example of when having design patterns as a part of your vocabulary can improve your ability to solve problems. Obviously, it wasn't part of my vocabulary or I wouldn't have missed it! There is a nice pattern that neatly addresses the concern of having a view model wired in by MEF that allows for attaching it in XAML without having to use code behind. Again, I'm not opposed to code behind, but any time I can abstract the behavior further and do it in a natural way without jumping through hoops just to make it work is something I'll consider.

Enter the Service Locator design pattern. The challenge is that you have components that are based on service contracts and the concrete implementation not known at design time. In our case, the "service" is the view model, and it's not concrete yet because we haven't composed the parts.

Let's create our service locator for view models. In this example, I only have one, but in the application I am working on there are several, and they all get exposed via the service locator:

public class ViewModelLocator 
{
   public ViewModelLocator()
   {
      CompositionInitializer.SatisfyImports(this);
   }
  
   [Import]
   public MainViewModel MainViewModelInstance { get; set; }
}

It's that simple. The class itself composes the view model for us, then provides a property we can use to access the view model. Because we are importing via MEF, we can control the lifetime for the view model and any other attributes we need to compose it properly. Now, we can wire in the view model with no code behind. The XAML looks like this:

<UserControl ... xmlns:vm="clr-namespace:ViewModel">
<UserControl.Resources>
   <vm:ViewModelLocator x:Key="VMLocator"/>
</UserControl.Resources>
<Grid
   x:Name="LayoutRoot"
   DataContext={Binding Source={StaticResource VMLocator},Path=MainViewModelInstance}">
   ...
</Grid>

There you have it ... a simple, clean way to bind the view model to the view without code behind and still allow MEF to compose all of the parts for you.

Jeremy Likness