Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / WPF

Prism, WPF, and Unity based modular application step by step

Rate me:
Please Sign up or sign in to vote.
4.56/5 (30 votes)
2 Dec 2013CPOL5 min read 127.4K   62   17
Starting modular application development using Prism, WPF, and Unity container.

Introduction

This article explains some simple steps to create a modular application using WPF and Prism.

Background

Third party DexExpress WPF control is used in this sample UI application. However, even without using any third party UI control, the steps below can be used to develop a modular UI application with WPF and Prism and by using regular WPF controls. The purpose of this example is to show the basic steps to start a modular application using Prism/Unity and not the usage of third party controls.

This article was posted back in February 2013 in my personal blog:

Using the code

Here we will explore a few basic steps for how we can start a modular application from scratch using Prism, WPF, and Unity container. There are often needs for modular applications, especially for large scale application development projects where each module performing its own specific tasks can be developed independently and later seamlessly incorporated within the modular application shell through a container. This also provides the capability to switch modules of similar functionality seamless for the application to bring up the desired one as configured. This article will not go into the details of demonstrating the various aspects and benefits of a modular application. Rather, this will just show a few basic steps of how we can take advantage of Prism and define regions in our UI where each module can provide service to independent regions within an application.

Step 1

In Visual Studio 2010, start a new WPF project. Remove the MainWindow.xaml file that was automatically created. Remove StartupUri="MainWindow.xaml" from the App.xaml file. 

Add the following references:   

Microsoft.Practices.Prism
Microsoft.Practices.Prism.Unity
Microsoft.Practices.Prism.UnityExtensions
Microsoft.Practices.Prism.ServiceLocation
Microsoft.Practices.Prism.Interactivity 

Create a class called Bootstrapper.cs and derive this from the UnityBootstrapper class. Create the main shell window by adding a new WPF window called PrismAppShell.xaml. Implement the abstract method CreateShell in the BootStrapper class.

Override these two methods in the Bootstrapper class:

InitializeModules
ConfigureModuleCatalog 

Inside App.xaml.cs within the OnStartup method, instantiate the BootStrapper object and call its Run().

App.xaml
XML
<Application x:Class="Prism.ModuleExmaple.App"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
> 
<Application.Resources> 
</Application.Resources>
</Application>
App.xaml.cs
C#
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        BootStrapper bootstrapper = new BootStrapper();
        bootstrapper.Run(); 
    } 
}

Implement a bootstrapper class which inherits from the UnityBootStrapper class and override the following three protected methods:

Bootstrapper.cs
C#
public class BootStrapper : UnityBootstrapper
{
    protected override System.Windows.DependencyObject CreateShell()
    {
        return this.Container.Resolve<PrismAppShell>();
    }
    protected override void InitializeModules()
    {
        base.InitializeModules();
        App.Current.MainWindow = (PrismAppShell)this.Shell;
        App.Current.MainWindow.Show();
    }
    protected override void ConfigureModuleCatalog()
    {
        base.ConfigureModuleCatalog();
        this.ModuleCatalog.AddModule(null); // placeholder
    }
}

Create a shell window as show in the XAML below.

PrismAppShell.xaml
XML
<Window x:Class="Prism.ModuleExmaple.PrismAppShell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="PrismAppShell" Height="300" Width="300">
    <Grid>
    </Grid>
</Window>

After this much, if you run the application from Visual Studio, an empty shell window will come up.

Step 2

Now let us divide the UI into regions and mark them with region names by updating the main shell PrismAppShell.xaml file as follows:

XML
<Window x:Class="Prism.ModuleExmaple.PrismAppShell"
      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="PrismAppShell" Height="900" Width="1200">
    <Grid x:Name="LayoutRoot">
        <DockPanel LastChildFill="True" HorizontalAlignment="Stretch" 
                 Name="dockPanel1" VerticalAlignment="Stretch">
            <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" 
                    Background="#FFCCD4F8" Height="100">
                <ContentControl prism:RegionManager.RegionName="RibbonRegion"></ContentControl>
            </StackPanel>
            <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" Background="#FFD9E1EF" Height="100">
                <ContentControl prism:RegionManager.RegionName="StatusRegion"></ContentControl>
            </StackPanel>
            <ScrollViewer>
                <StackPanel Orientation="Vertical" DockPanel.Dock="Left" Background="#FF50576F" Width="200">
                    <ContentControl prism:RegionManager.RegionName="TreeRegion"></ContentControl>
                </StackPanel>
            </ScrollViewer>
            <StackPanel Orientation="Vertical" DockPanel.Dock="Right" Background="#FF677BA7" Width="100">
                <ContentControl prism:RegionManager.RegionName="AlertRegion"></ContentControl>
            </StackPanel>
            <ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFC0DBF2">
                    <ContentControl prism:RegionManager.RegionName="BlotterRegion" 
                        HorizontalAlignment="Stretch" VerticalAlignment="Stretch"></ContentControl>
                </StackPanel>
            </ScrollViewer>
        </DockPanel>
    </Grid>
</Window>

If you run this application at this point, it is will display the application like below. Notice the regions are now defined in the UI (so that when we create a few modules, different modules can hook up to different UI regions here for corresponding service).

Image 1

Step 3

Let us place some actual UI controls to replace the place holder stack panels (which we named for different UI regions).

In the shell XAML, we will use a ribbon control and define the RibbonRegion there. A TreeView control on the left for TreeRegion, a TabControl in the middle for BlotterRegion. Bottom and right (StatusRegion and AlertRegion). We will keep the region defined still within the content control of each of these new UI controls we will add.

XML
<Window x:Class="PrismApp.Shell.PrismAppShell"
    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="PrismAppShell" Height="900" Width="1200"
    xmlns:my="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
    >
    <Window.Resources>
        <DataTemplate x:Key="crap">
            <StackPanel Width="100" Height="18"></StackPanel>
        </DataTemplate>
        <Style x:Key="TabItemStyleKey" TargetType="{x:Type TabItem}">
            <Setter Property="Header" Value="{Binding Path=DataContext.ViewTile}"></Setter>
            <Setter Property="HeaderTemplate" Value="{Binding Source=    {StaticResource crap}}"></Setter>
        </Style>
        <Style x:Key="TreeItemStyleKey" TargetType="{x:Type TreeViewItem}">
            <Setter Property="Height" Value="Auto"></Setter>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Top" />
            <Setter Property="BorderThickness" Value="0" />
        </Style>
    </Window.Resources>
    <Grid x:Name="LayoutRoot">
        <DockPanel LastChildFill="True" HorizontalAlignment="Stretch" 
                Name="dockPanel1" VerticalAlignment="Stretch">
            <my:Ribbon prism:RegionManager.RegionName="RibbonRegion" 
                 DockPanel.Dock="Top" HorizontalAlignment="Stretch" 
                 VerticalAlignment="Stretch" Name="ribbon1" Height="150"/>
            <StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom" 
                     Background="#FFD9E1EF" Height="100">
                <ContentControl prism:RegionManager.RegionName="StatusRegion"></ContentControl>
            </StackPanel>
            <ScrollViewer>
                <StackPanel Orientation="Vertical" DockPanel.Dock="Left" 
                          MinWidth="150" MaxWidth="200">
                    <TreeView ItemContainerStyle="{StaticResource TreeItemStyleKey}"
                         prism:RegionManager.RegionName="TreeRegion"
                         Name="treeView1" HorizontalAlignment="Stretch"
                         VerticalAlignment="Stretch" BorderThickness="0"
                    />
                </StackPanel>
            </ScrollViewer>
            <StackPanel Orientation="Vertical" DockPanel.Dock="Right" 
                      Background="#FF677BA7" Width="100">
                <ContentControl prism:RegionManager.RegionName="AlertRegion"></ContentControl>
            </StackPanel>
            <ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <StackPanel HorizontalAlignment="Stretch" 
                          VerticalAlignment="Stretch" Background="#FFC0DBF2">
                    <TabControl ItemContainerStyle="{StaticResource TabItemStyleKey}"
                        prism:RegionManager.RegionName="BlotterRegion" Name="tabControl1"
                        HorizontalAlignment="Stretch" 
                        VerticalAlignment="Stretch" Width="Auto">
                    </TabControl>
                </StackPanel>
            </ScrollViewer>
        </DockPanel>
    </Grid>
</Window>

Step 4

Now let us create a few modules by implementing the IModule interface and register with regions. For example, we created a module called cash blotter module and register it in the following way:

C#
public class CashBlotterModule : IModule
{
    private readonly IRegionViewRegistry regionViewRegistry = null;
    public CashBlotterModule(IRegionViewRegistry regionViewRegistry)
    {
        this.regionViewRegistry = regionViewRegistry;
    }
    public void Initialize()
    {
        this.regionViewRegistry.RegisterViewWithRegion("BlotterRegion", typeof(CashBlotterView));
        this.regionViewRegistry.RegisterViewWithRegion("TreeRegion", typeof (CashItemsTreeView));
    }
}

We will just add new XAML files and the corresponding view model files for modules we are adding. Here is a snapshot of Solution Explorer with the skeleton module files that we are adding. We added a cash blotter module, a deriv blotter module, cash items tree module, deriv items tree module, common module, ticketing module, etc. Names of the modules may not seem realistic, however let us just call them so for the sake of this example.

Image 2

Step 5

We will now update the BootStrapper class to load modules as shown below. (Below, modules are just added in the module catalog for brevity of this example. However you can further load modules using the container and decouple them from the bootstrapper altogether for loading modules independently and as needed.)  

C#
public class BootStrapper : UnityBootstrapper
{
    protected override System.Windows.DependencyObject CreateShell()
    {
        return this.Container.Resolve<PrismAppShell>();
    }
    protected override void InitializeModules()
    {
        base.InitializeModules(); 
        App.Current.MainWindow = (PrismAppShell)this.Shell;
        App.Current.MainWindow.Show();
    }
    protected override void ConfigureModuleCatalog()
    {
        base.ConfigureModuleCatalog();
        ModuleCatalog moduleCatalog = (ModuleCatalog)this.ModuleCatalog;
        // Seems like as soon as you add a new module, internally seletor module
        // adopter (since blotter region is on tab control), automatically adds a tab item)
        moduleCatalog.AddModule(typeof(PrismApp.Module.Cash.Blotter.CashBlotterModule));
        moduleCatalog.AddModule(typeof(PrismApp.Module.Deriv.Blotter.DerivBlotterModule));
        //moduleCatalog.AddModule("CashBlotterModule", 
        //   "PrismApp.Module.Cash.Blotter.CashBlotterModule");
    }
}

In above code, inside ConfigureModuleCatalog the module loading will be upgraded in the following section.

When we run now, the skeleton application comes up like this:

Image 3

Step 6

  • Replaced the WPF DataGrid with DevExpress grid control, WPF Toolbox ribbon with DevExpress ribbon control. (Third-party DevExpress controls are not needed to accomplish this, you can use any UI control, including regular .NET provided WPF controls).
  • View Injection - did view injection from cash blotter module and ticket module and as well from derivatives blotter and ticket module. 
  • Injected views in desired Prism regions on the shell, i.e., on BlotterRegion, RibbonRegion, TreeRegions.  
  • Model - connected to database for fake trade data.  (Code sample for data is not shown here, you can set up your own data provider and retrieve data and hook up to blotter module to display.)
  • ViewModel - created a number of view model classes in order for the views to bind to fake data. 

After all the above steps in step 6, our skeleton app looks like the following. In the UI below, when "Cash Trading" tab is chosen, the cash blotter module will provide the necessary data and service, and same for the Derivative Trading tab will be serviced by the deriv blotter module. Same goes for tickets. Also, alert region on the UI (now shown empty) will be filled by the alert module, same way status region in the UI (now showing empty) will be filled with status messages from the status module. All these modules can be developed independent of the main application, can be independently built, tested, and configured.

Image 4

License

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


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNot fully explained Pin
Member 787462227-Feb-18 2:41
Member 787462227-Feb-18 2:41 
Questionplease source code Pin
Member 1179185019-Jun-17 4:04
Member 1179185019-Jun-17 4:04 
GeneralMy vote of 1 Pin
Member 1121276828-Apr-16 8:18
Member 1121276828-Apr-16 8:18 
Questionbuild issue Pin
fabriziodb@live.it20-Oct-15 5:19
fabriziodb@live.it20-Oct-15 5:19 
get error in "log?.Warn"

VB
Error   4   Syntax error, ':' expected  D:\Documenti\Visual Studio 2012\Projects\Prism\Calcium\calcium-107968\Trunk\Source\Core\Core\ServiceModel\ChannelManagerSingleton.cs    364 10  Outcoder.Calcium.Wpf

QuestionNeed Help with Step6 Pin
Member 1164977928-Apr-15 9:45
Member 1164977928-Apr-15 9:45 
QuestionSource Code Available Pin
Member 112140165-Jan-15 19:53
Member 112140165-Jan-15 19:53 
AnswerRe: Source Code Available Pin
Member 1164977928-Apr-15 9:46
Member 1164977928-Apr-15 9:46 
QuestionRibbon? Pin
BikingBlake17-Dec-14 15:41
BikingBlake17-Dec-14 15:41 
AnswerRe: Ribbon? Pin
Gerald Gomes17-Dec-14 17:48
Gerald Gomes17-Dec-14 17:48 
GeneralMy vote of 1 Pin
viler8411-Nov-14 14:59
viler8411-Nov-14 14:59 
Question2 Tabs of TabControl with similar data Pin
Member 1110726924-Sep-14 9:20
Member 1110726924-Sep-14 9:20 
GeneralMy vote of 2 Pin
rcarty17-Sep-14 5:21
rcarty17-Sep-14 5:21 
Questionthis.Container.Resolve<PrismAppShell>(); Pin
rcarty17-Sep-14 5:18
rcarty17-Sep-14 5:18 
QuestionDownload source code? Pin
Younes Cheikh28-Apr-14 22:13
Younes Cheikh28-Apr-14 22:13 
Questionsample code pls Pin
JasRaj Bishnoi15-Apr-14 0:52
JasRaj Bishnoi15-Apr-14 0:52 
QuestionModules and sample source code Pin
CreF5-Dec-13 21:29
professionalCreF5-Dec-13 21:29 

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

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