Introduction to MEF and Prism 4.0

I love Inversion of Control; in my opinion its possibly one of the best software solutions and one that i think every developer should know. Today i’m going to cover something that uses IOC but isn’t your standard IOC container (and YES i know that you don’t have to have a container for it to be IOC but generally that’s what people expect).

First off, its quite clear to see that MEF is ‘trying’ to be a fully functional inversion of control container; I say ‘trying’ because there a are a number of things that MEF cant to do that other containers do out of the box such as life style management, interception, xml configuration etc… it does however give you property and constructor injection.

What MEF does well is allow you to manage a set of “unknown” objects where as Castle Windsor lets you manage a set of “known” objects. Anyway, enough talk, lets get into some code.

To get started, go and get a copy of Prism from here. Its a fairly large package because you get the source code and examples applications.

I’m using Visual Studio 2012 Express for this so, lets start by creating a new WPF Application project. When the project is ready go, change the framework and set it to 4.0 framework NOT the 4.0 Client Profile framework

Now set your references to the Prism dll’s

  1. Microsoft.Practices.Prism.dll
  2. Microsoft.Practices.Prism.Interactivity.dll
  3. Microsoft.Practices.Prism.MefExtensions.dll
  4. Microsoft.Practices.Prism.UnityExtensions.dll
  5. Microsoft.Practices.ServiceLocation.dll
  6. Microsoft.Practices.Unity.dll
  7. System.ComponentModel.Composition.dll

That’s the Prerequisites out of the way, lets get some code written. MEF is actually shipped with .Net hence the reference to the system.ComponentModel.Composition assembly (this is where MEF resides) the other reference are just the Prism framework which we will be using in conjunction with MEF.

In order to start using MEF and Prism you will need to create a ‘Bootstapper’ to setup the MEF IoC environment. While its possible to use Unity or Castle Windsor as the primary IoC container im going to use the MEF container in this example, this means that we will be using MEF to auto-discover our components and invoke them when we need them. So whats our ‘Bootstrapper’ look like, lets take a look at the code below

public class ApplicationBootStrapper : MefBootstrapper
{

}

You can see that we inherit from MefBootstrapper, we will then need to override the basic methods used to initalise MEF. The Bootstrapper needs to initialize the ‘Shell’. This is the start point, generally the GUI part of the application that you are working on. In your project explorer, create a new Window and call it ‘Shell”. Now lets implement it.

public class ApplicationBootStrapper : MefBootstrapper
{
        protected override DependencyObject CreateShell()
        {
            return new Shell();
        }
}

When we create a new instance of the bootstrapper it will all the CreateShell() method and set a new instance of our window on the base ‘Shell’ property of the MefBootstrapper base class. Lets put something nice and simple inside of our window so that we can see it working. The code below will add a simple WPF Items control to our page.

<Window x:Class="Prism.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://www.codeplex.com/prism"
        Title="Shell" Height="300" Width="300">
    <Grid>
            <ItemsControl prism:RegionManager.RegionName="mainRegion" />
    </Grid>
</Window>

You may notice the prism:RegionManager.RegionName=”mainRegion” attribute thats been added to the ItemsControl, this is going to be the ‘Target’ of our MEF / Prism example, its purpose will become clear very soon. In order for you to be able to add this to your ItemsControl you will first need to add a new namespace to your window (xmlns:prism=”http://www.codeplex.com/prism”) you should be able to see this specified in the example.

Lets now set up the Main Window of our application by overriding the InitializeShell method of MefBootstrapper.

public class ApplicationBootStrapper : MefBootstrapper
{
        protected override DependencyObject CreateShell()
        {
            return new Shell();
        }

        protected override void InitializeShell()
        {
            base.InitializeShell();
            App.Current.MainWindow = (Window)Shell;
            App.Current.MainWindow.Show();
        }
}

So this basically sets the MainWindow property of our WPF application; not very exciting so far. If you run the application now you will just see the new window load and that’s about all.

The next step is to configure whats called the ‘Aggregate Catolog’. This is what MEF uses to manage the loaded components, its basically the MEF equivalent of the IoC container. Lets take a look a a basic implementation.

public class ApplicationBootStrapper : MefBootstrapper
{
        protected override DependencyObject CreateShell()
        {
            return new Shell();
        }

        protected override void InitializeShell()
        {
            base.InitializeShell();
            App.Current.MainWindow = (Window)Shell;
            App.Current.MainWindow.Show();
        }

        protected override void ConfigureAggregateCatalog()
        {
            base.ConfigureAggregateCatalog();
            DirectoryCatalog dir = new DirectoryCatalog(Environment.CurrentDirectory + @"\Modules", "*.dll");
            this.AggregateCatalog.Catalogs.Add(dir);
        }
}

So, Whats this new code doing. We are basically telling MEF to scan a folder (Modules) under the current working folder for any dll files, once finished, the found files are added to the catalog. This is almost akin to the Castle Windsor Install method which looks like this


IWindsorContainer container = new WindsorContainer();
container.Install(FromAssembly().InDirectory(new AssemblyFilter(Environment.CurrentDirectory,"*.dll"));

If you are familiar with Castle Windsor you will know that this will load all dlls at the specified location and then find all implementations of
IWindsorInstaller interface and then load all the types registered.

Ok this is great, we now have MEF configured now all we need to do is create something which uses MEF’s capabilities.

You will need to create a new DLL for your main project to load; generally you will need your “module” to have WPF’s Presentation references so lets create a new WPF Application project and add it to our solution, when its loaded, add the same Prism references that you added in the original project and also convert it from the .Net 4.0 Client Profile to the .Net 4.0 Framework and change the project output type to ‘Class Library’.
Alter the build location so that it builds into a folder called “Modules” which is in the bin\Debug folder of the main application.

Create a new Class file in your new project, call it “SayHello.cs”. Open the class file; MEF requires all modules to have a class that implements the IModule interface. IModule exposes a single method ‘Initialize()’ this is the entry point for the module. In the Initialize method you place all the code that you want to run as the module is loaded. Your class should look like this.

namespace WpfApplication2
{
    using System.ComponentModel.Composition;
    using Microsoft.Practices.Prism.MefExtensions.Modularity;
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Regions;

    public class SayHello : IModule
    {
        public void Initialize()
        {
            
        }
    }
}

Before we continue with filling out the Initialize method, you first need to add a constructor to the class. This is our first point of injection.

namespace WpfApplication2
{
    using System.ComponentModel.Composition;
    using Microsoft.Practices.Prism.MefExtensions.Modularity;
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Regions;

    public class SayHello : IModule
    {
        IRegionManager region;
        public SayHello(IRegionManager regionManager)
        {
            region = regionManager;
        }

        public void Initialize()
        {
            
        }
    }
}

MEF uses constructor injection to pass the RegionManager into the constructor of our SayHello class. As you can see, we just store that in a local variable so that we can use it else where in our class. Remember, if its a constructor injected argument then its a mandatory dependency, if its a property then its an optional dependency an optional dependencies are not resolved in the constructor.

The RegionManager is responsible for maintaining a collection of regions and attaching regions to controls. Its purpose will be shown next.
Remember when we created the main application we added an ItemsControl to the Shell.xaml file and added the attribute (prism:RegionManager.RegionName=”mainRegion”) to it, Well here is where we make a reference to it in our new module.

namespace WpfApplication2
{
    using System.ComponentModel.Composition;
    using Microsoft.Practices.Prism.MefExtensions.Modularity;
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Regions;

    public class SayHello: IModule
    {
        IRegionManager region;
        public SayHello(IRegionManager regionManager)
        {
            region = regionManager;
        }

        public void Initialize()
        {
            region.RegisterViewWithRegion("mainRegion", null);
        }
    }
}

You can see that the Initialize method uses the local reference to the RegionManager to call a method RegisterViewWithRegion, the first argument passed in is the name of the region that we created earlier. Ok, the last parameter of the RegisterViewWithRegion method is currently set to null, lets change that.

Create a new User Control in the class library and call it DataView. Now change the contents of the Initialize method to

public void Initialize()
{
    region.RegisterViewWithRegion("mainRegion", typeof(DataView));
}

What we have done here is tell MEF that when it loads this dll it can place an instance of the new User Control that we just created into the Items Control in the main window; clever isnt it!

We’re not done just yet because we just need to tell MEF whats in the IModule implementation, we do this using attributes.

Add ImportingConstructor] to the constructor of our new SayHello class.

Add [ModuleExport(“SayHello”, typeof(SayHello))] to the class SayHello.

Your class should now look like this

namespace WpfApplication2
{
    using System.ComponentModel.Composition;
    using Microsoft.Practices.Prism.MefExtensions.Modularity;
    using Microsoft.Practices.Prism.Modularity;
    using Microsoft.Practices.Prism.Regions;

    [ModuleExport("SayHello", typeof(SayHello))]
    public class SayHello: IModule
    {
        IRegionManager region;
        [ImportingConstructor]
        public SayHello(IRegionManager regionManager)
        {
            region = regionManager;
        }

        public void Initialize()
        {
            region.RegisterViewWithRegion("mainRegion", typeof(DataView));
        }
    }
}

At the minute if we ran the project we wouldn’t see anything interesting at all, so lets change that.

Open the DataView.xaml file and add the following code

<UserControl x:Class="WpfApplication2.DataView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock Text="{Binding SayingHello}"></TextBlock>
    </Grid>
</UserControl>

The interesting part here is the TextBlock control that we have added to the UserControl, we have set a Data Binding {Binding SayingHello} to the Text property of the TextBlock. This binding wont be fulfilled in this class library, instead it will be fulfilled by the main application.

We also need to add a little code to the constructor of the UserControl, its another attribute and it looks like this [Export(typeof(DataView))]

namespace WpfApplication2
{
    using System.ComponentModel.Composition;
    using System.Windows.Controls;

    [Export(typeof(DataView))]
    public partial class DataView: UserControl
    {
        public Stuff()
        {
            InitializeComponent();
        }
    }
}

Thats it, thats the infrastructure complete. If you run it you still wont see anything that interesting because the TextBlock has no data so lets remedy that.

Add a button to the Shell.xaml code file, then create the event method in the code-behind

<Button Height="20" Width="30"  Click="Button_Click">Click</Button>

The code behind of Shell.xaml.cs should look like this.

    public partial class Shell : Window
    {
        public Shell()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
        }
    }

Lets create a simple structure to hold some data. For now, just create the class in the Shell.xaml.cs code-behind file

    public class SampleData
    {
        public string SayingHello { get; set; }
    }

now lets alter the Button_Click event procedure to create a new instance of the SampleData class.

namespace Prism
{
    public partial class Shell : Window
    {
        public Shell()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.DataContext = new SampleData { SayingHello = "Hello World" };
        }
    }

    public class SampleData
    {
        public string SayingHello { get; set; }
    }
}

Notice that when we created a new instance of our SampleData class we initalised it and set it to this.DataContext. We are using the DataContext as the source for our data binding.

So now if you run the application you should see the shell window with a single button in it; clicking the button should populate the UserControls TextBlock as as a result because the ItemsControl can contain an instance of the SayHello UserControl we can see the “Hello World” text appear in our applications window.

This is a loosely coupled application; this tutorial / example should give you the basics to try and extend it and play with MEF. Its possible to add new “types” to MEF so that they can be resolved with constructor or property injection and this is something i will cover in another post.

This has been a pretty basic MEF tutorial but I hope that you can see the possibilities that MEF offers.