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:
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.
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.
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
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.
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.
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
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
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!
Excellent !!!
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?
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.
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.
It’s not even swapping based on a class name. You can have any class, and as long as it’s decorated with [Export(“MainViewModel”)], it’ll work….
Thanks for clairification…
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…
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…
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.
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.
Ok another ahha moment for me and how late binding foils design time designers. Thanks! Never thought of it that way.
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.
That’s basically the same approach, Patrick. Its just that you’re holding the container in a static variable instead of using the CompositionInitializer.
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.
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!
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
Thank you for your response,
for public IEnumerable> ViewModelFactories …is this a typo? what would the value of T be for IEnumerable
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
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.