I'm currently trying to build a simple proof of concept with MEF, but I can't get it to work. I have three projects in my solution:
Component
here).A .NET library that defines a class which is derived from Button and declares a property of type Component
which is marked with an Import
attribute:
public class ComponentButton : Button
{
[Import]
public Component Component { get; set; }
}
A WPF app whose MainWindow has a property of type ComponentButton (marked as Export
) that is initialized in the constructor. Then I'd like to use MEF to assign this Component to all buttons that appear in the window (currently only one is defined in XAML):
[Export]
public Component Component { get; private set; }
public MainWindow()
{
InitializeComponent();
this.Component = new Component();
var container = new CompositionContainer(new DirectoryCatalog(".", "*"));
try
{
container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Debugger.Break();
}
}
However, this doesn't work. No exception is thrown, but the component isn't assigned to the button. When I look at the catalog in the debugger, I can see that while all assemblies were loaded and it finds the ExportDefinition, it couldn't find the import definition. But what really confuses me is that when I add an export to the button, it suddenly detects both the button's export and import:
[Export]
public string Dummy { get; set; }
But even then, it still doesn't assign the component to the button. What's going on?
The properties for your buttons aren't being set because the buttons are created by WPF (via the InitializeComponent
call) and not by MEF. Theoretically, you could walk through the visual tree and call container.SatisfyImportsOnce(obj)
for each and every object, but that would be a horrible thing to do performance-wise.
One solution is to set the property as usual with binding. However, it's possible to have the button perform self-composition. You'll have to expose the container somehow (via a static property for example) and have the button call SatisfyImportsOnce
on itself in the constructor:
Global.MyCompositionContainer.SatisfyImportsOnce(this);
This question might help as well.