This project is read-only.

Import MEF in UserControl on View

Mar 1, 2013 at 10:17 PM
I have a View (Page) with a view model, and a UserControl on that page with its own view model.
I tried the [PageExport("")] and [ViewModelExport("")] tags, but it didn't work. When the UserControl loads, it has the same data context as it's parent (the Page).

I tried tagging the uc's vm with [Export] and then on the uc [Import] ucViewModel, but the Import never resolves. I tried setting the DataContext of the uc on the Loaded event but ucViewModel was still null.

How can I go about doing this? I want to share the VM among a few user controls, so setting the DataContext to a new ucViewModel() isn't really an option.

I'm also having the same problem on another class where the Imports aren't working... am I missing something?
public sealed partial class MyUC : UserControl
{
    [Import]
    public ucViewModel vm { get; set; }
 
    public MyUC()
    {
        this.InitializeComponent();
    {
}
[Export]
[Shared]
public class ucViewModel
{
    [ImportingConstructor]
    public ucViewModel()
    {        
    }
}
Mar 2, 2013 at 10:51 AM
Hi,

The problem in this case is that MEF doesn't know that it needs to compose the UserControl - I assume that this is created via XAML in this case which is essentially a "new MyUC()". The way that MEF works normally is that it starts from a root class that calls CompositionHost.SatisfyImports(...) that then has a number of imports, each of which have their own imports, etc. For example if you look at the source for the OkraBootstrapper it calls "compositionHost.SatisfyImports(this);" which starts the chain. This imports the INavigationManager which in turn imports an IViewFactory which in turn imports your view which in turn imports any of its dependencies. There is never a line in Okra that says "new YourView()" for example - this is all dealt with by MEF in the composition chain.

So - regarding solutions I'll have a think about the best way to do this. You could for example expose the compostion host from the bootstrapper and call SatisfyImports(...) from within the user control, however it is not best practice with IoC containers to expose the container (and also introduces some issues with import lifetimes). Alternatively you could import the usercontrol and add it in the code-behind of the view - this seems better but not great.

I'll have a think and get back to you...
Andy
Mar 6, 2013 at 5:56 PM
Hi again,

I've had a think about this and I've got a couple of ideas. As I hinted in my last post, the trick is to ensure that the ucViewModel is created as an import to something else that is currently being composed by MEF (i.e. imported by something else). The two points that probably fit best are the view and view-model of the page that contains the UserControl - which of these makes most sense depends on your actual scenario. Let me know if one of these solutions works for you - there are more complex ways of doing this, but why over-complicate things if the simple way works!

Importing the ucViewModel into the pageViewModel

In this case you would add the ucViewModel as an imported property of the page's view model,
[ViewModelExport("MyPage")]
public class MyViewModel
{
    [Import]
    public ucViewModel MyUCViewModel { get; set; }
}
and in the XAML bind the UserControl's DataContext to this property.
<myns:MyUC DataContext="{Binding MyUCViewModel}"/>
Importing the ucViewModel into the pageView

In this case you would add the ucViewModel as an imported property of the page itself in the codebehind (probably via the importing constructor),
[PageExport("MyPage")]
public class MyPage : LayoutAwarePage
{
    [ImportingConstructor]
    public MyPage(ucViewModel myUCViewModel)
    {
        this.myControl.DataContext = myUCViewModel;
    }
}
with the MyUC named in the XAML
<myns:MyUC x:Name="myControl" />
Mar 7, 2013 at 4:48 PM
Andy,
I had a feeling the easiest solution would be something like that after you explained a bit on MEF importing. It made sense why it wasn't working. I decided to go with importing the ucViewModel to my pageViewModel, then binding the data context. Seems to be the cleanest solution.

Thanks!
Mar 7, 2013 at 8:55 PM
Great - Pleased that got you going again,

Andy