Geeks With Blogs
Jeff Ferguson Irritating other people since 1967

I recently set out to build a WPF application using the Composite Client Application Guidance (lovingly known as “Prism”) code base. Additionally, I wanted to make use of the Blacklight controls – specifically, the DragDockPanelHost and DragDockPanel controls – in the application. I soon discovered that these two code bases didn’t work together “right out of the box”. I found a problem, and a solution, and this post describes both the issue and its resolution.

The architecture promoted by Prism is that of an application made up of an executable shell and a set of module assemblies. In this pattern, each of the application views are implemented in their own module assembly, which is loaded by the shell at runtime. So far, so good.

Since I was interested in using the DragDockPanelHost and DragDockPanel controls in my WPF application, my approach was to put the DragDockPanelHost in my shell application, and a DragDockPanel in each of my Prism modules. With this design, and after assigning a Prism region name to the DragDockPanelHost in the shell application, Prism would do its thing and load each of the DragDockPanels from each module and add them into my DragDockPanelHost region at runtime.

You may be asking yourself, “And how did that work out for you?” To quote Stewie on Family Guy, “Not well, Brian. Not well.”

My first mistake was in assuming that Prism regions worked with any kind of container control, and, therefore, putting a region name on my DragDockPanelHost XAML element would be enough. It wasn’t. Prism, by default, only supports regions defined on containers derived from one of the following classes:

  • System.Windows.Controls.Primitives.Selector
  • System.Windows.Controls.ItemsControl
  • System.Windows.Controls.ContentControl

The DragDockPanelHost class is derived from Canvas, which explains the lack of love that I got from Prism.

Fortunately, Prism has a way to add support for other containers defined as regions through the RegionAdapterBase class. In my solution, I wrote a class, derived from RegionAdapterBase, that defined a region adapter for the Blacklight DragDockPanelHost. The class looks like this:

   1:      public class DragDockPanelHostRegionAdapter : RegionAdapterBase<DragDockPanelHost>
   2:      {
   3:          public DragDockPanelHostRegionAdapter(IRegionBehaviorFactory BehaviorFactory) : base(BehaviorFactory)
   4:          {
   5:          }
   6:   
   7:          protected override void Adapt(IRegion region, DragDockPanelHost regionTarget)
   8:          {
   9:              region.Views.CollectionChanged += (s, e) =>
  10:                  {
  11:                      if (e.Action == NotifyCollectionChangedAction.Add)
  12:                      {
  13:                          foreach (FrameworkElement CurrentElement in e.NewItems)
  14:                          {
  15:                              UserControl CurrentElementAsUserControl = CurrentElement as UserControl;
  16:                              DragDockPanel PanelToAdd = CurrentElementAsUserControl.Content as DragDockPanel;
  17:                              CurrentElementAsUserControl.Content = null;
  18:                              regionTarget.AddPanel(PanelToAdd);
  19:                          }
  20:                      }
  21:                      else if (e.Action == NotifyCollectionChangedAction.Remove)
  22:                      {
  23:                          foreach (FrameworkElement CurrentElement in e.OldItems)
  24:                              regionTarget.Children.Remove(CurrentElement);
  25:                      }
  26:                  };
  27:          }
  28:   
  29:          protected override IRegion CreateRegion()
  30:          {
  31:              return new AllActiveRegion();
  32:          }
  33:      }

The generic type used in the RegionAdapterBase class is the type of the class for which you need Prism to support as a region. The body of the effort is in the Adapt() method, which is called when controls are added or removed from the container.

This class needs to be used in the shell’s Prism bootstrapper through an overloaded method called ConfigureRegionAdapterMappings():

   1:      internal class Bootstrapper : UnityBootstrapper
   2:      {
   3:          protected override DependencyObject CreateShell()
   4:          {
   5:              // ... snip
   6:          }
   7:   
   8:          protected override IModuleCatalog GetModuleCatalog()
   9:          {
  10:              // ... snip
  11:          }
  12:   
  13:          protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
  14:          {
  15:              RegionAdapterMappings Mappings = base.ConfigureRegionAdapterMappings();
  16:              Mappings.RegisterMapping(typeof(DragDockPanelHost), Container.Resolve<DragDockPanelHostRegionAdapter>());
  17:              return Mappings;
  18:          }
  19:      }

 

The region adapter is registered with Prism in the RegisterMapping() method of the RegionAdapterMappings class.

With this, I can now define a user control in my Prism module that defines a DragDockPanel:

   1:  <UserControl x:Class="Narrative.Units.View"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:blacklight="clr-namespace:Blacklight.Controls;assembly=Blacklight.WPFControls">
   5:      <blacklight:DragDockPanel Header="Title" />
   6:  </UserControl>

 

Then I can, in my Prism shell, define a DragDockPanelHost with a region:

   1:  <Window x:Class="App.MainWindow"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:blacklight="clr-namespace:Blacklight.Controls;assembly=Blacklight.WPFControls"
   5:      xmlns:regions="clr-namespace:Microsoft.Practices.Composite.Presentation.Regions;assembly=Microsoft.Practices.Composite.Presentation"
   6:      Title="App Title" Height="300" Width="300" x:Name="Main">
   7:      <Grid x:Name="MainGrid">
   8:          <Grid.RowDefinitions>
   9:              <RowDefinition Height="auto"/>
  10:              <RowDefinition Height="auto"/>
  11:              <RowDefinition Height="*"/>
  12:          </Grid.RowDefinitions>
  13:          <blacklight:DragDockPanelHost Background="Khaki" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="Auto" Height="Auto" Grid.Row="2" x:Name="PanelHost" Margin="15" regions:RegionManager.RegionName="PanelHostRegion" />
  14:      </Grid>
  15:  </Window>

 

This solved the issue, and I can now have my DragDockPanelHost in a Prism executable shell and a DragDockPanel user control in a Prism module.

Posted on Tuesday, June 30, 2009 6:58 PM | Back to top


Comments on this post: Working with Prism and the Blacklight Controls

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
i don't know if you believe in cyberhugs. between men. but you just got one, buddy. sorry for invading your cyberspace.

thank you for sharing this region adapter and breaking down the process of creating it. i cannot wait to use it....
Left by headbiznatch on Jun 30, 2009 11:39 PM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Good stuff Jeff. I ran into a similar issue at a previous client using Prism and Silverilght. I'm bookmarking this one just in case I run into it in the future.
Left by Donn Felker on Jul 07, 2009 4:52 PM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Hi Jeff! Good work on this!
I can't actually get it to work in Silverlight beacuse the UserControl.Content is protected and can't be accessed this way.

Coud this snippet be a work around?
var currentElementAsUserControl = currentElement as UserControl;
var panelToAdd = new DragDockPanel
{
Content = currentElementAsUserControl
};
regionTarget.AddPanel(panelToAdd);

Haven't tested it yet but I'll get back with the results asap.
Left by David on Jul 09, 2009 6:22 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Yes, it seems to work ok for Silverlight aswell.
Left by David on Jul 10, 2009 8:16 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Hi David,

Thank you for pointing out the problem with this code when used in a Silverlight solution. Although this blog post was focused on WPF, I do need to find a way to get some Silverlight support. Thank you for keeping me honest!

I tried your code in a Silverlight solution, and it compiled, but I didn't get the runtime behavior I was expecting. I got two panels: the one in my module that I created as a child of the module's user control, and the new one you created in your code. In fact, if you write this:

UserControl CurrentElementAsUserControl = CurrentElement as UserControl;
DragDockPanel PanelToAdd = new DragDockPanel()
{
Content = CurrentElementAsUserControl,
Header = "PanelToAdd"
};
regionTarget.AddPanel(PanelToAdd);

You should be able to see the second title on the second panel when you run the Silverlight app.

I will continue to look for a solution and will keep everyone posted.

Thanks for reading!
Left by Jeff Ferguson on Jul 11, 2009 6:30 PM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Hi Tom,

No, I have not yet tried that scenario. So, you're app has more than one DragDockPanelHost with multiple panels in each host?
Left by Jeff Ferguson on Jul 19, 2009 9:54 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Hi Jeff,

Very usefull solution. However i am loosing data binding fro controls when using DragDockPanel in view.
Do you havwe some sugestions?
Left by Josef on Jul 22, 2009 5:11 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Josef,

Thank you for the feedback. Yes, there is a data binding bug in this code. I will be putting a formal blog post together later, but, for now, I will give you the short answer.

Before detaching the DragDockPanel from its container UserControl, be sure to assign the DragDockPanel's DataContext to the same reference as the UserControl's DataContext:

PanelToAdd.DataContext = CurrentElementAsUserControl.DataContext;

That should take care of things.
Left by Jeff Ferguson on Jul 24, 2009 11:16 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Thank you Jeff for your answer

I have implement now BlackLight version 4.1 which is ItemsControl. It functions very well, data binding functions too as well
Left by Josef on Aug 04, 2009 4:55 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Thanks, Josef. You're absolutely right. The new 4.x version of Blacklight makes this post a moot point. At some point, hopefully soon, I will mention this in an update blog post. Thanks for reading!
Left by Jeff Ferguson on Aug 04, 2009 3:51 PM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Hi Jeff,

I ran into a different problem and I wonder if you ever hit it before.
I referenced backlight library in one of my modules and tried to have some sample DragDockPanel in my view. I added it in xaml (just simple <blacklight:DragDockPanel /> in LayoutRoot, nothing more) and project compiled well. But when I ran the project it failed on View's InitializeComponent() saying it can't parse xaml. Removing DragDockPanel from xaml and adding it in the code after intialization is working just fine.

I created sample project without using Prism and it is working correct both ways.

I was wondering if I am referencing Blacklight library correct or there is something special about referencing control libraries in modules in prism.
Left by Nick on Sep 11, 2009 11:15 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Great (and very useful) post! It took me some weird Googling to find this, though, so I have replicated it on StackOverflow.

http://stackoverflow.com/questions/1877300/how-can-i-use-a-panel-as-a-region-in-prism

If you have an account there, I would be happy to mark any reply you put up as the real answer. I have linked back to this post as well.
Left by Jedidja on Dec 09, 2009 4:21 PM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Hi ,

i am not able to find the AddPanel of DragDockPanelHost

pl let me know the solution for the same
Thanks
Jignesh
Left by Jignesh on May 21, 2010 4:50 AM

# re: Working with Prism and the Blacklight Controls
Requesting Gravatar...
Really clear explanation about RegionAdaptor
Left by Ravuthasamy on Sep 25, 2013 7:07 AM

Your comment:
 (will show your gravatar)


Copyright © Jeff Ferguson | Powered by: GeeksWithBlogs.net | Join free