MEFedMVVM with PRISM 4

Today I was looking at PRISM 4 and how it uses MEF as its DI Container and I thought, wouldn’t it be cool if you could use the 2 together?

Update: Please also check out this post to see how you can use the same composition container for both PRISM and MEFedMVVM so that stuff like IRegionManager, IEventAggregator etc can be injected also in MEFed ViewModels

http://mefedmvvm.codeplex.com/workitem/15391

Why would it be cool?

PRISM brings to the table

– Region Manager
– Modules infrastructure
– Many other utilities and services that you can consume

MEFedMVVM brings to the table

– ViewModel injection in XAML
– Design Time vs Runtime services (so that you can inject design time services when in blend)
– ContextAware services such as IVisualStateManager

Having the 2 working together would be awesome. The question is can they work together?

…teasing… suspense … ok enough Smile

The answer is yes and very easily…

How to do it

In PRISM you need to create a bootstrapper that will basically compose your application. MEFedMVVM also has a sort of Bootstrapper where you can specify how you want to compose the MEF composition. So as such all you need to do is to have your PRISM bootstrapper also tell MEFedMVVM how to do the composition.

Let’s start by creating a PRISM bootstrapper

We need a class that inherits from MefBootstrapper and we will need to override a couple of methods. Here is the code to do this

public class Bootstrapper : MefBootstrapper

{

    protected override void ConfigureAggregateCatalog()

    {

        this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));

    }


    protected override void InitializeShell()

    {

        base.InitializeShell();


        Application.Current.MainWindow = (Shell)this.Shell;

        Application.Current.MainWindow.Show();

    }


    #region Overrides of Bootstrapper


    protected override DependencyObject CreateShell()

    {

        return this.Container.GetExportedValue<Shell>();

    }


    #endregion

}

Now lets enable MEFedMVVM

In order to do this we will simple need to implement the IComposer interface from MEFedMVVM and then return the AggregateCatalog property (that is given to us by PRISM)

#region Implementation of IComposer (For MEFedMVVM)


public ComposablePartCatalog InitializeContainer()

{

    //return the same catalog as the PRISM one

    return this.AggregateCatalog;

}


public IEnumerable<ExportProvider> GetCustomExportProviders()

{

    //In case you want some custom export providers

    return null;

}


#endregion

In this case we will return null as the GetCustomExportProviders. This is a feature used if you have some custom ExportProvider you want MEFedMVVM to use.

The last step (which is the actual line of code to enable MEFedMVVM) is where we tell the MEFedMVVM LocatorBootstrapper to use this class as runtime composer.

protected override DependencyObject CreateShell()

{

    //init MEFedMVVM composed

    MEFedMVVM.ViewModelLocator.LocatorBootstrapper.ApplyComposer(this);


    return this.Container.GetExportedValue<Shell>();

}

As you can see I have put that line of code in the CreateShell method so that the Composer is applied as early as possible so that all views can use MEFedMVVM.

 

Now you can start using MEFedMVVM as you would in a normal project… For example in the Shell you can say

<Window x:Class="MEFedMVVMAndPRISM.Shell"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="Shell" Height="300" Width="300"

        xmlns:mefed="http:\\www.codeplex.com\MEFedMVVM"

        mefed:ViewModelLocator.NonSharedViewModel="ShellViewModel">

and the ShellViewModel would be

[ExportViewModel("ShellViewModel")]

public class ShellViewModel

{

    public string Text { get; set; }


    public ShellViewModel()

    {

        Text = "Hello from the ViewModel";

    }

}

Of course here I am not really leveraging MEFedMVVM capabilities, yet the purpose of this post is not to show those capabilities but to show how you can use PRISM and MEFedMVVM together and take what is best from both. To read more on MEFedMVVM visit the codeplex site.

I create a small sample project to showcase both PRISM and MEFedMVVM working together.

Download sample

Capture

9 thoughts on “MEFedMVVM with PRISM 4

  1. If you need the IRegionManager to be injected to your ViewModels you will need to do the following in the CreateShell method of the bootstrapper

    ViewModelRepository.Instance.Resolver.Container.ComposeExportedValue( Container.GetExportedValue());

    This will inject the same instance of the IRegionManager in the MEFedMVVM CompositionContainer

    Regards
    Marlon

    • Slight correction to the IRegionManager (and any other service) composition fix.
      protected override DependencyObject CreateShell()
      {

      // Init MEFedMVVM composed
      MEFedMVVM.ViewModelLocator.LocatorBootstrapper.ApplyComposer((IComposer)this);

      // Need one line for each service shared between PRISM and MEFedMVVM (otherwise multiple services will be instantiated regardless of PartCreation specification)
      ViewModelRepository.Instance.Resolver.Container.ComposeExportedValue(Container.GetExportedValue());
      ViewModelRepository.Instance.Resolver.Container.ComposeExportedValue(Container.GetExportedValue());

      return this.Container.GetExportedValue();
      }

      Otherwise, each Import will get its own instance, regardless of the PartCreationPolicy. If you are wondering why EventAggregator isn’t receiving events on the subscribers, this may be why.

      • Whoops. It looks like my post suffers from the same problem as Marlon’s. GetExportedValue is a generic, which takes the interface or type of the service. So it should look like:

        Container.GetExportedValue<IRegionManager>()

        Container.GetExportedValue<IEventAggregator>()

    • Marlon. Hope all is well. I have been using MEFedMVVM with PRISM (well elements of it) and following a conversation with Sacha this morning, I remember a change we made that meant that you did not have to do this. We added some code that would allow MEFedMVVM and PRISM to share the same Container. Basically, make your Bootstrapper implement the interface IContainerProvider. Then implement the code as follows:

      [SuppressMessage(“Microsoft.Reliability”,
      “CA2000:Dispose objects before losing scope”,
      Justification = “The default export provider is in the container and disposed by MEF.”)]
      CompositionContainer IContainerProvider.CreateContainer()
      {
      if (this.Container == null)
      {
      var mefedProvider = new MEFedMVVMExportProvider(MEFedMVVMCatalog.CreateCatalog(AggregateCatalog));
      var providersList = new List();

      //check if there are any custom export providers
      var providers = GetCustomExportProviders();
      if (providers != null && providers.Any())
      providersList.AddRange(providers);

      //add mefedMVVM provider
      providersList.Add(mefedProvider);

      var container = new CompositionContainer(providersList.ToArray());
      foreach (var imefedProvider in container.Providers.OfType())
      {
      imefedProvider.SourceProvider = container;
      }

      return container;
      }
      return Container;
      }

      protected CompositionContainer CreateContainer()
      {
      return ((IContainerProvider)this).CreateContainer();
      }

      With this code in place, you no longer need to do this to get shared instances:
      // Need one line for each service shared between PRISM and MEFedMVVM (otherwise multiple services will be instantiated regardless of PartCreation specification)
      ViewModelRepository.Instance.Resolver.Container.ComposeExportedValue(Container.GetExportedValue());

      If you need any more info on this, I’ll be happy to oblige.

      Cheers

      Steve

  2. Hi Marlon,

    This is an awesome framework – thank you! I do have one question about the ample code you’ve provided…..when I open the solution, I don’t see any design time data in any of the views or the shell – is that the way it’s supposed to be. Since the bootstrapper is in the mail project, does it also mean that i will not be able to view the design time data in the individual views and only view it in the shell?
    thanks,
    Priya

  3. I’ve been using code from your post for getting MEFedMVVM to work with Prism, and we recently discovered our objects weren’t being disposed. It turns out that if you pass an ExportProvider into the constructor of the MEF CompositionContainer, MEF will not take responsibility for disposing it. So our Bootstrapper now holds a reference to the MEFedMVVMExportProvider and explicitly disposes it when our application is shutting down.

    Hope this helps someone out there.

  4. How would one share a ViewModel instantiated using Prism’s View Discovery

    IE.
    Use a string builder to build the query string then launch the view via:

    Me.regionManager.RequestNavigate(RegionNames.MainContentRegion, New Uri(builder.ToString(), UriKind.Relative))

    I haven’t run across an example yet that utilizes Prism’s ability to compose the view on the fly then load data from the passed in query string.

Leave a comment