1

I'm trying to create a simple modular MVVM application with MEF. I have a ViewModel class and a UserControl as the View. I connect the two through a DataTemplate, like this:

<DataTemplate DataType="{x:Type local:MyViewModel}"> <local:MyView /> </DataTemplate> 

In the View, I define the ViewModel as a StaticResource, to make binding simple:

<UserControl.Resources> <local:MyViewModel x:Key="ViewModel" /> </UserControl.Resources> 

Then I bind like this:

<Grid DataContext="{StaticResource ResourceKey=ViewModel}"> <TextBlock Text="{Binding Text}" /> </Grid> 

This all works as intended without MEF. However, as I am aiming for modularity, I use MEF to discover my ViewModel classes. I have an Export attribute on my ViewModel class:

[Export(typeof(MyViewModel))] public class MyViewModel { // ... } 

and I use MEF to dynamically load the ViewModel into my shell in App.xaml.cs:

private void Application_Startup(object sender, StartupEventArgs e) { var shell = new MainWindow(); var catalog = new AssemblyCatalog(this.GetType().Assembly); var container = new CompositionContainer(catalog); shell.Contents.ViewModel = container.GetExportedValues<MyViewModel>().First(); shell.Show(); } 

Now, at this point, MEF creates an instance of my ViewModel when it loads the vm, and my View creates another instance when it declares the vm as a resource. (This is easily checked by setting a breakpoint in the constructor.)

The question is, how should I pass the instance created by MEF to my resource declaration? Can I declare that specific instance as resource?

DropBox link with full code: https://www.dropbox.com/sh/pbdl029d26sx7gl/AAA6po50dLjbJSoNPBhCyWZ3a?dl=0

1
  • Modified my infrastructure using DataContextSpy based on this answer: stackoverflow.com/a/5402653/5219911 Leaving the question open for a bit, in case someone comes up with another soulition, but this seems to work at the moment. Commented Sep 29, 2015 at 17:49

2 Answers 2

0

The creation of the MyViewModel is purely based on the sequence of how your program execution but you can set the CreationPolicy to make your instance become a singleton so that both your code and the resources both referring to the same instance.

[Export(typeof(MyViewModel)), PartCreationPolicy(CreationPolicy.Shared)]

Side note: To use MEF, Microsoft hide the implementation of CompositionInitializer and CompositionHost from .Net Framework for some reason. Try to google up and import the 2 classes from Microsoft instead of using the CompositionContainer directly. You'll have better experience in using MEF with those.

Sign up to request clarification or add additional context in comments.

1 Comment

This unfortunately only tells MEF to instantiate my viewmodel only once.The issue is that both MEF and the WPF resource lookup tries to instantiate said viewmodel. If I created the view directly, I'd be able to add a viewmodel resource. However, as the view is created from a DataTemplate, I don't have a reference to the view to directly acces its resources.
0

Okay, so, what I had to do was this.

I had two instances because once MEF instantiated the ViewModels when it imported them, and then another time WPF created them, when it created the ViewModel resource. I figured that the solution would be not directly creating the resource, but had no idea how I could manage to do that. Then along came Resource Injection and then DataContextSpy, from this question here: https://stackoverflow.com/a/5402653/5219911

And here's the direct link to the topic: http://www.codeproject.com/Articles/27432/Artificial-Inheritance-Contexts-in-WPF

I am now using a resource that is a DataContextSpy, through which I can reach out to the ViewModel instance that is used when creating the DataTemplate.

In my View's resources, I define and then I just set the root element's DataContext to this resource: DataContext="{Binding Source={StaticResource ResourceKey=ViewModel}, Path=DataContext}"

Now, unfortunately, I don't get Intellisense support with this alone, as DataContextSpy is sort of a proxy to the real DataContext, so I do have to manually set up the design time DataContext type by using: d:DataContext="{d:DesignInstance Type=viewModel:MyViewModel}"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.