Click here to Skip to main content
Click here to Skip to main content

Prism 4 MEF Application

, 13 Sep 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Building a Prism 4 application using MEF
Prism4Mefdemo.jpg

Introduction

There are quite a few articles written about Prism 4 in CodeProject. Many of you have done a very good job and have explained the mechanics very well. I have decided to take Prism 4 to a different point of view using MEF (Managed Extensibility Framework) and this does not compare with the other articles.

The article provides you with a framework to navigate Views using Menu Bars and Ribbon control. It builds on MVVM pattern (Model View View-Model) that loose coupling Shell application and Modules. Prism using MEF is very a different approach compared to Unity and it may be hard to switch from one to another.

Background

This article is focused on Prism 4 using MEF (Managed Extensibility Framework) instead of Unity. First we go through the similarities between MEF and Unity. MEF registers types and instances using IoC concepts and Unity registers types and instances with the container. They imperatively create instances of registered types. They inject instances of registered types into constructors and properties. They have declarative attributes for marking types and dependencies that need to be managed. Both of them resolve dependencies in an object graph.

However, Unity has several capabilities that are not found in MEF; as Unity resolves concrete types without registration and resolves open generics. Unity uses interception to capture calls to objects and add additional functionality to the target object.

Only MEF discovers assemblies in a directory and uses XAP file download and assembly discovery that is advantageous in SilverLight Application. It recomposes properties and collections as new and automatically exports derived types.

This article assumes that you have gone through the articles in CodeProject, Creating View-Switching Applications with Prism and A Prism 4 Application Checklist. The articles explained very well about the Shell Windows, Bootstrapper, Modules and Views. Please read them first, if you have not as this article does not cover them again to avoid duplication.

MVVM (Model View ViewModel)

MVVM pattern is trying to separate the functionalities between the Model and View. The ViewModel acts as Business Object inherent data checking to validate the incoming data and executes functionalities through Data Binding. Prism using MEF makes it easier to design the framework for the MVVM pattern by Import and Export objects between Views, ViewModel and Model. The demo project used Module Controller class to reduce the duplication of functionalities and it may have been a debate whether to use a controller or not. However, if it makes things easier for me, I am always for it rather than a die-hard pattern follower.

Prism Libraries

Prism MEF references are slightly different from Unity:

Add references to the following Prism assemblies to your new module project:

  • Microsoft.Practices.Prism.dll
  • Microsoft.Practices.ServiceLocation.dll
  • Microsoft.Practices.Prism.MefExtensions.dll

You will need to refer to System.ComponentModel.Composition.dll for the [Import] and [Export()] attributes.

The Demo Application

The demo application has four projects as follows:

  • Prism4MefDemo.exe creates from WPF Ribbon Application’s template, make sure you have installed Microsoft Ribbon for WPF 4.0.
  • Prism4MefDemo.Infrastructure.dll is the main support class library.
  • Prism4MefDemo.ModuleOne.dll is first Module based on the Prism guidance.
  • Prism4MefDemo.ModuleTwo.dll is just another Module.

The Shell Demo Application has three Regions in the Shell Window as follows:

  • RibbonRegion – Hosting the Ribbon Menu
  • NavigatorRegion – Hosting the Menubar similar to Outlook Menubar built from Listbox control
  • WorkspaceRegion- Hosting views for input and output of the application

The ShellWindow class has an [Export] attribute that has a reference ExportAttribute class in System.ComponentModel.Composition.dll that exports infer type and contract name.

The Prism4MefBootstapper class uses MefBootstrapper inheritance rather than UnityBootstrapper and has a MefBootstrapper.Container. The override DependencyObject CreateShell() is different from Prism Unity using MefBootstrapper.Container to get export value of the ShellWindow as follows:

protected override DependencyObject CreateShell()
{
        return this.Container.GetExportedValue<shellwindow>();
}

In order to initialise Prism4MefBootstaper class, we need to add new to the AssemblyCatalog in the override ConfigureAggregateCatalog.

protected override void ConfigureAggregateCatalog()
{
         this.AggregateCatalog.Catalogs.Add
         (new AssemblyCatalog(typeof(Prism4MefBootstapper).Assembly));       
}

The demo application has auto populate views using three classes in the Prism4MefDemo.Infrastructure as follows:

  • The AutoPopulateExportedViewsBehavior class inherits Microsoft.Practices.Prism.Regions.RegionBehavior class and interfaces System.ComponentModel.Composition. IPartImportsSatisfiedNotification.
  • The main method of the AutoPopulateExportedViewsBehavior class is the AddRegisteredViews that overrides the RegionBehavior.OnAttach and implements the IPartImportsSatisfiedNotification.OnImportsSatisfied. It has a property RegisteredViews to keep the View Region Registration.
  • The ViewExportAttribute class inherits System.ComponentModel.Composition.ExportAttribute and interfaces IViewRegionRegistration that will implement RegionName property.

The IViewRegionRegistration interface has RegionName property. Once again, we add new AutoPopulateExportedViewsBehavior class in the override ConfigureAggregateCatalog as below:

protected override void ConfigureAggregateCatalog()
{
         this.AggregateCatalog.Catalogs.Add
         (new AssemblyCatalog(typeof(AutoPopulateExportedViewsBehavior).Assembly));
}

Once we have done that, we can override ConfigureDefaultRegionBehaviors as below:

protected override Microsoft.Practices.Prism.Regions.
	IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
         var factory = base.ConfigureDefaultRegionBehaviors();
         factory.AddIfMissing("AutoPopulateExportedViewsBehavior", 
         typeof(AutoPopulateExportedViewsBehavior));
         return factory;
}

The overrides CreateModuleCatalog to load the modules using the App.config are as follows:

/// <summary>
/// Creates the <see cref="IModuleCatalog"/> used by Prism.
/// </summary>
/// <remarks>
/// The base implementation returns a new ModuleCatalog.
/// </remarks>
/// <returns>
/// A ConfigurationModuleCatalog.
/// </returns>
protected override IModuleCatalog CreateModuleCatalog()
{
         // When using MEF, the existing Prism ModuleCatalog 
         // is still the place to configure modules via configuration files.
         return new ConfigurationModuleCatalog();
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
  <configSections>
    <section name="modules" 
    type="Microsoft.Practices.Prism.Modularity.ModulesConfigurationSection, 
	Microsoft.Practices.Prism"/>
  </configSections>
 
  <modules>
    <module assemblyFile="Prism4MefDemo.ModuleOne.dll" 
    moduleType="Prism4MefDemo.ModuleOne.ModuleOne, Prism4MefDemo.ModuleOne.ModuleOne, 
    Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
    moduleName="ModuleOne" startupLoaded="true" />
    <module assemblyFile="Prism4MefDemo.ModuleTwo.dll" 
    moduleType="Prism4MefDemo.ModuleTwo.ModuleTwo, Prism4MefDemo.ModuleOne.ModuleTwo, 
    Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
    moduleName="ModuleTwo" startupLoaded="true" />
  </modules>
  
</configuration>

Shell Components

In the Shell Demo Application, I have created HomeView.xaml, HomeNavigator.xaml and HomeRibbonTab.xaml. Since this a demo application, I try to make Home components within the Shell, however, this can be done as a separate module component.

HomeView.xaml

The parts that are needed to be highlighted are as follows:

The Export attribute has a "HomeView" contract to allow the System.Uri to be identified the resource to be represented by the Uri instance. We can use this to navigate the view.

Uri viewNav = new Uri("/HomeView", UriKind.Relative);
regionManager.RequestNavigate(RegionNames.WorkspaceRegion, viewNav);

The ViewExport attribute has a RegionName = RegionNames.WorkspaceRegion contract to allow the AutoPopulateExportedViewsBehavior.OnAttach to attach the view into WorkspaceRegion.

HomeNavigator.xaml

HomeNavigator is WPF UserControl that has a ListBox that binds to View Object Collection that is part of MenuControls.xaml Resource Dictionary.

HomeNavigator class has ViewExport attribute has a RegionName = RegionNames.NavigatorRegion contract to allow the AutoPopulateExportedViewsBehavior.OnAttach to attach the view into NavigatorRegion. It also imports HomeNavigatorModel as a DataContext to bind the View Object Collection and ShowView command.

HomeRibbonTab.xaml

HomeRibbonTab is WPF RibbonTab that is part of Microsoft.Windows.Controls.Ribbon and has a RibbonGroup and the ItemsSource="{Binding Path=ViewObjects}". The RibbonButton has the Label binds to the Title property and the Command binds to DataContext.ShowView. The CommandParameter binds the ViewObject to return into the command when executed.

RibbonTab throws a multi-trigger binding exceptions and this has been restyle in RibbonControls.xaml to remove the binding.

HomeRibbonTab class has ViewExport attribute has a RegionName = RegionNames.RibbonRegion contract to allow the AutoPopulateExportedViewsBehavior.OnAttach to attach the view into NavigatorRegion. It also imports HomeNavigatorModel as a DataContext to bind the View Object Collection and ShowView command. Since the HomeRibbonTab has the same functionalities, I used the same View Model.

Modules

The two modules in the demo application are basically the same to show the loose coupling of the modules. Essentially, each module has its own Navigation Menu and Ribbon Menu loaded by the AutoPopulateExportedViewsBehavior class. The module has a ModuleController class and interface to communicate between navigation and views. The ModuleController is being used as MVVM pattern bridges between two different View Models rather than duplicating functionalities within the View Models.

The view binds to properties on a ViewModel and exposes data contained in model objects. The ModuleController is used to load data into and bind DelegateCommand to functions. The bindings between view and ViewModel are simple to construct because a ViewModel object is set as the DataContext of a view.

Demo Module Components

ModuleController Class and IModuleController Interfaces

The Export attribute in the ModuleController class exports the IModuleController interface to hide the implementations. It has an [ImportingConstructor] in the constructor to use the MEF constructor initialisation. It imports the IRegionManager as the regionManager to navigate the views and loads the data into the ViewObjects.

NavigatorModel and RibbonTabModel Class

The two classes are basically same at the moment, however, in a real business application you may see objects inside NavigatorModel but not the RibbonTabModel; so the separation allows you to built different functionalities. It loads the IModuleController interface using the [ImportingConstructor] and assigned the properties.

Views

ModuleOneNavigator.xaml

ModuleOneNavigator is a WPF UserControl that has a ListBox that binds to View Object Collection that is part of MenuControls.xaml Resource Dictionary.

ModuleOneNavigator class has ViewExport attribute has a RegionName = RegionNames.NavigatorRegion contract to allow the AutoPopulateExportedViewsBehavior.OnAttach to attach the view into NavigatorRegion. It also imports ModuleOneNavigatorModel as a DataContext to bind the View Object Collection and ShowView command.

ModuleOneRibbonTab.xaml

ModuleOneRibbonTab is a WPF RibbonTab that is part of Microsoft.Windows.Controls.Ribbon and has a RibbonGroup and the ItemsSource="{Binding Path=ViewObjects}". The RibbonButton has the Label binds to the Title property and the Command binds to DataContext.ShowView. The CommandParameter binds the ViewObject to return into the command when executed.

RibbonTab throws a multi-trigger binding exceptions and this has been restyled in RibbonControls.xaml to remove the binding.

ModuleOneRibbonTab class has ViewExport attribute has a RegionName = RegionNames.RibbonRegion contract to allow the AutoPopulateExportedViewsBehavior.OnAttach to attach the view into NavigatorRegion. It also imports ModuleOneRibbonTabModel as a DataContext to bind the View Object Collection and ShowView command.

View1.xaml and View2.xaml

The part that needed to be highlighted as follows:

The Export attribute has a name "View1" or "View2" contract to allow the System.Uri to be identified the resource to be represented by the Uri instance. We can use this to navigate the view. In the code, I have used the view property in the ViewObject.

Uri viewNav = new Uri("/View1", UriKind.Relative);
regionManager.RequestNavigate(RegionNames.WorkspaceRegion, viewNav);

Conclusion

This article presents creating Prism using MEF object rather than Unity. I do not have a preference over the different methods as I have built Prism 2 using Unity and migrating Prism 4 using Unity. Changing from one to another can be quite painful and you may want to consider building another Prism project from the ground up using MEF or even stick to Unity because you are comfortable with it.

History

  • 13 Sept 2011: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

No Biography provided

Comments and Discussions

 
QuestionMissing the point? PinmemberDXXL3-Mar-13 2:45 
AnswerRe: Missing the point? Pinmembertruck032115-Nov-13 7:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 13 Sep 2011
Article Copyright 2011 by Danny Siew from Wellington, New Zealand
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid