naresh's picture

Getting Started with Prism and MEF
7

The Developer’s Guide to Microsoft Prism provides an excellent source of information for Prism and MEF. It includes extensive documentation, QuickStarts and reference implementations that are very helpful. However, when I was starting out with Prism 4.0, I found that even the simplest MEF application included in this guide was a bit complex for me to use as a template for my own first Prism application. Now that I’ve crossed this hurdle, I’ve written a simple tutorial for people in my situation.

We’ll build a simple “Hello World” application (here’s the live demo and the source). You type in your name, and it responds with “Hello <your name>”. Of course, we’ll use Prism and MEF to make this a modular application. The finished app looks like this:

Screenshot

The application will consist of the Prism shell and one module called “Hello”, as shown below.

Regions

Prerequisites

You should have the following applications and libraries installed on your machine to follow this tutorial:

  1. Visual Studio 2010
  2. Prism 4.0 – November 2010
  3. Microsoft Silverlight 4 Tools for Visual Studio 2010
  4. Silverlight 4 Toolkit – April 2010

Overview of Tutorial Steps

Here are the major steps we’ll follow to build the application:

  1. Create a base Silverlight application
  2. Replace MainPage with the Prism Shell
  3. Add infrastructure to register views
  4. Create HelloModule
  5. Register HelloModule with MEF

So let’s get started.

Create a Base Silverlight Application

  • Open Visual Studio and select File > New > Project...
  • Navigate to Installed Templates > Visual C# > Silverlight and choose Silverlight Application
  • Enter “SimpleMefApplication” as the project name and the solution name and a directory location of your choice. Click OK.

    New Project

  • Visual Studio will present a few more options for creating a Silverlight application. Just click OK to accept the defaults.

    New Silverlight Application

  • Visual Studio will now create a very basic Silverlight solution for you. It consists of two projects:
    1. SimpleMefApplication – This is the Silverlight client project
    2. SimpleMefApplication.Web – This is a simple Web Site to host the Silverlight application

    Solution

  • At this point you should be able to run the application. Select Debug > Start Debugging (or press F5) and Visual Studio will start the application in your web browser. Of course the screen will be blank since the application currently does nothing!
  • Close the web browser. Select Debug > Stop Debugging (or press Shift-F5) in Visual Studio to stop debugging the application.

Replace MainPage with the Prism Shell

We will now do some surgery on the application created by Visual Studio and replace the default MainPage with the Prism Shell. This Shell needs to be invoked by the MEF bootstrapper – we will modify App.xaml.cs to accomplish this.

  • Expand the SimpleMefApplication project and delete MainPage.xaml. This automatically deletes the code-behind file called MainPage.xaml.cs.
  • Add a new UserControl to the project called Shell. To do this right click on the SimpleMefApplication project and select Add > New Item...
  • Navigate to Installed Templates > Visual C# > Silverlight and choose Silverlight User Control.
  • Enter Shell.xaml as the name of the control and click Add.
  • Add the following references to the SimpleMefApplication project as we will be using them in the following steps (right-click References and select Add Reference...)
    • Microsoft.Practices.Prism
    • Microsoft.Practices.Prism.MefExtensions
    • System.ComponentModel.Composition
    • System.Windows.Controls
    • System.Windows.Controls.Input.Toolkit
    • System.Windows.Controls.Toolkit
  • Replace the contents in Shell.xaml with the following code. This creates the application title (“Simple MEF Application”), a divider and finally a Prism region called “MainRegion”. The region will be later used by HelloModule to present its contents.
    <UserControl
        x:Class="SimpleMefApplication.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:prism="http://www.codeplex.com/prism"
        xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
        mc:Ignorable="d"
        d:DesignHeight="200" d:DesignWidth="450">
    
        <Grid x:Name="LayoutRoot">
            <Grid.Background>
                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                    <GradientStop Color="#FFFFFFFF" Offset="0"/>
                    <GradientStop Color="#FFEEEEEE" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
    
            <Grid.RowDefinitions>
                <RowDefinition Height="50"/>
                <RowDefinition />
            </Grid.RowDefinitions>
    
            <TextBlock
                Grid.Row="0" Grid.Column="0"
                HorizontalAlignment="Left"
                Margin="10,5,0,0"
                Text="Simple MEF Application"
                Foreground="#FF373737" FontWeight="Bold" FontSize="24" FontFamily="Corbel" />
    
            <toolkit:Separator
                Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                Margin="0,0,0,0"
                VerticalAlignment="Bottom"
                Height="10"
                BorderBrush="#193441"/>
    
            <ContentControl
                Grid.Row="1"
                prism:RegionManager.RegionName="MainRegion" />
        </Grid>
    </UserControl>
        
  • In Shell.xaml.cs, add the [Export] attribute above the Shell class and add a using directive at the top for System.ComponentModel.Composition. This ensures that the Shell will be exported to the MEF container. Here’s the finished code for Shell.xaml.cs (unnecessary using directives removed):
    using System.ComponentModel.Composition;
    using System.Windows.Controls;
    
    namespace SimpleMefApplication
    {
        [Export]
        public partial class Shell : UserControl
        {
            public Shell()
            {
                InitializeComponent();
            }
        }
    }
        
  • Add a MefBootstrapper to bootstrap your MEF application. To do this add a class called SimpleMefApplicationBootstrapper to the SimpleMefApplication project and populate it with the following code:
    using System.ComponentModel.Composition.Hosting;
    using System.Windows;
    using Microsoft.Practices.Prism.MefExtensions;
    using Microsoft.Practices.Prism.Regions;
    
    namespace SimpleMefApplication
    {
        public class SimpleMefApplicationBootstrapper : MefBootstrapper
        {
            protected override void ConfigureAggregateCatalog()
            {
                base.ConfigureAggregateCatalog();
                this.AggregateCatalog.Catalogs.Add(
                    new AssemblyCatalog(typeof(SimpleMefApplicationBootstrapper).Assembly));
            }
    
            protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
            {
                var factory = base.ConfigureDefaultRegionBehaviors();
    
                return factory;
            }
    
            protected override DependencyObject CreateShell()
            {
                return this.Container.GetExportedValue();
            }
    
            protected override void InitializeShell()
            {
                base.InitializeShell();
                Application.Current.RootVisual = (UIElement)this.Shell;
            }
        }
    }
        
  • In App.xaml.cs, replace the Application_Startup code to run the bootstrapper you just created. Here’s the finished code:
    private void Application_Startup(object sender, StartupEventArgs e)
    {
        SimpleMefApplicationBootstrapper bootstrapper =
            new SimpleMefApplicationBootstrapper();
        bootstrapper.Run();
    }
        
  • Run the application now. You should see the Shell we just created with an application heading and a seperator. The MainRegion is still blank because we have not done anything to populate it.

    Screenshot

Add Infrastructure to Register Views

Now that we have a basic Prism app running, let’s add some infrastructure code that will allow us to register views with Prism regions. This infrastructure will be used in the the next step when we create the HelloView. I have copied the infrastructure code directly from the Stock Trader Reference Implementation and packaged it as a project. Simply add this project to your solution using the steps below.

  • If you have not done so already, download the completed source and unzip it at a convenient location.
  • Copy the SimpleMefApplication.Infrastructure folder from the unzipped location to your solution folder.
  • In Visual Studio, right-click on your solution and select Add > Existing Project...
  • Traverse to the copied Infrastructure project and double-click on SimpleMefApplication.Infrastructure.csproj. The project will be added to your solution.

Create HelloModule

We are finally at a point where we can create a Prism module.

  • Right-click on the solution and select Add > New Project.
  • Navigate to Installed Templates > Visual C# > Silverlight and choose Silverlight Class Library.
  • Enter “SimpleMefApplication.Module.Hello” as the name of the project and click OK. A new project will be created for you.
  • Delete the class called Class1 that is created by default.
  • Add the following references to the project as we will be using them in the following steps (right-click References and select Add Reference...)
    • Microsoft.Practices.Prism
    • Microsoft.Practices.Prism.MefExtensions
    • System.ComponentModel.Composition
    • SimpleMefApplication.Infrastructure (project reference to our infrastructure project)
  • Create a class called HelloModule in the project. Replace the default contents with the code shown below. Note that HelloModule implements the IModule interface and is exported so that MEF knows about it.
    using Microsoft.Practices.Prism.MefExtensions.Modularity;
    using Microsoft.Practices.Prism.Modularity;
    
    namespace SimpleMefApplication.Module.Hello
    {
        [ModuleExport(typeof(HelloModule))]
        public class HelloModule : IModule
        {
            public void Initialize()
            {
            }
        }
    }
        
  • We will now create HelloView along with its ViewModel (yes we will use the new MVVM support provided by Prism 4.0). To start with, create three folders in the project called “Interfaces”, “ViewModels” and “Views”. Your project should now look like this:

    Solution

  • Add a new view to the project called HelloView. To do this right click on the Views folder and select Add > New Item...
  • Navigate to Installed Templates > Visual C# > Silverlight and choose Silverlight User Control.
  • Enter HelloView.xaml as the name of the control and click Add.
  • Replace the contents in HelloView.xaml with the following code. This creates a row for the user to enter her name and another row to display the response.
    <UserControl
        x:Class="SimpleMefApplication.Module.Hello.Views.HelloView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="400">
    
        <StackPanel x:Name="LayoutRoot" Margin="10">
    
            <StackPanel Orientation="Horizontal" VerticalAlignment="Top">
                <TextBlock Text="Name" VerticalAlignment="Center" />
                <TextBox Width="100" VerticalAlignment="Center" Margin="10 0 0 0"
                         Text="{Binding Path=Name, Mode=TwoWay}" />
                <Button Content="Submit" VerticalAlignment="Center" Margin="10 0 0 0"
                        Command="{Binding SubmitCommand}"/>
            </StackPanel>
            
            <TextBlock Text="{Binding Message}" Margin="0 10 0 0" Foreground="Red" />
    
        </StackPanel>
    
    </UserControl>
        
  • Create an interface called “IHelloViewModel” under Interfaces and populate it with the code below:
    namespace SimpleMefApplication.Module.Hello.Interfaces
    {
        public interface IHelloViewModel
        {
        }
    }
        
  • Create a ViewModel called “HelloViewModel” under ViewModels and populate it with the code shown below:
    using System.ComponentModel.Composition;
    using System.Windows.Input;
    using Microsoft.Practices.Prism.Commands;
    using Microsoft.Practices.Prism.ViewModel;
    using SimpleMefApplication.Module.Hello.Interfaces;
    
    namespace SimpleMefApplication.Module.Hello.ViewModels
    {
        [Export(typeof(IHelloViewModel))]
        [PartCreationPolicy(CreationPolicy.NonShared)]
        public class HelloViewModel : NotificationObject, IHelloViewModel
        {
            public HelloViewModel()
            {
                this.SubmitCommand = new DelegateCommand<object>(this.SubmitExecute);
            }
    
            #region SubmitCommand
    
            public ICommand SubmitCommand { get; private set; }
    
            private void SubmitExecute(object obj)
            {
                Message = "Hello " + Name;
            }
    
            #endregion
    
            #region Members
    
            private string _name;
            public string Name
            {
                get { return _name; }
                set
                {
                    if (value != _name)
                    {
                        _name = value;
                        this.RaisePropertyChanged("Name");
                    }
                }
            }
    
            private string _message;
            public string Message
            {
                get { return _message; }
                set
                {
                    if (value != _message)
                    {
                        _message = value;
                        this.RaisePropertyChanged("Message");
                    }
                }
            }
    
            #endregion // Members
        }
    }
        
  • Open HelloView.xaml.cs and replace its contents with the code shown below. Note the ViewExport attribute above the HelloView class – it registers the view with MEF and targets it to the MainRegion. Also the DataContext of the view is set to IHelloViewModel. It is injected by MEF by specifying the [Import] attribute.
    using System.ComponentModel.Composition;
    using System.Windows.Controls;
    using SimpleMefApplication.Infrastructure;
    using SimpleMefApplication.Module.Hello.Interfaces;
    
    namespace SimpleMefApplication.Module.Hello.Views
    {
        [ViewExport(RegionName = "MainRegion")]
        [PartCreationPolicy(CreationPolicy.NonShared)]
        public partial class HelloView : UserControl
        {
            public HelloView()
            {
                InitializeComponent();
            }
    
            [Import]
            public IHelloViewModel ViewModel
            {
                set { this.DataContext = value; }
            }
        }
    }
        

Register HelloModule with MEF

We are almost there! So far HelloModule is an isolated module, unknown to the SimpleMefApplication. For the application to load this module we need to let the MEF bootstrapper know about it. Also note that HelloModule uses the Infrastructure project to register itself with MEF. Hence we also need to let the application know about the Infrastructure project. So let’s tie up the loose ends and get the application running.

  • Add the following project references to the SimpleMefApplication project (right-click References and select Add Reference...)
    • SimpleMefApplication.Infrastructure
    • SimpleMefApplication.Module.Hello
  • Open SimpleMefApplicationBootstrapper and modify ConfigureAggregateCatalog( ) and ConfigureDefaultRegionBehaviors( ) to register Infrasturcture and HelloModule with MEF. Here’s the modified code.
    protected override void ConfigureAggregateCatalog()
    {
        base.ConfigureAggregateCatalog();
        this.AggregateCatalog.Catalogs.Add(
            new AssemblyCatalog(typeof(ViewExportAttribute).Assembly));
        this.AggregateCatalog.Catalogs.Add(
            new AssemblyCatalog(typeof(SimpleMefApplicationBootstrapper).Assembly));
        this.AggregateCatalog.Catalogs.Add(
            new AssemblyCatalog(typeof(HelloModule).Assembly));
    }
    
    protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
    {
        var factory = base.ConfigureDefaultRegionBehaviors();
    
        // Behavior that registers all views decorated with the ViewExport attribute
        factory.AddIfMissing(
            "AutoPopulateExportedViewsBehavior",
            typeof(AutoPopulateExportedViewsBehavior));
    
        return factory;
    }
        
  • Add the following using directives to SimpleMefApplicationBootstrapper to resolve all references:
    using SimpleMefApplication.Infrastructure;
    using SimpleMefApplication.Module.Hello;
        
  • That’s it – MEF now knows about HelloModule and it will instantiate the Module on startup. In addition, it will instantiate HelloView and place it in the MainRegion. Run the application and try it for yourself!

Summary

To recap, here are the steps we followed to create a simple Prism application using MEF:

  1. We created a basic Silverlight application in Visual Studio.
  2. We replaced the MainPage with the Prism Shell.
  3. Some infrastructure code was then added to help with view registration.
  4. Building on this foundation, we created the HelloModule.
  5. Finally, we registered the HelloModule with MEF.

Hope you found this tutorial to be useful. Please let me know if you have any suggestions for improvement.

Happy coding!

Tags: 

Comments

Great! Nice and straight to the point! How about a adding a WCF data service (without EF)?

I am trying to apply the following link to your project. It is using Unity instead of Mef. I am planning on using Mef. http://www.dotnetcurry.com/ShowArticle.aspx?ID=639 Maria
naresh's picture

Let me know how it goes. BTW, you can look at Bullsfirst source from our repository. It uses MEF, MVVM, DelegateCommand etc. and also demonstrates best practices around WCF. I am planning to write an article about all this but feel free to look at the source and let me know if you have any questions.

Really thanks. It's really helpful to make "Mef Prism". But.. Why don't you posted any more? It's been 6month since you posted this.  

It seems incorrect to put a reference to the helo module in the shell app because this effectively tighly couples the shell to the module??? I thought the whole point was that the shell does not know about the modules? yes or no?

Very nice tutorial. I think I'm finally beginning to understand Prism and MEF! One thing I'd like to see in Prism and MEF tutorials are more explanations of why the default template code is being replaced. In other words, when you say something like replace X with Y, I'd like to know what X didn't do that Y does do. (Clearly, I don't mean simple renamings of files, methods, etc.; the reasons for those are obvious.) I'm always left wondering why the supplied, default code isn't good enough. Since it's the default template code, what does it not do that it should, and that the Y code does do? Hope this makes sense. Changing subjects, the word "shell" should be "Shell" (capital S) in CreateShell. (See line 26 of the code for the SimpleMefApplicationBootstrapper.) You might also want to delete the erroneous text in line 36 of that same code block.

I think it would be very useful to add and expain the using of DeploymentCatlog here. It will highlight the distributed nature of MEF and dismiss the tigthly coupling between the module and hosting application. Waiting to collabolare!