MEF CompositionInitializer for WPF
- by Reed
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:
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.
Composition.Initialization.Desktop.zip
Edit on 3/29:
Glenn Block has since updated his version of CompositionInitializer (and ExportFactory<T>!), and made it available here:
http://cid-f8b2fd72406fb218.skydrive.live.com/self.aspx/blog/Composition.Initialization.Desktop.zip
This is a .NET 3.5 solution, and should soon be pushed to CodePlex, and made available on the main MEF site.