Home > View Post

Template/View selection with MEF in Silverlight

One of my favourite features in WPF is the ability to have it automagically wire up a databound object (often a ViewModel) to a particular DataTemplate. A lot of people really miss this feature when using Silverlight as it's still missing from the latest version (Silverlight 4 at the time of writing).

Folk have blogged a number of bespoke solutions, including one by my colleague Rob Garfoot: Flexible Data Template Support in Silverlight.

I was recently working on a project where I was keen to leverage similar functionality to help me use a ViewModel to manage navigation. It's a relatively common approach in my experience; we have a daddy ViewModel, maybe called MainViewModel that has a property of type object (or maybe BaseViewModel depending how you roll):

public class MainViewModel : INotifyPropertyChanged
    // simplified, needs INPC implementation
    public object CurrentViewModel { get; set; }

The MainViewModel can then act as a 'Controller' as it swaps out different CurrentViewModel etc. I'm sure you can see how you would vary this particular implementation to do a bunch of different things.

With MEF being my new muse I wanted to explore how I could leverage MEF to help me do this in an elegant way. There's a lot of talk of Convention over Configuration lately and in particular, it's application to the MVVM pattern in how View's get hooked up to ViewModel's. The typical convention being WibbleView would automagically get mapped to WibbleViewModel, for example.

Call me old fashioned but I'm just not comfortable with this approach yet. I've always preferred stuff to be explicit and for me, this is a keen violation of that trend. Having said that, I also prefer stuff that's simple so a huge XML file containing aliases and a mapping table doesn't appeal to me either.

Anyway, with MEF rushing to my rescue, here's the experimental solution I came up with.

1. Create a new control based on the ContentControl. 2. Bind the ContentControl to the CurrentViewModel property. 3. Override the OnContentChanged method inside the content control to 'intercept' the content and, where appropriate, choose an appropriate View for this content. 4. Decorate my views with some instruction that indicates which ViewModel it should apply to. 5. Discover these views at runtime.

Points 4 and 5 are where MEF really helps out. I often think of MEF being an unusual technology as he doesn't really change the design pattern, he just makes the implementation so much easier!

This implementation uses some of the more advanced features of MEF including the ExportFactory type and custom MetadataAttributes - I won't go into the nitty details here, just share the key ideas and the code with you.

What we end up with...

This is the resulting usage pattern.

In the 'main' view we bind to the MainViewModel using our new ViewModelContentControl which is where most of the magic happens:

    Content="{Binding CurrentViewModel}" />

Then each ViewModel is attributed with a ViewModelTemplate attribute in the code-behind. So the 'OneView' would look like this:

public partial class OneView : UserControl
    public OneView()

And the associated OneViewModel ViewModel would be a simple, err, ViewModel. No need for anything funky there (which feels right to me).

Job Done. Here's what a ridiculously simple usage might look like:

Most of the magic happens inside that ViewModelContentControl. It exposes an Import property that's looking for a list of FrameworkElements (the views).

public ObservableCollection<ExportFactory<FrameworkElement, IViewModelTemplateMetadata>> ElementFactories { get; set; }

At runtime, called from inside OnContentChanged we scan these Imports looking for a matching template:

private object FindContainer(object content)
    Type contentType = content.GetType();

    if (ElementFactories == null || ElementFactories.Count == 0)
        return content;

    var factory = ElementFactories.SingleOrDefault(ef => ef.Metadata.ViewModelType == contentType);

    if (factory == null)
        return content;

    var fe = factory.CreateExport().Value;
    fe.DataContext = content;
    return fe;

If we find a matching template, we set the content (the ViewModel) to be the DataContext of the newly created View. Cool.

I also added some animation when the change happens just because I could.

You likey? Get the source below. As always, this is demoware and your mileage may vary. It's just for fun.

There's a bunch of problems that aren't considered in this simple demonstration; for example, what if multiple views use a single ViewModel (of course, a further layer of abstraction would see this problem off no problem).

Josh Post By Josh Twist
4:32 AM
19 May 2010

» Next Post: Xamlathon unLIVE '10
« Previous Post: Silverlight Security: Securing Your Silverlight Applications

Comments are closed for this post.

Posted by Steve @ 10 Aug 2010 9:19 AM
Why not simply make a convention? I created an convention which adds at application start all views as DataTemplate to my application which have an ViewModel with pattern {viewname}Model beside it.

This works very well and in general its better to have the two beside instead of two different folders.

Posted by Josh @ 23 Aug 2010 4:23 PM
Hi Steve,

Call me old fashioned but I'm still not fully comfortable with convention alone. A good old attribute keeps me happy!

© 2005 - 2022 Josh Twist - All Rights Reserved.