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

WPF: Showing you how to use PRISM in a very unlike PRISM way

Rate me:
Please Sign up or sign in to vote.
4.90/5 (28 votes)
22 Apr 2010CPOL11 min read 138.6K   2.1K   80   38
Showcasing how to use PRISM's regions outside of PRISM.

Introduction

It has been a while since I wrote anything about frameworks, and the MVVM pattern in particular. I have found myself yearning to get back into do a sizable project; as such, I am just about to embark on updating my Cinch MVVM framework to bring it in line with the forthcoming .NET 4.0 / VS2010 release. However, just before I started work on that, I wanted to show just how easy it is to extend Cinch, to include certain areas of PRISM/CAL, in particular PRISM/CALs region support, as this is one area that PRISM/CAL has that Cinch does not.

PRISM Smorgasbord (Never Thought I Would Find a Need for That Word)

Now, I do not know how many of you have used PRISM/CAL, or have worked with similar code such as the Smart Client Software Factory, which although quite different to PRISM/CAL, still has some similarities.

Anyway, that is by the by; let's concentrate on what PRISM/CAL can do for us.

PRISM/CAL is billed as being "Composite WPF and Silverlight". So as such, it offers developers the ability to create composite UIs which get constructed into one UI application. So how does PRISM/CAL do this? Well, it does have a fairly good set of arsenal that aids in this, such as:

Shell

There is typically a single Window which acts as the application shell. This is actually a pretty common approach to doing WPF apps, and has been for quite some time. Some of the best WPF apps are all single Window applications; just look at Expression Blend:

Image 1

That is a single Window application. Anyway, within PRISM/CAL, the shell is only really responsible for creating regions.

BootStrapper

The bootstrapper is responsible for loading up all the disparate (and seemingly unconnected) modules such that they can be used within a single shell Window. This typically also creates the shell window and runs it.

Modules

PRISM/CAL offers things called modules to aid in the well, er, modularity of building a composite system. Each module can have various classes, such as perhaps a View (typically UserControl), and maybe a ViewModel. These modules are understood as units of code that the PRISM/CAL core code knows what to do with in order to get them to be compiled such that they can be used within the main application shell.

Event Aggregator

Is a disconnected messaging system that uses WeakReference objects such that the sender/receiver are free to be GCd. The Event Aggregator also offers the ability to have sent messages to be invoked on the UI thread. You can think of this as a communication channel where the sender and receiver do not know about each other but rather go through some middle man. This is conceptually very similar to using the Mediator pattern, which is what my own Cinch MVVM framework uses.

Dependency Injection

PRISM/CAL makes very heavy use of DI/IOC, and it does this by using the Unity application block, which is Microsoft's own IOC container. It works quite like most other IOC containers more or less; OK, there are some that have less syntax to get to grips with, but conceptually, they all work roughly the same, they are all able to store Types and resolve Type dependencies by injecting the required Types in at constructor/property level, and they can all manage object instance lifecycles (singleton, new instance, etc.).

Basically, all we really care about is that PRISM/CAL is using DI/IOC to resolve various Types.

Region Support

The last piece in the PRISM/CAL puzzle is region support. Some of you may know what this is while others may not; well, put simply, regions are a control or area of the screen which will accept content to be added to them. PRISM/CAL comes with a very handy way of working with regions; all that you are required to do is use an attached property on one of the supported region controls, which are the following controls as far as I know:

  • ContentControl
  • Grid
  • StackPanel
  • TabControl

There may be more that I have missed off, apologies.

Here is an example of how to setup a region on a control that is part of the shell's XAML:

XML
<TabControl cal:RegionManager.RegionName="MainRegion"/>

Now you are able to dynamically add new content into the TabControl using a very easy to use syntax; you can do something like this:

C#
View1 view = new View1();
regionManager.RegisterViewWithRegion("Someregion", () => view);
regionManager.Regions["Someregion"].Activate(view);

RegionAdaptors

Going slightly off subject here, but just for your information, if your UI requirements warrant slightly more complex controls that you want to use as regions, then do not worry, you can also do that by the use of a custom RegionAdaptor.

You can find examples of this at various places, but all you really have to do is inherit from RegionAdapterBase<T> and override the Adapt() and CreateRegion() methods, and override the ConfigureRegionAdapterMappings() method in the bootStrapper class.

John Papa has a good example of how to create a custom RegionAdaptor, which you can find at this URL: Fill My region Please, or for a slightly more adventurous RegionAdaptor, have a look at this one: WindowRegionAdapter for CompositeWPF.

Take What You Want

Now, some of you may be thinking, yeah so what Sacha, you have not told me much, or even shown any code yet. Well, this is a very small article, but I did not want to get into any code until I could quickly go into PRISM/CAL a bit so at least those that have not used it would get a very, very quick run down on how it works.

I have to say, I am not a massive fan of PRISM/CAL, but I really, really like the region support. I do not know how many of you have read my Cinch MVVM framework articles; heck, some of you may even be using my Cinch MVVM framework I guess. Well, over the past months (since I let the framework out there, I have been contacted by various people talking about PRISM/CAL's excellent region support, and I tell you what, I agree, I love PRISM/CAL's region stuff, it is just that I am not that keen on the rest; don't get me wrong, the P&P folks are smart hombres, it is just a bit too constraining for me at times; you have to do things a certain way. Or do you?).

Well, actually, no, you don't. You see, the other great thing about PRISM/CAL is that you can just use the bits you want to use and leave the rest. I like regions and do not care much for the rest, so the rest of this article will be dedicated to showing you how to use PRISM/CAL with another MVVM framework. Naturally, I will be using my own Cinch MVVM framework, but where it's appropriate, I will state an alternative way of doing something if you do not wish to use my Cinch MVVM framework.

OK, so enough banging on about it; let's continue to see how I have made my Cinch MVVM framework be able to work with RISM/CAL's excellent region support.

So How Can We Use Regions in Another MVVM Framework

It is actually not that hard to get region support to work outside PRISM/CAL. At least, it was not that hard for me to get it to work with my Cinch MVVM framework. All I had to do was follows these easy steps:

Step 1: Make sure I had all the required references in place

I simply had to make sure that the attached demo project had the correct assembly references, which for me equated to this:

Image 2

As I say, the demo app is written for use with my Cinch MVVM framework, so if you are not using that, you will not have to add any references for the assemblies in the Cinch folder.

Step 2 : Project Structure

As I stated above, PRISM/CAL uses a bootStrapper and also normally works with modules. The bootStrapper is not optional, as it sets up the Unity IOC container. However, modules are actually optional; we do not have to use modules at all, but we do have to override the GetModuleCatalog() method to state that there will, in fact, be no modules to load into the shell.

One thing I should mention is that the demo app included with this article obviously assumes that all files are part of the same project. The project structure for the attached demo project looks like this:

Image 3

If your project requires things a little different from this, such as ViewModels in a separate assembly, just make sure that all your own projects have the correct references, as outlined in the previous subsection.

Step 3: The BootStrapper

As I stated earlier on, PRISM/CAL uses a bootstrapper file to ensure various things such as the shell are setup. We too must do that if we wish to use any of the PRISM/CAL bits and pieces in our own apps. Here is the complete code for the modified bootStrapper file:

C#
using System.Windows;
using Microsoft.Practices.Composite.Modularity;
using Microsoft.Practices.Composite.UnityExtensions;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

namespace CinchAndPrismRegions
{
    class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            #region Setup Cinch with PRISM/CAL supporting service

            //********************************************************************
            //METHOD 1 : 
            //
            //Add ViewManager to Cinch directly, to allow it to use 
            //PRISM/CAL regions. This is prefferred method
            //********************************************************************
            IRegionManager regionManager = base.Container.Resolve<IRegionManager>();
            Cinch.ViewModelBase.ServiceProvider.Add(typeof(IViewManager),
                new ViewManager(regionManager));


            //********************************************************************
            //METHOD 2 : 
            //
            //Add ViewManager to Cinch via Unity, to allow it to use 
            //PRISM/CAL regions. This is strictly not really required, as all it does
            //is make the Unity IOC container aware of the IViewManager service. However
            //any interaction with the IViewManager and Cinch will be via the 
            //Cinch ViewModelBase.ServiceProvider and Cinch ViewModelBase.Resolve<T>() 
            //methods.
            //
            //I just thought it good practice to add it to Unity, on the off chance
            //that it may be used elsewhere. Though as I say with Cinch this is very 
            //unlikely
            //********************************************************************
            
            //base.Container.RegisterType<IViewManager,ViewManager>(
            //     new InjectionConstructor(base.Container.Resolve<IRegionManager>())); 

            //var viewManager = base.Container.Resolve<IViewManager>();
            //Cinch.ViewModelBase.ServiceProvider.Add(typeof(IViewManager), viewManager);

            #endregion

            //Create CAL shell
            //NOTE : Shell is without modules, as I want it to be a 
            //bulk standard MVVM app just with added
            //regionManager support)
            Shell shell = new Shell();
            shell.Show();
            return shell;
        }

        protected override IModuleCatalog GetModuleCatalog()
        {
            ModuleCatalog catalog = new ModuleCatalog();
            return catalog;
        }
    }
}

From this code, there are really only two important things to observe, which are:

  1. That we are creating (or creating and storing within the Unity IOC container, in case the IViewManager service is needed elsewhere, strictly, this is not required, but I just thought I would show you how to add services to the Unity IOC container) an instance of the IViewManager (which is a service for working with the regions that I have written). This IViewManager expects a IRegionManager as a constructor parameter. The IRegionManager service is a PRISM/CAL Type, which is available from the Unity IOC container, so you can see in the code above that IRegionManager is being injected into the IViewManager, by obtaining an instance of the IRegionManager service from Unity. You will also note that the IViewManager service is then stored within the Cinch ViewModelBase ServiceProvider instance. This allows all Cinch ViewModels to make use of the IViewManager service. This step is specific to Cinch. So if you not using Cinch, you could simply store the IViewManager service in a singleton somewhere, or even on a property within a base ViewModel class, basically just somewhere from where you can use it easily.
  2. The next step is to tell PRISM/CAL that we do not really want to work with modules. We do this by overriding the GetModuleCatalog() method.

Step 4: Creating a Region Supporting UI Service

So if we want to work with regions, we should really be looking to create a reusable object that we can use all over the place. This is the job of services typically. So I went about creating a simple Region supporting service which I have called ViewManager, which simply looks like this:

Service Definition
C#
/// <summary>
/// A simple region service contract
/// that works with PRISM/CALs regions
/// </summary>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CinchAndPrismRegions
{
    public interface IViewManager
    {
        void CreateAndShowViewInRegion(String regionName, Type viewType);
        void ShowViewInRegion(String regionName, Object viewInstance);
    }
}
Service Implementation

And here is a minimal service that works with PRISM/CAL regions:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Composite.Regions;
using Microsoft.Practices.Unity;

namespace CinchAndPrismRegions
{
    /// <summary>
    /// A simple region supporting UI service
    /// that works with PRISM/CALs regions
    /// </summary>
    public sealed class ViewManager : IViewManager
    {
        private IRegionManager regionManager;


        public ViewManager(IRegionManager regionManager)
        {
            this.regionManager = regionManager;
        }

        /// <summary>
        /// Creates and shows a view in the region specified by the regionName
        /// input parameter
        /// </summary>
        /// <param name="regionName">The region name to put view in</param>
        /// <param name="viewType">The type of the view to create</param>
        public void CreateAndShowViewInRegion(String regionName, Type viewType)
        {
            var content = Activator.CreateInstance(viewType);
            regionManager.RegisterViewWithRegion(regionName, () => content);
            regionManager.Regions[regionName].Activate(content);
        }

        /// <summary>
        /// Shows the view instance in the region specified by the regionName
        /// input parameter
        /// </summary>
        /// <param name="regionName">The region name to put view in</param>
        /// <param name="viewInstance">The instance of the view to create</param>
        public void ShowViewInRegion(String regionName, Object viewInstance)
        {
            regionManager.RegisterViewWithRegion(regionName, () => viewInstance);
            regionManager.Regions[regionName].Activate(viewInstance);
        }

    }
}

And that is about all you have to know to use PRISM/CAL with your own MVVM framework; obvioulsy, I used Cinch, which already supported UI services, so that is the only thing you will have to watch if you are using your own MVVM framework.

So How About A Small Demo Then?

What use is some code without a small demo? To this end, I have created a small demo app that allows the following:

  • There is a single main Window (the shell) which hosts one of the standard PRISM/CAL region enabled controls, a TabControl. The shell Window makes use of a small ViewModel called ShellViewModel.
  • That there are two very simple Views that are shown in the shells region. The showing of these views in the shell is done by code in the ShellViewModel.

Here is all the XAML code for the Shell Window:

XML
<Window x:Class="CinchAndPrismRegions.Shell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="http://www.codeplex.com/CompositeWPF"
    Title="Hello World" Height="300" Width="300">
    
    
    <Window.Resources>
        <Style TargetType="{x:Type TabItem}" x:Key="TabItemRegionStyle">
            <Setter Property="Header" 
                    Value="{Binding RelativeSource={RelativeSource Self}, 
                Path=Content.DataContext.HeaderText}" />
        </Style>
    </Window.Resources>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <Button  Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                Content="Show View1" Command="{Binding ShowView1InRegionCommand}"/>
            <Button Margin="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                Content="Show View2" Command="{Binding ShowView2InRegionCommand}"/>
        </StackPanel>
        <TabControl Grid.Row="1" Name="MainRegion" cal:RegionManager.RegionName="MainRegion"
                    ItemContainerStyle="{StaticResource TabItemRegionStyle}"/>
    </Grid>
</Window>

There are two things to not here:

  1. The attached RegionManager property, which means the TabControl that uses this attached property can now be used with PRISM/CAL's regions.
  2. That the TabControl uses a ItemContainerStyle. This ItemContainerStyle simply shows a piece of text which is the current View name which is fetched from the active View's ViewModel.

OK, so that is the shell's XAML; what about the ViewModel that makes use of the regions? Well, here it is in its entirety:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Cinch;

namespace CinchAndPrismRegions
{
    public class ShellViewModel : Cinch.ViewModelBase
    {
        private SimpleCommand showView1InRegionCommand;
        private SimpleCommand showView2InRegionCommand;

        public ShellViewModel()
        {
            showView1InRegionCommand = new SimpleCommand
            {
                CanExecuteDelegate = x => true,
                ExecuteDelegate = x => ExecuteShowView1InRegionCommand()
            };

            showView2InRegionCommand = new SimpleCommand
            {
                CanExecuteDelegate = x => true,
                ExecuteDelegate = x => ExecuteShowView2InRegionCommand()
            };
        }


        public SimpleCommand ShowView1InRegionCommand
        {
            get { return showView1InRegionCommand; }
        }

        public SimpleCommand ShowView2InRegionCommand
        {
            get { return showView2InRegionCommand; }
        }


        private void ExecuteShowView1InRegionCommand()
        {
            this.Resolve<IViewManager>().CreateAndShowViewInRegion(
                "MainRegion", typeof(View1));
        }

        private void ExecuteShowView2InRegionCommand()
        {
            this.Resolve<IViewManager>().ShowViewInRegion(
                "MainRegion", new View2());
        }
    }
}

And just for completeness, here is the code for one of the ViewModels:

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Cinch;
using System.ComponentModel;

namespace CinchAndPrismRegions
{
    public class View1ViewModel : Cinch.ViewModelBase
    {
        private String headerText = String.Empty;

        public View1ViewModel()
        {
            HeaderText = "View1";
        }

        /// <summary>
        /// Bound Type for search
        /// </summary>
        static PropertyChangedEventArgs headerTextChangeArgs =
            ObservableHelper.CreateArgs<View1ViewModel>(x => x.HeaderText);

        public String HeaderText
        {
            get { return headerText; }
            set
            {
                headerText = value;
                NotifyPropertyChanged(headerTextChangeArgs);
            }
        }
    }
}

And here is the end result; we can click one of the buttons (which will call the ICommands in the ShellViewModel), and a new View will be shown in the Shell Window's "mainRegion", which if you recall was the TabControl.

Image 4

That's It

Anyway, that is all I wanted to say for now; but as I eluded to in the introduction, I am just about to embark on some big updates to my Cinch MVVM framework to bring it in line with the forthcoming .NET 4.0 / VS2010 release.

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 Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
QuestionCinch sourcecode Pin
girijaa21-Jun-15 21:55
professionalgirijaa21-Jun-15 21:55 
QuestionSimple and Impressive Pin
Khaled Kokah25-Apr-13 4:05
Khaled Kokah25-Apr-13 4:05 
GeneralMy vote of 5 Pin
prasad0219-Dec-10 21:51
prasad0219-Dec-10 21:51 
GeneralCinch V2 or/and Prism v4 Pin
Romain Jestin9-Dec-10 3:24
Romain Jestin9-Dec-10 3:24 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber9-Dec-10 4:18
Sacha Barber9-Dec-10 4:18 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Romain Jestin21-Dec-10 6:44
Romain Jestin21-Dec-10 6:44 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Romain Jestin21-Dec-10 8:46
Romain Jestin21-Dec-10 8:46 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber21-Dec-10 10:21
Sacha Barber21-Dec-10 10:21 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber22-Dec-10 0:51
Sacha Barber22-Dec-10 0:51 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Romain Jestin22-Dec-10 0:55
Romain Jestin22-Dec-10 0:55 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber22-Dec-10 1:30
Sacha Barber22-Dec-10 1:30 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber22-Dec-10 1:31
Sacha Barber22-Dec-10 1:31 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber22-Dec-10 2:38
Sacha Barber22-Dec-10 2:38 
GeneralRe: Cinch V2 or/and Prism v4 Pin
Sacha Barber21-Dec-10 10:23
Sacha Barber21-Dec-10 10:23 
GeneralDamn! Pin
Jammer6-May-10 1:55
Jammer6-May-10 1:55 
GeneralRe: Damn! Pin
Sacha Barber6-May-10 2:17
Sacha Barber6-May-10 2:17 
Cool let me know when its available
Sacha Barber
  • Microsoft Visual C# MVP 2008-2010
  • Codeproject MVP 2008-2010
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue

My Blog : sachabarber.net

GeneralRe: Damn! Pin
Jammer6-May-10 3:06
Jammer6-May-10 3:06 
GeneralRe: Damn! Pin
Sacha Barber6-May-10 3:57
Sacha Barber6-May-10 3:57 
GeneralWhy do we need Cinch ? [modified] Pin
Member 456543323-Apr-10 9:56
Member 456543323-Apr-10 9:56 
GeneralRe: Why do we need Cinch ? Pin
Sacha Barber23-Apr-10 10:44
Sacha Barber23-Apr-10 10:44 
GeneralRe: Why do we need Cinch ? Pin
Member 456543323-Apr-10 22:43
Member 456543323-Apr-10 22:43 
GeneralRe: Why do we need Cinch ? Pin
Sacha Barber23-Apr-10 22:58
Sacha Barber23-Apr-10 22:58 
GeneralRe: Why do we need Cinch ? Pin
Member 456543323-Apr-10 23:18
Member 456543323-Apr-10 23:18 
GeneralRe: Why do we need Cinch ? Pin
Sacha Barber23-Apr-10 23:48
Sacha Barber23-Apr-10 23:48 
GeneralGreat!!! Pin
CreF22-Apr-10 22:29
professionalCreF22-Apr-10 22: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.