Welcome to EMC Consulting Blogs Sign in | Join | Help

David Wynne's Blog

WPF, Model-View-ViewModel (MVVM), MEF and other Acronyms

IMG00041

Stuart Harris and I have been working on a new WPF project of late, one aspect of which needed to support a plug-in type model so we decided to try out MEF (Managed Extensibility Framework) and were both pretty impressed – as Stu has been evangelising.  We were also keen to try and bring some of the MVVM goodness we’d been using on our last few Silverlight projects into the mix and thought we’d try and come up with a pattern of using MEF as the IoC container aspect in WPF.

It’s still early days on the project so I’m sure we haven’t hit all the issues that may be inherent in our current implementation, but I thought I’d throw together a quick sample of the approach we’re taking right now.

The Plan

In Silverlight we’d been using Ninject as the IoC container and the basic execution flow goes something like this:

  • View (User Control XAML) – added to Page.xaml (the application Root Visual) and so instantiated by the application.
  • View’s Data Context set to a ViewModel via a specific property on the ServiceLocater class
  • ServiceLocater class setup as a Resource in App.xaml (so instantiated for us and available to everything)
  • ServiceLocater exposes a property per ViewModel, its get accessor makes a request to Ninject’s kernel for the ViewModel which then deals with instantiation and injection of that ViewModel and related dependencies.

We wanted to have more or less the same execution, but with MEF pulling the strings.  We wanted to be able to set the DataContext of a View in its XAML so Resharper can resolve it and give you IntelliSense to the associated ViewModel in XAML which is really handy.  We were also quite keen to try and get rid of the ServiceLocater class if possible, which is basically continually growing boiler plate code.

Introducing MEF

MEF is all about composable parts, which come in two flavours; imports and exports.  An export is something you want to share and an import is the point at which you want to inject an export.  So in the MVVM pattern your ViewModel is an export which get’s imported into the View.  Any dependencies the ViewModel may have are also exports and imported, or injected, into the ViewModel.

Like other IoC containers MEF needs to a way of knowing about what exports it has at its disposal and what imports require “composing” (or matching up to available exports).  In MEF this is the CompositionContainer which contains a Catalog of exports – this is the doorway to some of MEF’s cooler features (like monitoring directories for updated/new exports etc) but in our case we’re keeping it simple and will build the catalog using the executing assembly.  (i.e. MEF will examine the executing assembly for anything marked as an Export and add it to the catalog).

MEF will take care of instantiating classes marked for Export for us and managing their dependencies and lifetime.  But it won’t magically do the same for classes that only has imports, such as our View.  So we need to not only instantiate the View ourselves but also tell MEF that it now exists and wants its imports satisfying (“composing”).

Here’s the basic order of execution we want to achieve, we’ll step through each stage with code samples shortly.

  • Application Starts
  • Create the CompositionContainer with a catalog of exports based on the executing assembly
  • Main Window gets instantiated by App.xaml
  • Main Window instantiates the User Controls contained in it.
  • In the Constructor of each User Control, it adds itself to the Composition Container via the Compose method.
  • In doing so, its Imports are satisfied – if this includes Exports (such as a ViewModel) that have not yet been instantiated, they get instantiated by MEF and its imports satisfied and so on.

Some Code

I’ve refactored my previous feed reader example built using Silverlight and Ninject to WPF using MEF.  It essentially grabs the latest items from a Digg RSS feed and displays them in a list box.  Check out the previous post for more info.

App Start
First we create the CompositionContainer, adding a catalog of exports found in the executing assembly.  We’re also exposing the Composition Container as an internal static so views can add themselves later.

public partial class App
{
    private static CompositionContainer container;

    internal static CompositionContainer Container
    {
        get
        {
            return container;
        }
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        container = new CompositionContainer(catalog);
    }
}

The View
FeedReaderView.xaml is added to the MainWindow and so get’s instantiated for us.  In the code behind of the view we want to create a ViewModel property to which we will set the view’s DataContext.  To get the ViewModel to import into this property we need to ask the Composition Container to compose our view.  This is easily accomplished by hitting the static internal Container property we exposed from App.xaml.cs.  Since this is something every view is going to have to do in the same way, we created an extension method to keep the view’s code behind succinct.

public static class UserControlExtensions
{
    public static void RegisterWithContainer(this UserControl view)
    {
        try
        {
            var batch = new CompositionBatch();
            batch.AddPart(view);
            App.Container.Compose(batch);
        }
        catch (CompositionException compositionException)
        {
            MessageBox.Show(compositionException.ToString());
        }
    }
}


public partial class FeedReaderView
{
    public FeedReaderView()
    {
        this.RegisterWithContainer();
        this.InitializeComponent();
    }

    [Import]
    public FeedReaderViewModel ViewModel
    {
        get;
        private set;
    }
}

Notice that the call to RegisterWithContainer (the extension method) takes place before InitializeComponent, it’s important the ViewModel property is assigned a value before you try and use it as the DataContext.  If it’s null when the DataContext is set – data binding kinda blows up.

Finally we can now use our ViewModel as the DataContext – whilst you could set this in the code-behind, if you’re using Resharper then you definitely want to do this in the XAML.  Resharper will resolve the bindings and give you intellisense through to your ViewModel properties in the text editor, which is really rather funky.

<UserControl x:Class="MVVMWithMEF.UI.Views.FeedReaderView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Name="view"
             DataContext="{Binding ElementName=view, Path=ViewModel}">
    ...
</UserControl>

The ViewModel
The ViewModel side of things is very simple.  All we need to do is add an Export attribute to the class and in our case inject our feed service via some constructor injection:

[Export]
public class FeedReaderViewModel : BaseViewModel
{
    [ImportingConstructor]
    public FeedReaderViewModel(IFeedService feedService)
    {
        ...
    }

    ...
}

Since we’re injecting (or importing) IFeedService this too needs to be marked up for export.  At this stage we’re going to export an RssFeedService implementation of IFeedService so to get our contracts to match we need to specify the export contract as conforming to the interface.

[Export(typeof(IFeedService))]
public class RssService : IFeedService
{
    ...
}

And that’s all there is to it.  Download the code below, take a look, read Stu’s blog and tell us what you think.

Published 22 May 2009 11:56 by David.Wynne

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

MVVM, WPF, and MEF « WPF design and research said:

June 18, 2009 22:01
 

J Wolthuis said:

Your coding style is very clean, with clear illustrations of async feeds, MEF composing, and implementation of the M-V-VM pattern. I was disappointed to see the XAML of the main window, containing a hardwired View. If the container hardwires its view (and thus knows about it), why force the view to jump thru the "RegisterWithContainer" hoop?

The "RegisterWithContainer" design allows a separation between the container and one-or-more views. How would I design a new view/viewModel, call RegisterWithContainer, and avoid hardwiring containers-to-views?

October 15, 2009 02:02
 

Marlon Grech said:

you should have a look at my library MEFedMVVM. this can be achieved in a simpler way by using the library.

http://mefedmvvm.codeplex.com/

May 26, 2010 00:17
 

David.Wynne said:

@Marlon - hey I'll take a look thanks.  We did this back in May 2009, don't think your project was around then!  Sure things have moved on.  :)

June 11, 2010 12:22

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Personal Edition), by Telligent Systems