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

Layout Manager for Prism v2

By , 24 Apr 2009
 
diagram.jpg

Introduction

One of the issues you may encounter when working on a Prism project is the management of regions and views within your application. While the RegionManager does an adequate job of managing regions, the orchestration of views and regions is pretty much left up to the developer.

A common approach is to define string constants in a common infrastructure assembly and injecting views into regions using these constants. This gets the job done, but adds rigidity to your application. For applications which require multiple layouts, coordinating regions and views can be a bit tedious.

One common approach I would not recommend is injecting your views in your module's Initialize method.

public void Initialize()
{
var view = new MyView();
_Container.RegisterInstance<IMyView>(view);
_RegionManager.Regions[RegionNames.Shell].Add(view);
}

This violates the encapsulation of the module, restricting the reuse of the module.

Background

On one project, we opted to create a "layout module." The sole purpose of this module was to load a layout UserControl into the Shell region of the main application window, and injecting the views into its own defined regions. Definitely a step in the right direction by decoupling the module views from the regions. The layout module was defined and loaded like any other module, but had to be the last module loaded due to its dependencies. One drawback to this approach was the increasing number of dependencies. The layout module had to reference all the infrastructure assemblies of the views it was required to manage.

Still this solution felt a bit too purpose-built. And other issues quickly arose, such as multiple layout support.

Ideally we were looking for a complete decoupling of regions and views with the ability to dynamically load layouts as required.

We quite liked the idea of using "layout views", views whose sole purpose was to define regions, and providing no business or UI logic. But, the source and introduction of these views needed to be dynamic and flexible.

Using the Code

The LayoutManager is my first attempt at tackling this issue. Its purpose is to dynamically manage one or more layout configurations for a Prism application.

To compile and run the LayoutManager you will need Visual Studio 2008 SP1 and the latest version of the Composite Application Guidance for WPF and Silverlight - February 2009.

The solution is fairly standard Prism solution, consisting of an Infrastructure, Shell and Modules projects. For the sake of simplicity, I've only included a single Modules project, where normally there would be more.

solution.jpg

classDiagram.jpg

The LayoutManager maintains a collection of Layout objects, which define layout controls, along with the views that will reside in the layout.

Configuration

The LayoutManager is configured by a LayoutProvider specified in your app.config file.

<section name="layoutProvider" type="Composite.Layout.Configuration.LayoutProviderSection, Composite.Layout"/>

Currently, two providers are available: ConfigLayoutProvider and XamlLayoutProvider. Custom providers can be used by inheriting from LayoutProviderBase.

ConfigLayoutProvider

Defines the LayoutManager in the app.config file as shown below:

<layoutProvider name="ConfigLayoutProvider" 
    type="Composite.Layout.Configuration.ConfigLayoutProvider, Composite.Layout">
    <layoutManager shellName="Shell" >

      <layouts>
        <layout name="FirstLayout" 
              filename="Layouts\FirstLayout.xaml" 
              fullname="First Layout" 
              isDefault="True"
              description="This is the default layout" 
              thumbnailSource="pack://application:,,,/LayoutManager.Infrastructure;
              component/Resources/Images/layout1.png">

          <views>
            <view typeName="LayoutManager.Infrastructure.IViewA,
                LayoutManager.Infrastructure" regionName="Left"  />
            <view typeName="LayoutManager.Infrastructure.IViewB,
                LayoutManager.Infrastructure" regionName="Right" />

            <viewModel typeName="LayoutManager.Infrastructure.IMenuViewModel,
                LayoutManager.Infrastructure" regionName="Menu"  viewProperty="View"/>
          </views>
        </layout>

        ...
          </layouts>
    </layoutManager>
  </layoutProvider>
XamlLayoutProvider

Defines the LayoutManager in Xaml

<layoutProvider name="XamlLayoutProvider"

           type="Composite.Layout.Configuration.XamlLayoutProvider, Composite.Layout"
           filename="Layouts\LayoutConfiguration.xaml"/>

The source of the Xaml can be specified by type or by filename.

<Layout:LayoutManager xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

                      xmlns:Layout=
                          "clr-namespace:Composite.Layout;assembly=Composite.Layout"
                      xmlns:Infrastructure=
    "clr-namespace:LayoutManager.Infrastructure;assembly=LayoutManager.Infrastructure"
                      ShellName="Shell">
    <Layout:LayoutManager.Layouts>
        <Layout:Layout x:Name="FirstLayout"

                       Fullname="First Layout"
                       Filename="Layouts\FirstLayout.xaml"
                       Description="This is the default layout"
                       ThumbnailSource=
"pack://application:,,,/LayoutManager.Infrastructure;component/Resources/Images/layout1.png"
                       IsDefault="True">

            <Layout:Layout.Views>
                <Layout:ViewModel RegionName="Menu"
                                  Type="{x:Type Infrastructure:IMenuViewModel}"
                                  ViewProperty="View" />

                <Layout:View RegionName="Left"
                             Type="{x:Type Infrastructure:IViewA}" />
                <Layout:View RegionName="Right"
                             Type="{x:Type Infrastructure:IViewB}" />

            </Layout:Layout.Views>
        </Layout:Layout>
	...
    </Layout:LayoutManager.Layouts>
</Layout:LayoutManager>

Each Layout contains a Views collection. The views collection accommodates both Views and ViewModels. The View specifies what view control is to be loaded and what region it is to be placed in. You can also set the visibility for the view. Use the ViewProperty of the ViewModel to specify the name of the property on your ViewModel which holds the View.

The LayoutManager is loaded after all of the modules have initialized. In the Bootstrapper.cs:

        protected override void InitializeModules()
        {
            base.InitializeModules();
            InitializeLayoutManager();
        }

        private void InitializeLayoutManager()
        {
            var layoutManager = LayoutConfigurationManager.LayoutManager;
            layoutManager.Initialize(Container);
            Container.RegisterInstance(layoutManager,
                new ContainerControlledLifetimeManager());
            //parameterless LoadLayout loads the default Layout into the Shell
            layoutManager.LoadLayout();
        }

The LayoutManager requires use of the Container. Once your layouts have been loaded, call the Initialize method passing in the container.

Once that is done, you can register the LayoutManager in the container making it accessible to other modules.

Loading a Layout

Layouts are loaded by calling the LoadLayout method of the LayoutManager.

  • LoadLayout() - loads the default layout in the Shell
  • LoadLayout(string layoutName) - loads the named layout in the Shell

The MenuViewModel.cs illustrates the use of LoadLayout:

private void LayoutCommandExecute(ILayout layout)
{
var layoutManager = _Container.Resolve<ILayoutManager>(); 
layoutManager.LoadLayout(layout.Name);
}

The basic sequence of loading a layout is:

  1. If there is a current layout, remove it from the RegionManager.
  2. Clear out any controls that were bound to any regions. This step is necessary otherwise you will get an InvalidOperationException ("This control is being associated with a region, but the control is already bound to something else.") when you try to reload it in the future. Currently, the LayoutManager only supports ItemsControls, ContentControls and Panels using the RegionManager.RegionName attached property.
  3. Add the new Layout Control to the RegionManager.
  4. Register any Regions contained within the Layout Control.
  5. Load any views associated with the new layout.

Events

There are several events raised by the LayoutManager:

  • LayoutManagerInitializedEvent - raised at the end of Initialize (see MenuViewModel.cs for an example of subscribing to this event)
  • LayoutLoadingEvent - raised at the beginning of LoadLayout
  • LayoutLoadedEvent - raised at the end of LoadLayout
  • LayoutUnloadingEvent - raised before the current layout is about to be unloaded
  • LayoutUnloadedEvent - raised after the current layout has been unloaded

All of these events are published through the EventAggregator.

Limitations

Currently there are several limitations with the LayoutManager, these are:

LayoutManager currently only supports UserControls as layout controls. There is also the basic assumption that your application main window has a single region defined, where layout controls are injected. Regions must be defined in XAML using the RegionManager.RegionName attached property.

Other Considerations

While the LayoutManager does decouple the regions from the views, it does not entirely do away with string-based region names. Dynamic manipulation of regions and views in code will still rely on region names (see the AddCommandExecute method in MenuViewModel.cs on how to programmatically add layouts). And region name attributes must match actual region names in the Layout control.

A possible approach to addressing this dependency may be to introduce a RegionType enumeration such as Top, Bottom, Left, Right, Center, StatusBar, Menu, Toolbar, etc. In which case, the LayoutManager could resolve these regions regardless of string names.

I have not tested the LayoutManager in all possible scenarios, such as nested layouts and custom RegionAdapters, or with Silverlight.

I am interested in feedback/comments/suggestions from the Prism community.

Points of Interest

For a good introduction to using Prism and WPF check out:

History

  • 13th April, 2009: Initial post
  • 23rd April, 2009: Refactored the Composite Layout assembly, including change of namespace. LayoutManager, Layout, View and ViewModel are now DependencyObjects. Decoupled layout configuration from the app.config file, allowing for flexible configuration. Added ConfigLayoutProvider and XamlLayoutProvider. Add token views and custom menus.

License

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

About the Author

Ron Gramann
Software Developer
United Kingdom United Kingdom
I'm a software developer living in West Sussex, UK, and enjoy working wth .Net 3.5 technologies.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalquestionmembershayderi14-Jul-10 3:57 
Hi there,
seems like a nice solution..
My question is:
What if you have two view's of the same type that supposed
to be located at the same region (as tab items for instance)??
I mean that you can specify a view name instead of view type
(at the app.config file..) and at the application level you need to set
every view with a name (Not a big disadvantage for my opinion)...
GeneralRe: questionmemberRon Gramann16-Jul-10 22:31 
Good question. I've never tried that scenario. If it is an all or nothing proposition, I suppose you could write an extension method which returns the "name" if one is supplied or type if not? This would be easy enough to verify, just register two views, one by name, the other by type and see what happens.
GeneralWindow ManagermemberChristianR192-May-10 8:08 
Hi,
I tried your solution, and I really liked it. Saved me the time to start from scratch.
I added some code, like a "code only" layout definition, and some other small things.
 
If you are interested, if there is a SVN version, I could merge the changes. But until now it is more or less cosmetic stuff.
 
But now I am facing the next "problem". A layout for a single window works fine. But I have two monitors and plenty of space on my screens. I want to support multiple windows, so basically I need a higher "WindowManager" which manages layouts in different windows.
 
So instead of firing up a single Shell, I might have a MainWindow and a ToolboxWindow.
Layouts may be assigned to windows, or manually loaded into them.
 
It is very similar to the LayoutManger.
 
Have you already had some ideas in this direction? Are you currently planning on improving this project. If so, let me know, perhaps we could work together and create some nice module for PRISM.
 
(The layouting stuff is, in my opinion, a little bit cumbersome in PRISM. I am not sure, perhaps I am "overgeneralizing" stuff, and most people simply use Blend and XAML, but I want a Window-Manager Smile | :) Also to deal with issues arising when porting stuff to mobile or Silverlight (not currently, but that will be the next domain I tackle)..
 
Chris
GeneralRe: Window ManagermemberRon Gramann4-May-10 4:27 
Hi Christian,
 
I would be interested in seeing what you've done. Unfortunately, I do not have a SVN version, but wouldn't rule it out in the future.
 
Regarding the WindowManager, I think its a good idea. I haven't had that requirement, but I do see the value in it. You can send me an email to discuss - ron at gramann dot co dot uk.
 
Re: Blend - I think Blend is fine for visually designing layouts, but it does not address the mechanics of managing them; same with windows.
 
You should write a CodeProject article on the WindowsManager.
 
Cheers
 
Ron
GeneralRe: Window ManagermemberMohammed Abed24-May-10 21:23 
Hi there,
 
Actually i am also interested in the WindowManager, it would be nice to have a layout for every window in the application to support multiple monitors, I use WindowRegionAdapter to populate multiple windows, each window will have its own regions according to it's layout. did you reach any ground regarding WindowManager or shall i start implementing it?
GeneralRe: Window ManagermemberRon Gramann24-May-10 23:01 
Hi Mo,
 
Best to contact Christian. He was planning on setting up a CodePlex project to address Window and Region Management in Prism. Also, you may want to join a new community for design patterns and Prism run by kfrosty - DotNetPatterns.
It would be nice to see this area formalised into a standard extension. Unfortunately, I'm committed to other work at the moment.
 
Cheers
 
Ron
GeneralThanks for the helpful samplememberkfrosty12-Mar-10 3:43 
Thanks Ron.
 
I just started a Prism/MVVM community for people to gather in a central place to post info and ask questions.
 
I just started a thread and linked to your helpful sample. I'll also be posting a sample of how to dynamically change your regions with templates and display views from different modules.
 
Links to Prism samples demonstrating dynamic regions
GeneralRe: Thanks for the helpful samplememberRon Gramann17-Mar-10 9:51 
Thanks kfrosty,
 
Will check out your new community and will try to contribute, provided I get some time Smile | :)
 
Cheers
 
Ron
GeneralUI Controllermemberdakash28-Apr-09 3:50 
Hello Ron.
I'm starting to develop a complicated application using WPF and Prism.
I read a lot about the RegionManager and yet something was missing - the UI controller.
I think that the Layout Manager you present solves 50% of the problem, but still I think something is missing.
Think about a mobile phone application, where you have the main window ("home"), and you browse to sub menus. Each sub menu can be expanded to couple of other menus. The user is able to press the "home" key at any state - and go back "home".
Your solution gives the ability to manage the various types of layouts, but who will orchestrate them?
Should I develop my own UI controller that will manage the application's state machine?
Do you have any other suggestion?
 
Thanks.
Dakash
GeneralRe: UI ControllermemberRon Gramann28-Apr-09 4:24 
Hi Dakash,
Regarding the use of a UI Controller, I think it depends on your requirements. If your UI needs to change based on a set of business rules, then I think a controller is in order.
I prefer to use markup for appearance and layout whenever possible. Displaying a view/views based on user action, application state or business rules sounds like controller code.
BTW, I recently updated this article, but it looks like the source code has not been updated.
 
Cheers
 
Ron
GeneralRe: UI Controller [modified]memberdakash28-Apr-09 5:14 
Thanks Ron.
My UI has flows similar to mobile phone. I was thinking using the WPF's NavigationService (http://msdn.microsoft.com/en-us/library/ms750478.aspx) but it means that all my XAMLs should be "Page" and not "Window".
Do you know if there's a design pattern for UI controller?
Am I missing something here? Maybe that functionality is redundant when using the WPF technology?
 
And by the way, can you send me the updated source code?
 
Thanks.
Dakash
 
modified on Tuesday, April 28, 2009 11:33 AM

GeneralRe: UI ControllermemberRon Gramann28-Apr-09 7:52 
Hi Dakash,
I don't know of a specific pattern for a UI Controller (other than MVC). Are you looking for some application plumbing for the Shell? If so, check out some of the Composite Contrib projects on CodePlex. There is also a Shell project which adds a lot of this kind of functionality.
 
rg
(PS. Source code on the way)
GeneralRe: UI Controllermemberdakash29-Apr-09 5:55 
Hello Ron.
Answering your question, I think the answer is "yes". I'm looking for some kind of "Manager" for the LayoutManager you provided. My requirements for this manager, is that it'll be capable of replacing layouts, based on user selection. It's logic shall be similar to state machine, where each state is eventually a layout that is presented, and the transactions between the states are triggered by the user's selection in each layout.
The traditional way (I know) is to connect between layouts that are "neighbors", but the problem with this design is that the layouts (or specifically the "Model" in each layout) should know each other - this will cause my system to be tightly coupled, where each module references the layout holding the next state.
Was I clear?
 
One more question: Can you point me to the specific project you mentioned in CodePlex?
 

Thank you very much,
 
Dakash
GeneralRe: UI ControllermemberRon Gramann29-Apr-09 7:47 
The LayoutManager should meet your requirement on controlling the layouts. Maintaining state is another issue and is not the purpose of the LayoutManager. Sounds like it should be a service of some type. Communication between modules should not be done through references, but through the EventAggregator or through a service.
 
The CodePlex links are:
 
Composite WPF Contrib - http://compositewpfcontrib.codeplex.com/
 
Composite Extensions - http://compositeextensions.codeplex.com/
 
Composite WPF Shell - http://compositewpfshell.codeplex.com/
 
Hope this helps.
 
rg
GeneralRe: UI Controllermemberdakash29-Apr-09 11:04 
Hello Ron.
Thanks for the reply.
Do you suggest using the EventAggregator as the transport for the event that causes the switching of the layouts?
So, basically, I need to develop some kind of UI controller which will hold a reference to the LayoutManager (LM) and will switch Layouts when one of my user controls publishes an event which implies a layout switch?
 
One thing I've noticed when playing with the LM is that the views are reloaded. Sometimes, reloading the views is not needed (for example when playing a video). Is there a way to reuse the current view when switching layouts?
 
Thanks,
dakash
GeneralRe: UI ControllermemberRon Gramann30-Apr-09 0:23 
Hi Dakash,
Seems this thread has ventured into other areas. I sent you an email with more information and some ideas.
 
rg
GeneralSome feedback / Questionsmembercbertolasio20-Apr-09 17:46 
I have read your article pretty thoroughly. I feel your pain in that managing views in regions is a bit tedious.
 
I have some questions as I am not sure what you are trying to do and what problem it solves.
 
- what is a layout supposed to be and why do you need a layout?
- have you considered Scoped Regions? So Module A has a ViewA that defines Regions inside of ViewA and all of the subordinate views of Module A are injected into the scoped regions inside of View A? Module A still depends on infrastructure but all views inside of module A can still communicate outside of module A using infrastructure.
- your view is decoupled from the module, but coupled to a layout, that is now defined in config. youre still coupled to where in the UI the view will reside, views are put into layouts now, and layouts are put into regions. what have you accomplished? you have moved the problem to some other code.
- if you dont beleive that a module should be responsible for loading views into regions, I beleive that this is where a supervising controller comes into play? module loads controller, controller does the work and determines where to place the view.
- are your customers supposed to build new xml configuration files for new layouts? why and when does your customer need a new layout?
- if you have common commands, events, regions, services, and DTOs in your infrastructure and each module depends on infrastructure why would your modules be coupled and dependent upon one another? Module A publishes event, module B just happens to be listening for the event. The event is defined in infrastructure. How much more de-coupled can that be?
- suppose that you publish your regions, commands, events, services, toolbar / menubar views etc that live in your infrastructure module, and other module developers come along and create modules for use in your application, and now you want a new "skin / laout", dont you just define a new shell and load NewShell instead of the old shell, and poof, as long as you have not broken your published API, then the module developers module should work in your new shell.
 
I do appreciate your thoughts on the topic, but it feels like you may be creating a generalization that may never be used or may never solve a specifc problem, or maybe I dont see the specific problem you are trying to solve. Possibly consider creating a specialization that meets the customers needs and works, and then if needed, consider generalizations.
 
Another question you might ask is "what would your solution look like if you didnt have this problem?"
 
if you are curious to chat outside of codeProject I would be more than happy to kick around some ideas... cbertolasio AT gmail DOT com.
 
Regards
GeneralRe: Some feedback / QuestionsmemberRon Gramann21-Apr-09 8:02 
Hi cbertolasio,
 
Thanks for your comments, I'll try and answer as many of these points as I can, but will probably send you a more detailed email.
To answer "What problem does this solve?" - I think it comes down to how you go about specifying "what view goes where", and where you do it. In our development we do not reference the RegionManager within our Modules. Our views do not know (nor care) where they will be deployed. Each of our modules has its own Infrastructure assembly. Your suggestion of using a supervising controller is a step in the right direction and moves the responsibility away from the view module and shell.
As an example, if you were to take the StockTraderRI_Desktop application, and invert the layout (charts on the left-side rather than right), you would need to either modify code or your xaml. Either solution would require a recompile.
We needed to be able to dynamically alter the layout of regions and views within our shell, similiar to the Reading Pane options in Outlook.
We could have hard-coded this, but felt pulling the definition out into a config file was much more flexible and did not require a recompile of the application.
On the suggestion of a p & p member, I've made some modifications to the LayoutManager, including defining layouts in xaml and support of other layout providers.
Regarding your question about having customers create layouts, no, I would not expect a customer to do that (but couldnt prevent them); this was purely a way for our development team to drive our layout from a configuration file.
Hope that helps. Will send you a more detailed email.
 
Cheers
 
Ron
GeneralRe: Some feedback / Questionsmembercbertolasio21-Apr-09 19:12 
Ahh, I see.
 
"We needed to be able to dynamically alter the layout of regions and views within our shell, similiar to the Reading Pane options in Outlook." Ok, now it makes sense. I have done things similar to this in .net 1.1 days with windows forms and 3rd party docking controls. ASP.NET Personalization Providers also did something similar with, but I havent seen it used commercially yet.
 
There is a commercial 3rd party library that supports Xaml Dock Management. You can also persist user settings. So, imagine you have defined a bunch of dockable areas, and you just drag your modules where they need to go, and save the layout to a file when the user closes the application, poof youre done. Kind of like an IDE, like eclipse, vs.net, outlook, etc.
 
You mention that when the layout changes you dont want to recompile. Agreed. That would be bad. However, modifying YouApplication.exe.config at runtime could pose some issues. Elevated security requirements, you would probably have to reload the the configuration (unless there are some new features of .net 3.5) and layout by hand, possible click once issues due to signing, etc. If you were going to write this to a file, I would think about putting it into the UserSettings or some place safe that will not require elevated permissions or do whacky stuff with the app domain.
 
Maybe the P&P team should create a "DockableRegion" that would let us define "DockPanels" inside this region, where we could drag and drop modules?
GeneralRe: Some feedback / Questions [modified]memberRon Gramann21-Apr-09 23:51 
Re: Docking Panels - there are a number of developers on the Prism forums working with SandDock and Infragistics Dockable panels. I haven't done anything with docking and Prism, so I can't say.
 
Re: Modifying App.Config - yes, a restart would be required. This was one of the motivations behind creating different layout providers. It would be nice to be able to simply drop a xaml file into a folder and have the application automatically pickup the new layout and add it to the Layouts collection (using a FileWatcher).
 
Re: DockableRegion - I think the RegionAdapter for something like this would likely be a Canvas control.
 
I hope to get the updated code posted shortly. Will let you know.
 
Cheers
 
Ron
 
modified on Tuesday, April 28, 2009 5:41 AM

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130619.1 | Last Updated 24 Apr 2009
Article Copyright 2009 by Ron Gramann
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid