MEF CompositionInitializer for WPF

The Managed Extensibility Framework is an amazingly useful addition to the .NET Framework.  I was very excited to see System.ComponentModel.Composition added to the core framework.  Personally, I feel that MEF is one tool I’ve always been missing in my .NET development.

Unfortunately, one perfect scenario for MEF tends to fall short of it’s full potential is in Windows Presentation Foundation development.  In particular, there are many times when the XAML parser constructs objects in WPF development, which makes composition of those parts difficult.  The current release of MEF (Preview Release 9) addresses this for Silverlight developers via System.ComponentModel.Composition.CompositionInitializer.  However, there is no equivalent class for WPF developers.

The CompositionInitializer class provides the means for an object to compose itself.  This is very useful with WPF and Silverlight development, since it allows a View, such as a UserControl, to be generated via the standard XAML parser, and still automatically pull in the appropriate ViewModel in an extensible manner.  Glenn Block has demonstrated the usage for Silverlight in detail, but the same issues apply in WPF.

As an example, let’s take a look at a very simple case.  Take the following XAML for a Window:

<Window x:Class="WpfApplication1.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="220" Width="300">
    <Grid>
        <TextBlock Text="{Binding TheText}" />
    </Grid>
</Window>

This does nothing but create a Window, add a simple TextBlock control, and use it to display the value of our “TheText” property in our DataContext class.  Since this is our main window, WPF will automatically construct and display this Window, so we need to handle constructing the DataContext and setting it ourselves.

We could do this in code or in XAML, but in order to do it directly, we would need to hard code the ViewModel type directly into our XAML code, or we would need to construct the ViewModel class and set it in the code behind.  Both have disadvantages, and the disadvantages grow if we’re using MEF to compose our ViewModel.

Ideally, we’d like to be able to have MEF construct our ViewModel for us.  This way, it can provide any construction requirements for our ViewModel via [ImportingConstructor], and it can handle fully composing the imported properties on our ViewModel.  CompositionInitializer allows this to occur.

We use CompositionInitializer within our View’s constructor, and use it for self-composition of our View.  Using CompositionInitializer, we can modify our code behind to:

public partial class MainView : Window
{
    public MainView()
    {
        InitializeComponent();
        CompositionInitializer.SatisfyImports(this);
    }

    [Import("MainViewModel")]
    public object ViewModel
    {
        get { return this.DataContext; }
        set { this.DataContext = value; }
    }
}

We then can add an Export on our ViewModel class like so:

[Export("MainViewModel")]
public class MainViewModel
{
    public string TheText
    {
        get
        {
            return "Hello World!";
        }
    }
}

MEF will automatically compose our application, decoupling our ViewModel injection to the DataContext of our View until runtime.  When we run this, we’ll see:

MainWindow

There are many other approaches for using MEF to wire up the extensible parts within your application, of course.  However, any time an object is going to be constructed by code outside of your control, CompositionInitializer allows us to continue to use MEF to satisfy the import requirements of that object.

In order to use this from WPF, I’ve ported the code from MEF Preview 9 and Glenn Block’s (now obsolete) PartInitializer port to Windows Presentation Foundation.  There are some subtle changes from the Silverlight port, mainly to handle running in a desktop application context.  The default behavior of my port is to construct an AggregateCatalog containing a DirectoryCatalog set to the location of the entry assembly of the application.  In addition, if an “Extensions” folder exists under the entry assembly’s directory, a second DirectoryCatalog for that folder will be included.  This behavior can be overridden by specifying a CompositionContainer or one or more ComposablePartCatalogs to the System.ComponentModel.Composition.Hosting.CompositionHost static class prior to the first use of CompositionInitializer.

Please download CompositionInitializer and CompositionHost for VS 2010 RC, and contact me with any feedback.

Edit on 3/29:

Glenn Block has since updated his version of CompositionInitializer (and ExportFactory<T>!), and made it available here:

This is a .NET 3.5 solution, and should soon be pushed to CodePlex, and made available on the main MEF site.

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Comments

32 Responses to “MEF CompositionInitializer for WPF”
  1. Wim Coenen says:

    Why not just use WPF data templates?

    For example, look at the sample code for Josh Smith’s introductory article for MVVM (http://msdn.microsoft.com/en-us/magazine/dd419663.aspx). It only explicitly creates and links the main window and main window viewmodel. The rest of the views are never explicitly instantiated anywhere. Only the viewmodels are instantiated, and WPF data templates take care of creating and showing a window based on the data templates defined in MainWindowResources.xaml.

    • Reed says:

      Wim,

      DataTemplates are a fantastic tool, but they work the other way around. In order to use a DataTemplate, you need to work in a ViewModel-first approach, since the DataTemplate will force a content presenter to construct a View for you, given a ViewModel.

      This is a perfectly valid approach in many situations, but it does have some disadvantages at times. It tends to be less “Blendable”, for example.

      Also, if you look closely above – you’ll notice that there is no hard-coded link between the VM and the View. In order to use DataTemplates, some portion of the View has to specify “this View is used for this ViewModel”, which adds a (granted loose) binding between the two classes. This approach lets you have different VM classes in different assemblies, and decide, at runtime, which to load.

      -Reed

      • Wim Coenen says:

        I just stumbled upon your excellent posts about WPF, so I feel a bit silly now about lecturing you about data templates ;-) I had only been following this blog since the Parallelism posts.

        However, I still think the “View first” approach is a bit awkward in a composed UI. Glenn Block recently posted about refactoring “HelloMEF” to use MVVM, and it was actually his diagram that crystallized my opinion: http://blogs.msdn.com/gblock/archive/2010/03/08/building-hellomef-part-v-refactoring-to-viewmodel.aspx

        That diagram looks all wrong to me: viewmodels shouldn’t have to know anything about views. And they don’t have to if you use data templates.

        Then again, I’m still very new to both WPF and MVVM so I’m probably still missing many aspects here, like that “blendability” you talked about.

        • Glenn Block says:

          Hi Wim

          The VIewModel in that sample actually does not have to (assuming you mean the widgets import). The widgetst can implement IWidget and the VM can import that. In this case I started with UserControl for simplicity. When I refactored I overlooked changing that.

          ViewFirst is not about the VM knowing about the View, it’s actaully the reverse. If you look at my sample each View only know’s about IT’s VM not vice versa.

  2. Glenn Block says:

    Great post Reed! Thanks for doing this.

    I went and actually re-ported our CompositionInitializer AND ExportFactory to work for the desktop. Currently it’s on my skydrive here: http://cid-f8b2fd72406fb218.skydrive.live.com/self.aspx/blog/Composition.Initialization.Desktop.zip but I plan to push it to Codeplex.

    You were the catalyst to get me off my butt to do this, so thanks!

    Glenn

    • Reed says:

      Glenn,

      I just added a nice large link at the end of the post to your new version. Thanks for getting it up on your SkyDrive!

      -Reed

  3. Kate says:

    Hello Reed,

    I am learning MEF and I am kind of stuck at one point.

    In my application, I have written 2 extensions 1) Basic Calculation and 2) Customized Calculation, both implementing ICalculate interface. Now what I want to do is that by default my application should use “Basic calculation” extension BUT when the “Customized Calculation” is available, it should use the Customize one…and not the base one.

    Can you please guide me how should i write the import statements to load only one of the available extensions?

    many thanks!

  4. Dipen says:

    Excellent !!!

  5. John Peters says:

    I don’t fully understand this statement “MEF will automatically compose our application, decoupling our ViewModel injection to the DataContext of our View until runtime.”

    The mainview does the import and names the type which in this case is the same name as the viewmodel.

    [Import("MainViewModel")]
    public object ViewModel
    {
    get { return this.DataContext; }
    set { this.DataContext = value; }
    }

    How is this decoupled? Because it’s a type and not an instance?
    Why would there ever be more than one instance and how would the proper one be selected for the binding?

    • Reed says:

      This is decoupled because the type doesn’t need to be known at compile time, only the “string” – in this case, we know were importing some class that will act as a “MainViewModel”, but we don’t need to know anything about it, have a reference to it directly, etc.

      There will only be one instance, as MEF will (by default) provide us a single instance that gets reused.

      • John Peters says:

        Ok Gotcha…MEF using a string in the Import which is magically instanciated into an object based on a class name. This allows swapping classes or even, as MEF does well adding extensions to be used at will.

  6. John Peters says:

    Playing around with this today I see that the design time property page for Bindings doesn’t see the Viewmodel. Ouch! That’s a super nice feature to not have…

    • Reed says:

      This tends to be a problem with all ViewModel first approaches to MVVM, as well. It’s annoying, though I often will have a custom class for design time data when the design experience is important anyways…

      • John Peters says:

        Hi Reed;
        I had been able to readily get viewmodels bindings to show in the designer of view. There are two ways to do it. 1) A static resource of Viewmodel in view or 2) Code behind in View creates view model in CTOR.

        I didn’t realize until now that viewmodel first creates this problem… Hmmmm… I’ll need to get used to the differrences and see how this fits in with MEF injection…I really want the injection stuff.

        • Reed says:

          John,

          The problem is that, for designer support, the VM has to be bound at design time. For injection support, you’re saying you want to late bind this. ViewModel first development tends to favor late binding, as well. As such, both tend to break designer support.

          I typically work around this by just using custom design time data (which has other advantages), at the cost of a bit of extra work to make the design time data. This gives you the best of both worlds, but does have a cost in terms of implementation.

          • John Peters says:

            Ok another ahha moment for me and how late binding foils design time designers. Thanks! Never thought of it that way.

  7. Patrick Mancier says:

    Ok, I am not exactly sure if what Im about to say is relevant, but I got this working without having to use the code in this article. I initially tried using this code and simply could not make it work, it was giving me some kind of error in not being able to find some definition in the container. i spent 1/2 a day on it and simply gave up.

    So I found a guy who was making a call to MefBootstrapper.Container.SatisfyImportsOnce(object). Well, I took this info, and during my call to ConfigureContainer I simply set a static member of my App : Application class to the container, and then call the SatisyImportsOnce in the constructor of my view and my view model . Everything worked perfectly, all my imports on the view began to populate including my reference to the ViewModel itself and then all my view model imports were populated as well. It just seems this approach is so much simplier than incorporating this code presented here. I think what I did appears to have accomplished the same thing, the only ‘bad’ thing I guess is using a static global to reference the container, but I dont even think thats all that bad.

    I am not sure what the difference is between SatifyImports but from the documentation the only ‘disadvantage’ I see is that it is not available for recomposition after using this method. I am not sure for what I am doing its a big deal, but if anyone reading this can comment on any fault in my method please I would love to hear it because I dont know if what I did is exactly right, all I know is it appears to work.

    • Reed says:

      That’s basically the same approach, Patrick. Its just that you’re holding the container in a static variable instead of using the CompositionInitializer.

      • Patrick Mancier says:

        Thanks for the response. I did appy the idea you present of importing the ViewModel in my view code behind and use the IPartImportsSatisfiedNotification interface to set the data context once the view model has been imported. This approach seems to work pretty well.

        There is one thing that might be detrimental to my approach I thought about and that is that if any of the imports change during execution apparently the imports will not be recomposed/updated. From what I am reading that really the main difference between SatisfyImportsOnce and SatistfyImports. I dont know if I need to worry about it for the particular case Im interested in but perhaps others would have an issue with my approach. It is a bit frustrating the Microsoft did not include those composition calls for WPF layer because I am not completely happy with using my technique but I guess until I find some weakness to it Ill be using it. So far the code is working as I would expect it to.

  8. Valery says:

    I’m a WPF developer and I want my application to us MEF to handle my modelviews & views. I realized that the CompositionInitializer.SatisfyImports(this) was not working and that’s when i stumbled until this site. I downloaded the ComponentsModel.Composition.Initialization.Desktop and after adding this dll to my project…it “kind of” worked.

    Basically I was following John Papa’s blog on how to use mvvm light framework and mef.
    http://johnpapa.net/simple-viewmodel-locator-for-mvvm-the-patients-have-left-the-asylum

    For the ViewModelIndexer class when I called CompositionInitializer.SatisfyImports(this); My [ImportMany("ViewModel", AllowRecomposition = true)]
    public IEnumerable<Lazy> ViewModelsLazy { get; set; } variable populated correctly but not my [ImportMany("ViewModel", AllowRecomposition = true)]
    public IEnumerable<ExportFactory> ViewModelsFactories
    {
    get;
    set;
    }

    which utilizes the ExportFactory class. I noticed that the export factory class is in the ComponentModel.Composition.Initialization.Desktop_4 under both System and Microsoft. What am I missing? I’m new to WPF so I’m not really understanding where the disconnect is. I stepped into the
    CompositionInitializer.SatisfyImports(this) and traced it but i’m still not fully understanding how to solve this issue.

    Help please!

    • Reed says:

      Valery,

      ExportFactory isn’t in WPF 4 (but will be in 4.5). The assembly above was for .NET 3.5, though -

      I had some issues with this, and would recommend recompiling everything against .NET 4. The assembly, as downloaded above, may have issues as it’s built against 3.5.

      That being said – I believe the other problem is that should be:

      public IEnumerable> ViewModelFactories

      In addition, I believe you need to do some extra decoration on the exports if you want [ImportMany] to work with ExportFactory. See: http://stackoverflow.com/a/8450345/65358

      -Reed

      • Valery says:

        Thank you for your response,
        for public IEnumerable> ViewModelFactories …is this a typo? what would the value of T be for IEnumerable

  9. Bahri Gungor says:

    Would someone enlighten me as to why CompositionInitializer.SatisfyImports cannot work if the part has an export definition? I am sure my ignorance of various scenarios and my simplistic use of MEF might be confusing me, however, by commenting out the code below, my imports are satisfied on an exported class.

    if (part.ExportDefinitions.Any())
    {
    throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
    Strings.ArgumentException_TypeHasExports, part.ToString()), “part”);
    }

    An explanation would be greatly appreciated!

    • If I’m correct (not 100% sure), SatisfyImports is to be used for classes that can not be discovered by the container normally. Classes marked with a Export do not need this as the container will discover them. From this page:

      http://mef.codeplex.com/wikipage?title=CompsitionInitializer

    • Reed says:

      I’m not 100% sure why this decision was made by the MEF team, but it kind of makes sense. Having an [Export] on a part that would need to be used with SatisyImports would be fairly confusing, as it wouldn’t really get exported. The part wouldn’t be part of the main catalog, so the export wouldn’t be discoverable until after you’d imported (at runtime), which would likely lead to very odd behavior.

Trackbacks

Check out what others are saying about this post...
  1. [...] This post was mentioned on Twitter by Larry King, Vicente Cartas, Reed Copsey, Jr., Reed Copsey, Jr., Reed Copsey, Jr. and others. Reed Copsey, Jr. said: @xpaulbettsx It's up and available here http://reedcopsey.com/2010/03/26/mef-compositioninitializer-for-wpf/ [...]

  2. Social comments and analytics for this post…

    This post was mentioned on Twitter by ReedCopsey: @xpaulbettsx It’s up and available here http://reedcopsey.com/2010/03/26/mef-compositioninitializer-for-wpf/

  3. Daily tech links for .net and related technologies – Mar 29-31, 2010…

    Daily tech links for .net and related technologies – Mar 29-31, 2010 Web Development Querying the Future…

  4. [...] found here. Thanks Glenn. And in addition, I found lots of other great info from Reed Copsey’s site. Thanks [...]

  5. [...] bumped into Reed Copsey, Jr.’s article “MEF CompositionInitializer for WPF”. I downloaded included there Composition.Initialization.Desktop.zip solution for Visual Studio 2010 [...]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!