DCSIMG
Blendability Part II – Design time support for Prism - Essential WPF

Blendability Part II – Design time support for Prism

In my previous post I’ve presented the Blendability concept and explained how to leverage Blend’s sample-data generation in order to support view-model at design time.

In this post I would like to continue with this concept and reveal a tiny research I’ve done related to design time support of Prism modules.

If you’ve ever developed a WPF Composite Application using Prism you may be aware of a frustrating problem while trying to work with the Shell at design time. It always ends with something like this:

image

As you can see, the Shell is always left blank, and there is no way to design it properly since no module was loaded at design time and you don’t have the perspective of a whole picture.

To fix this problem, all you have to do is forcing Blend or Visual Studio designers to load the missing modules.

A good start point would be the RegionManager class. As you may notice, we are using the RegionManager to “mark” a WPF control as region by setting the RegionManager.RegionName attached property.

Digging inside RegionManager you can see the following lines of code:

public static readonly DependencyProperty RegionNameProperty =
  DependencyProperty.RegisterAttached(
   
"RegionName"
,
   
typeof(string
),
   
typeof(RegionManager
),
   
new PropertyMetadata(OnSetRegionNameCallback));
private static void OnSetRegionNameCallback(
   DependencyObject element,
   DependencyPropertyChangedEventArgs args)
{
   
if (!IsInDesignMode(element))
    {
        CreateRegion(element);
    }
}

Looking at the definition of the RegionNameProperty attached property, there is a OnSetRegionNameCallback property changed callback. This method actually does the job. But as you can see, in case that you’re at design-time it simply does nothing. Removing this condition yields an exception at design-time, caused by a missing service locator.

So now we are dealing with how to force the creation of a service locator, and so many other services such as the IEventAggregator.

Herein, let me introduce the Designtime Bootstrapper. This guy is a special Prism bootstrapper we will use for design time-only modules, easily created from XAML.

Now before I shall begin, let’s have few words about the differences between module’s run-time and design-time activities.

Working at design-time, it’s not reasonable to load a module as it was at run-time. There may be special module configuration and run-time activities such as server calls, which are not appropriate at design-time. Here you should be considering developing modules for design-time only. But this one is just my friendly suggestion, not a mandatory solution.

Now let’s continue with the Designtime Bootstrapper. Starting with Prism 4, there are two options to load modules: Unity and MEF. In this post I’ll concentrate in one of these two options, which is MEF of course.

Loading modules with MEF, you should create a MefBootstrapper.

Here is my design-time MEF bootstrapper:

[ContentProperty("Catalog")]
public class DesigntimeMefBootstrapper
:
   
MefBootstrapper, ISupportInitialize
{
   
/// <summary>
    /// Gets or sets the design-time catalog.
    /// </summary>
    public DesigntimeCatalog
Catalog
    {
       
get
;
       
set
;
    }

   
/// <summary>
    /// An empty stub to attach the bootstrapper from XAML.
    /// </summary>
    public static void
SetBootstrapper(
       
Application
application,
       
DesigntimeMefBootstrapper
bootstrapper)
    {
    }       

   
/// <summary>
    /// There is no need for Shell at design-time.
    /// </summary>
    protected override DependencyObject
CreateShell()
    {
       
return null
;
    }

   
/// <summary>
    /// Use the Catalog added at design time.
    /// </summary>
    protected override void
ConfigureAggregateCatalog()
    {
       
base
.ConfigureAggregateCatalog();

       
if (Catalog != null
)
        {
            AggregateCatalog.Catalogs.Add(Catalog);
        }
    }
       
   
protected override IModuleCatalog
CreateModuleCatalog()
    {
       
return new ConfigurationModuleCatalog
();
    }

   
/// <summary>
    /// Register the container instance so it can be imported later.
    /// </summary>
    protected override void
ConfigureContainer()
    {
       
base
.ConfigureContainer();
           
        Container.ComposeExportedValue<
CompositionContainer
>(Container);
    }

   
void ISupportInitialize
.BeginInit()
    {           
    }

   
void ISupportInitialize
.EndInit()
    {
       
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
        {
            Run();
        }
    }
}

This design-time bootstrapper provides the following:

  1. Special Catalog property of type DesigntimeCatalog. This is an adapter for the MEF catalog so we can create design-time catalogs directly from XAML.
  2. Implementation of the ISupportInitialize interface to ensure that all properties are initialized from XAML before acting.
  3. Stub method for creating a design-time bootstrapper directly from Application’s XAML as an attached property.

And here is one of my MEF catalog adapters for design-time:

public class DesigntimeAssemblyCatalog : DesigntimeCatalogAdapter<AssemblyCatalog>
{
   
protected override AssemblyCatalog
CreateInnerCatalog()
    {
       
var assembly = Assembly
.Load(AssemblyName);
       
return new AssemblyCatalog
(assembly);
    }

   
public string AssemblyName { get; set; }            
}

Having these, lets jump into the Application’s XAML file to see how can we use them.

<Application x:Class="Blendability.Prism.App"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:ts="http://blogs.microsoft.co.il/blogs/tomershamam">

    <ts:DesigntimeMefBootstrapper.Bootstrapper>
        <ts:DesigntimeMefBootstrapper>
            <ts:DesigntimeAggregateCatalog>
                <ts:DesigntimeAssemblyCatalog AssemblyName="Blendability.Modules.Camera2dT1" />
                <ts:DesigntimeAssemblyCatalog AssemblyName="Blendability.Modules.DesigntimeModule" />
            </ts:DesigntimeAggregateCatalog>
        </ts:DesigntimeMefBootstrapper>
    </ts:DesigntimeMefBootstrapper.Bootstrapper>

    <Application.Resources>

    </Application.Resources>

</Application
>

As you can see, I’ve created an instance of type DesigntimeMefBootstrapper directly from the Application’s XAML file, configured it to load two modules: A regular module, and another one created for design-time only.

Now that everything is ready, let’s go back to where we started. We had to remove the design-time condition from the RegionManager, OnSetRegionNameCallback method.

Since Prism 4 is an open source, we can open the RegionManager source code and change it, but if you ask me, I’d rather have an adapter than changing the original code since who knows what will be brought with the next version of Prism.

So here is my adapter:

public class RegionManager
{       
   
public static readonly DependencyProperty
RegionNameProperty =
       
DependencyProperty
.RegisterAttached(
           
"RegionName"
,
           
typeof(string
),
           
typeof(RegionManager
),
           
new PropertyMetadata
(OnSetRegionNameCallback));
       
   
public static void
SetRegionName(
       
DependencyObject
regionTarget,
       
string
regionName)
    {
       
if (regionTarget == null
)
           
throw new ArgumentNullException("regionTarget"
);
        regionTarget.SetValue(RegionNameProperty, regionName);
    }
       
   
public static string GetRegionName(DependencyObject
regionTarget)
    {
       
if (regionTarget == null
)
           
throw new ArgumentNullException("regionTarget"
);
       
return regionTarget.GetValue(RegionNameProperty) as string
;
    }

   
private static void
OnSetRegionNameCallback(
       
DependencyObject
element,
       
DependencyPropertyChangedEventArgs
args)
    {
        Microsoft.Practices.Prism.Regions.
RegionManager
.SetRegionName(
            element, (
string
)args.NewValue);

       
if (DesignerProperties
.GetIsInDesignMode(element))
        {
            CreateRegion(element);
        }           
    }

   
private static void CreateRegion(DependencyObject
element)
    {
       
IServiceLocator locator = ServiceLocator
.Current;
       
DelayedRegionCreationBehavior
regionCreationBehavior =
            locator.GetInstance<
DelayedRegionCreationBehavior>();
        regionCreationBehavior.TargetElement = element;
        regionCreationBehavior.Attach();
    }
}

Ok, now let’s change the shell to work with the region-adapter and see what happens at design-time.

imageimage

This is it guys, we are done! Now we are able to watch the whole story at design-time.

Update #1: Changed RegionManager to RegionAdapter to prevent confusion.

Here is the code.

In the next post, I’ll talk about a replacement for the view-model locator using MEF, providing nice and clean solution for design-time.

Happy new year to you all.

Published Monday, January 03, 2011 7:24 PM by Tomer Shamam

Comments

# re: Blendability Part II – Design time support for Prism

Monday, January 03, 2011 10:34 PM by Ran Trifon

Hi Tomer,

Great post, it is the first solution I see of design time view of the shell in Prism. Very very nice.

Maybe it will be in Prism.V5.

# re: Blendability Part II – Design time support for Prism

Tuesday, January 04, 2011 9:09 AM by Tomer Shamam

Hi Ran ;)

Indeed it's a break through. I haven't found any resource over the internet providing a real solution to this issue yet.

Thanks.

# re: Blendability Part II – Design time support for Prism

Thursday, January 13, 2011 11:52 PM by David Kossoglyad

Anyway, WPF development with Prism is looking as a fight against WPF philosophy... Who can explain, why you need Region, then you have ContentControl?

# re: Blendability Part II – Design time support for Prism

Friday, January 14, 2011 11:02 AM by Tomer Shamam

On the contrary, Prism as opposed to CAB was design with WPF philosophy in mind. Region and ContentControl can’t be compared as Region is Prism’s way of implementation for the UI composition represents a place holder for one or many views, and ContentControl is one of the possible WPF controls can be used as place holder for single view. You can also use ItemsControl as place holder for many views or you can define your own Region adapter, for example to be used with Border, Ribbon or other controls.

# re: Blendability Part II – Design time support for Prism

Thursday, January 20, 2011 1:12 PM by Elad Katz

Really good post Tomer :)

(and yet another example of how everything in Prism is help us turn simple problems to overly sophisticated solutions ;-)

# re: Blendability Part II – Design time support for Prism

Thursday, January 20, 2011 1:22 PM by Tomer Shamam

Thanks Elad. Agree with Prism is sophisticated solution, but only for composite UI, which is not simple.

# re: Blendability Part II – Design time support for Prism

Friday, January 21, 2011 2:48 AM by Elad Katz

Not what I meant. Prism was not built with Blendability in mind, and that is just showing from every corner.

So, yeah. Adopting MVVM as the primary UI-Architecture is a big step forward, but MVVM without blendability is one crippled design pattern.

That being said, I'm using Prism now in a SL application. When it's needed it's needed... But it aint pretty.

# re: Blendability Part II – Design time support for Prism

Friday, January 21, 2011 10:42 AM by Tomer Shamam

Elad, from what you’re saying I conclude that:

(a) “Prism was not built with Blendability in mind”

(b) “Adopting MVVM as the primary UI-Architecture is a big step forward”

(c) “MVVM without Blendability is one crippled design pattern”

I agree with each separately, but not as a whole.

You can also say that MVVM was not built with Blendability in mind, up until someone in the Blend team raises the glove and created the d:DataContext, way after someone provided a solution with a simple markup-extension and attached property.

Anyway, working with Prism and MVVM you’ll have the same Blendability support on each view/view-model separeated from the composed shell. When working with Prism, you usually create the view/view-model from code behind, then use region manager or view register to inject it into the relevant region.

Herein the Blendability problem rises again, you won’t have Blendability support only at the composite shell, so you won’t see the whole picture since it composed at runtime based on solution specific configuration, which can be vary from project to project, version, per customer request and license.

This time I raised the glove, and I really hope that the Prism team will have such solution or another in the next Prism’s drop or version.

# re: Blendability Part II – Design time support for Prism

Thursday, February 17, 2011 10:01 AM by Alex

Have you ever tried to port your solution to Silverlight?

# re: Blendability Part II – Design time support for Prism

Thursday, February 17, 2011 10:15 AM by Tomer Shamam

Hi Alex,

Haven't tried it on Silverlight 4. It mainly designed for use with WPF, but feel free to try. I'll be happy to hear about the results.

# re: Blendability Part II – Design time support for Prism

Saturday, March 05, 2011 12:55 PM by Houman

I have tried it in Silverlight 4. I get a weird behaviour; while it works in Blend, it doesnt work in Visual Studio 2010 Designer. Why? Its is the same code... Anyone experienced the same problem?

# re: Blendability Part II – Design time support for Prism

Thursday, March 10, 2011 9:08 AM by Tomer Shamam

Hi Houman, It's very nice that it works with SL4, I haven't tried that. You should launch a second Visual Studio with the same solution, attaching it to the first one for debugging it. You may now have break points at design time, in the second VS of course, so you'll have an idea what's happening over there.

I'll be glad to hear about your progress.

Thanks.

# re: Blendability Part II – Design time support for Prism

Thursday, June 16, 2011 9:03 PM by Bonas Khanal

Great post. I am using Prism with Ninject and trying to get this to work. Interestingly, there are not many articles around trying to solve this problem.

I tried downloading and compiling your solution. First error, I got was around Prism libraries (I don't install these into GAC). Got that working after referencing from Lib.

Now, the exception is get is : Cannot resolve dependencies to System.windows.version-2.0.5.0.?

Also, have you tried making this work other than with MEF?

Thanks.

# re: Blendability Part II – Design time support for Prism

Sunday, June 19, 2011 5:39 PM by Tomer Shamam

Hi Bonas Khanal,

As for the first one, sounds wierd. Make sure you didn't add reference to old WPF reference. This compiled with WPF 4.0.

As for the second, no I didn't. But you can port my code to use other DI tool such as Unity. Try to look at Prism's service locator. Also get rid of MEF integration. There is no simple way of doing that.

# re: Blendability Part II – Design time support for Prism

Friday, May 04, 2012 8:54 AM by Claudia

 Thanks for pointing out your veroisn. I got into this because folks (including Jeremy) didn't know it had been done. And there you are  .I also prefer your implementation of StructureMapServiceLocator which follows the recommended approach. I didn't know about that approach at the time I wrote so I just winged it.Anyone who wants the most literal translation should use yours.I comfort myself for the hours spent on four points:(1) I refined the UnityBootstrapper so readers have a clearer idea what it does, (2) I have a truly IoC agnostic approach which makes it easier for others to implement the IoC approach they prefer, (3) I used the Prism RI as my proof point, (4) I have tests for all variations (new today) and a clear path forward for maintaining those tests.Thanks for the note and your interest.

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above:
Powered by Community Server (Commercial Edition), by Telligent Systems