Prism 4 Application Checklist
Prism 4 Application Checklist
Introduction
This article was written as a companion piece to Creating View-Switching Applications with Prism 4, also published on
CodeProject. However, it can be used as a guide to setting up almost any Prism 4 application. The article provides a step-by-step
explanation of how to set up a Prism application.
The checklist in this article assumes that the developer will use Unity 2.0 as the Dependency Injection (DI) container for the
application, and that logging will be performed using the log4net framework. But the checklist does not depend heavily on these
components, and it should be easily adaptable for use with other DI containers and logging frameworks.
The checklist includes the steps needed to set up a Prism application for View-switching, and the demo app included with the
article is a View-switching app. For more information on View-switching, see the companion article. Note that the demo app
does not implement logging.
We copy the Prism DLLs to a solution library folder so that we can use a private, or side-by-side installation of Prism for our
solution. This approach eliminates some of the versioning problems that can occur when Prism is updated. For example, I have
several Prism 2.x solutions that I maintain. Since each solution uses its own Prism DLLs, the older version does not interfere with
the newer version, and vice versa.
Step 1c Set references to library DLLs: In the References section of Solution Explorer, add references to the DLLs added to
the solution library in the preceding step.
Step 1d Rename the Shell window: First, rename MainWindow.xaml to ShellWindow.xaml in Solution Explorer. Then, open
ShellWindow.xaml.cs in VS. The class will still be named MainWindow. Rename the class using the Visual Studio refactoring
tools; this will change all references to the class, as well.
Step 1e Add a namespace definition to the Shell window: Add a XAML namespace definition to ShellWindow.xaml:
xmlns:prism=http://www.codeplex.com/prism
Step 1f Lay out Shell window: ShellWindow.xaml will contain controls that are declared as regions, typically
ContentControl or ItemsControl objects. Each of these controls represents a region in the Shell window. These
regions will hold the content that is loaded into the Shell window.
ContentControl regions are suitable for regions that will display one View at a time.
ItemsControl regions are suitable for regions that will display several Views at once, such as the task buttons in the
screenshot above.
ShellWindow.xaml is laid out in much the same way as any other window, using grids, splitters, and other layout controls. The
only difference is that the window contains few if any real controls. Instead, it contains placeholder controls that are declared as
regions. Prism inserts content from regions into these placeholders later.
Each region control needs both a x:Name and a RegionName. The regular x:Name is used by your code to refer to the
control as needed. Prisms RegionManager uses the RegionName to load content later. So, a region declaration based on
an ItemsControl looks like this:
<ItemsControl x:Name=MyRegion prism:RegionManager.RegionName=MyRegion />
Note that both names can be the same.
Step 1g Create a Bootstrapper: The bootstrapper initializes a Prism app. Most of the heavy lifting is done by the Prism library,
and the main task of the bootstrapper class created by the developer is to invoke this functionality. To set up the bootstrapper,
first create a class in the Shell project called Bootstrapper.cs. Update the class signature to inherit from the UnityBootstrapper
class.
A sample Bootstrapper class is included in the download file. The sample class includes the method overrides discussed
below.
Step 1h Override the CreateShell() method: Create an override for the CreateShell() method so that it looks like this:
protected override System.Windows.DependencyObject CreateShell()
{
/* The UnityBootstrapper base class will attach an instance
* of the RegionManager to the new ShellWindow. */
return new ShellWindow ();
}
This override creates a new ShellWindow and sets it as the Prism Shell.
Step 1i Override the InitializeShell() method: Create an override for the InitializeShell() method of the
UnityBootstrapper class. The override method will set the base class Shell property to the Shell window for the app.
The override should look like this:
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
2
App.Current.MainWindow.Show();
}
Step 1j Override the CreateModuleCatalog () method: This step assumes you want to use Module Discovery to populate
the module catalog, where application modules are simply placed in a specified folder, where Prism will discover and load them.
Create an override for the CreateModuleCatalog() method of the UnityBootstrapper class. The override method
should look like this:
protected override IModuleCatalog CreateModuleCatalog()
{
var moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = @".\Modules";
return moduleCatalog;
}
All we need to do is specify the folder where modules will be placed when compiled. Here, we specify a Modules subfolder in the
Shell project bin folder.
Step 1k Override the ConfigureRegionAdapterMappings() method: If your application uses any Region Adapters, create an
override for the ConfigureRegionAdapterMappings() method of the UnityBootstrapper class. Region
Adapters are used to extend Prism region capabilities to controls (such as the WPF Ribbon) that cannot support Prism regions
out-of-the-box. See Appendix E to the Developer's Guide to Microsoft Prism.
The override method should look like this:
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
// Call base method
var mappings = base.ConfigureRegionAdapterMappings();
if (mappings == null) return null;
// Add custom mappings
mappings.RegisterMapping(typeof(Ribbon),
ServiceLocator.Current.GetInstance<RibbonRegionAdapter>());
// Set return value
return mappings;
}
Step 1l Modify the App class: The App class represents the applicationit is called when the application starts. We will use
the App class to set up our Prism application, by configuring our logging framework and calling the Bootstrapper we
created in the preceding steps. So, open App.xaml.cs, and add the following override method to the class:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Configure Log4Net
XmlConfigurator.Configure();
// Configure Bootstrapper
var bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
The logger configuration call assumes we are using the log4net framework, and that it is being configured in an App.config file.
See the How-To document Configuring log4net for more information on configuring log4net in this manner.
Step 1m Modify App.xaml: WPF applications are normally initialized by the StartupUri attribute in App.xaml. This
attribute simply points to the main window as the window to load on startup. Since we are taking control of initialization in the
App class, we dont need the XAML attribute. So, open App.xaml and delete that attribute.
Step 1n Create a custom logger class: Create a new custom logger class, implementing ILoggerFacade, to enable Prism
to log using your logging framework. A sample custom logger is attached to this document as Appendix B. It enables Prism
logging with the log4net framework.
3
Step 1o Initialize the custom logger: In the application bootstrapper, add an override to the CreateLogger() method.
The override simply needs to create and return an instance of your custom logger, like this:
protected override Microsoft.Practices.Prism.Logging.ILoggerFacade CreateLogger()
{
return new Log4NetLogger();
}
View objects have to be registered with Unity using the overload shown below. By
default, Unity resolves view objects as type System.Object, which this overload
maps to the correct view type. See "Developer's Guide to Microsoft Prism" (Ver 4),
p. 120. */
6
TTo>():
TFrom: The first type parameter should always be System.Object, because this is how Prism will initially resolve the
View object.
TTo: The second type parameter should match the actual type of the View object. Unity will map the resolved object from
System.Object to this type.
View name: The method parameter specifies the name of the View to be registered, as a string value.
By default, Prism creates a new instance of the View object every time a resolution is requested. You can override this behavior,
and register a class as a singleton, by passing a second method parameter, in the form of a new
ContainerControlledLifetimeManager().
Note that there are several other overloads for IUnityContainer.RegisterType(), including an overload that allows
the developer to specify an interface or base class to resolve, and the concrete type to return when a resolution is requested.
Step 3c Implement the IRegionMemberLifetime interface: If the View should be unloaded when its host module is
deactivated, implement the IRegionMemberLifetime interface on either the View or its View Model. The interface
consists of a single property, KeepAlive. Setting this property to false will cause the View to be unloaded when the user
navigates to a different module. Here is sample code to implement the interface:
using Microsoft.Practices.Prism.Regions;
using Microsoft.Windows.Controls.Ribbon;
namespace Prism4Demo.ModuleA.Views
{
/// <summary>
/// Interaction logic for ModuleARibbonTab.xaml
/// </summary>
public partial class ModuleARibbonTab : RibbonTab, IRegionMemberLifetime
{
#region Constructor
public ModuleARibbonTab()
{
InitializeComponent();
}
#endregion
#region IRegionMemberLifetime Members
public bool KeepAlive
{
get { return false; }
}
#endregion
}
}
If KeepAlive will always have a value of false, then you can safely implement the interface on the View class and
7
hard-code the value (as shown above). However, if the value of the property will be changed at run-time, implement the
interface on the corresponding View Model instead, in order to avoid having the programs back end interacting directly with
the View.
Step 3d Add the ViewSortHint Attribute: If the View will be loaded into an ItemsControl region, or any other region
that holds multiple items, you may want to add the ViewSortHint attribute to the View. This attribute specifies the sort
order of the View when it is loaded into the region. Here is an example for an Outlook-style Task Button:
[ViewSortHint("01")]
public partial class ModuleATaskButton : UserControl
{
public ModuleATaskButton()
{
InitializeComponent();
}
}
Note that the control declared as a Prism region must support sorting for the ViewSortHint attribute to have any effect.
Conclusion
The preceding steps will produce the basic framework for a Prism application. There are a number of other elements involved in
8
a production app, such as a View Model for each View defined in the applications modules, and the back-end logic that
implements the applications use cases. You can find additional documentation for the demo app, as well as a discussion of
loosely-coupled communication between modules, in the companion article cited above.
As always, I welcome peer review by other CodeProject users. Please flag any errors you may find and add any suggestions for
this article in the Comments section at the end of the article. Thanks.
System.Windows;
Microsoft.Practices.Prism.Modularity;
Microsoft.Practices.Prism.Regions;
Microsoft.Practices.Prism.UnityExtensions;
Microsoft.Practices.ServiceLocation;
Microsoft.Windows.Controls.Ribbon;
Prism4Demo.Shell.Views;
PrismRibbonDemo;
namespace Prism4Demo.Shell
{
public class Bootstrapper : UnityBootstrapper
{
#region Method Overrides
/// <summary>
/// Populates the Module Catalog.
/// </summary>
/// <returns>A new Module Catalog.</returns>
/// <remarks>
/// This method uses the Module Discovery method
/// of populating the Module Catalog. It requires
/// a post-build event in each module to place
/// the module assembly in the module catalog
/// directory.
/// </remarks>
protected override IModuleCatalog CreateModuleCatalog()
{
var moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = @".\Modules";
return moduleCatalog;
}
/// <summary>
/// Configures the default region adapter
/// mappings to use in the application, in order
/// to adapt UI controls defined in XAML
/// to use a region and register it automatically.
/// </summary>
/// <returns>The RegionAdapterMappings instance
/// containing all the mappings.</returns>
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
// Call base method
var mappings = base.ConfigureRegionAdapterMappings();
if (mappings == null) return null;
// Add custom mappings
var ribbonRegionAdapter =
ServiceLocator.Current.GetInstance<RibbonRegionAdapter>();
mappings.RegisterMapping(typeof(Ribbon), ribbonRegionAdapter);
// Set return value
return mappings;
}
9
/// <summary>
/// Instantiates the Shell window.
/// </summary>
/// <returns>A new ShellWindow window.</returns>
protected override DependencyObject CreateShell()
{
/* This method sets the UnityBootstrapper.Shell property to the ShellWindow
* we declared elsewhere in this project.
* Note that the UnityBootstrapper base
* class will attach an instance
* of the RegionManager to the new Shell window. */
return new ShellWindow();
}
/// <summary>
/// Displays the Shell window to the user.
/// </summary>
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
#endregion
}
}
System;
Microsoft.Practices.Prism.Modularity;
Microsoft.Practices.Prism.Regions;
Microsoft.Practices.ServiceLocation;
Microsoft.Practices.Unity;
Prism4Demo.ModuleA.Views;
namespace Prism4Demo.ModuleA
{
/// <summary>
/// Initializer class for Module A of the Prism 4 Demo.
/// </summary>
public class ModuleA : IModule
{
#region IModule Members
/// <summary>
/// Initializes the module.
/// </summary>
public void Initialize()
{
/* We register always-available controls
* with the Prism Region Manager, and on-demand
* controls with the DI container.
* On-demand controls will be loaded when we invoke
* IRegionManager.RequestNavigate() to load the controls. */
// Register task button with Prism Region
var regionManager =
ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("TaskButtonRegion",
typeof(ModuleATaskButton));
10
/*
*
*
*
*
*
// Member variables
private readonly ILog m_Logger =
LogManager.GetLogger(typeof(Log4NetLogger));
#endregion
#region ILoggerFacade Members
/// <summary>
/// Writes a log message.
/// </summary>
/// <param name="message">The message to write.</param>
/// <param name="category">The message category.</param>
/// <param name="priority">Not used by Log4Net; pass Priority.None.</param>
public void Log(string message, Category category, Priority priority)
{
switch (category)
{
case Category.Debug:
m_Logger.Debug(message);
break;
case Category.Warn:
m_Logger.Warn(message);
break;
case Category.Exception:
m_Logger.Error(message);
break;
case Category.Info:
m_Logger.Info(message);
break;
}
11
}
#endregion
}
}
12