Month: July 2010

WPf and Prism Tab Region Adapter, Part 02.

As we saw in the previous post, it’s pretty complicate to create a custom design in WPF to override the default style of the TabControl, but it’s pretty simple to extend the behavior of it.

As a senior dev, I usually don’t like to: 1) touch what is already working, 2) reinvent the wheel just to write the same code twice. For that there is the refactoring process, at most!

So, let’s start by the requirement we had in the previous post, we need to emulate the VS IDE in our applications, that’s it! We also saw that Prism has the RegionAdapter, so now we just need a cool control. Well there is the Avalon Dock project that is open source, really well done, flexible and ready for WPF 4. So let’s use it for our purposes. The final result should be something like this:


Avalon dock is crazy powerful and allows you to build a complete system of docking and modal windows into your WPF application. But before doing that you need to write a custom region adapter for it!

So, this is the basic concept of a custom Region Adapter in Prism. You create you custom adapter class by inheriting from RegionAdapterBase<T> in the following way:

Region adapter for Avalon Dock
  1. public sealed class AvalonRegionAdapter : RegionAdapterBase<DocumentPane>
  2. {
  3.     public AvalonRegionAdapter(IRegionBehaviorFactory factory)
  4.         : base(factory)
  5.     {
  7.     }


Avalon dock has a lot of future, and you should create a Region adapter able to attach any type of dock view. In my case I will use only the Tab region adapter that is called DocumentPane. Now the RegionAdapterBase requires that you implement three methods:

Create region
  1. protected override IRegion CreateRegion()
  2. {
  3.     return new AllActiveRegion();
  4. }


The create region which specify what base adapter you want to use. In this case we want to handle any type of view added to this adapter, like an ItemsContainer or a TabControl.

  1. protected override void Adapt(IRegion region, DocumentPane regionTarget)
  2. {
  3.     region.Views.CollectionChanged += delegate(Object sender, NotifyCollectionChangedEventArgs e)
  4.     {
  5.         OnViewsCollectionChanged(sender, e, region, regionTarget);
  6.     };
  7. }

Then we override the adapt method. This method is called once so in my case, because I have a DocumentPane I will then listen for the event Views.CollectionChanged. In this way I know every time if a view has been added or removed from the region.

Code Snippet
  1. private void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DocumentPane regionTarget)
  2. {
  3.     if (e.Action == NotifyCollectionChangedAction.Add)
  4.     {
  5.         //Add content panes for each associated view.
  6.         foreach (object item in e.NewItems)
  7.         {
  8.             UIElement view = item as UIElement;
  10.             if (view != null)
  11.             {
  12.                 DockableContent newContentPane = new DockableContent();
  13.                 newContentPane.Content = item;
  14.                 //if associated view has metadata then apply it.
  15.                 newContentPane.Title = view.GetType().ToString();
  16.                 //When contentPane is closed remove the associated region
  17.                 newContentPane.Closed += (contentPaneSender, args) =>
  18.                 {
  20.                 };
  21.                 regionTarget.Items.Add(newContentPane);
  22.                 newContentPane.Activate();
  23.             }
  24.         }
  25.     }
  26.     else
  27.     {
  28.         if (e.Action == NotifyCollectionChangedAction.Remove)
  29.         {
  31.         }
  32.     }
  33. }

Now, here there are two major steps. First we want to know if the item has been added or removed from the collection. If it’s added we create a new DockableContent and we set the Content to our view. Then we need to set a couple of properties like Title and name. In my case I am just adding the view, we will see later how to implement our TabModel property. What we can do then is to attach a listener to the close event of this tab. Why? Because when avalon will close a dock document we need also to destroy the corresponding view.

Then the second part is when the regionAdapter has a request of closing a view. We want to destroy the corresponding tab control.

Now go back a little bit and change the code in this way:

Code Snippet
  1. TabViewModel viewModel = ((UserControl)view).DataContext as TabViewModel;
  2. if (view != null)
  3. {
  4.     DockableContent newContentPane = new DockableContent();
  5.     newContentPane.Content = item;
  6.     if (viewModel != null)
  7.     {
  8.         Image img = new Image();
  9.         img.Source = new BitmapImage(new Uri(@”Resources/Alerts.png”, UriKind.Relative));
  10.         newContentPane.Title = viewModel.TabModel.Title;
  11.         newContentPane.IsCloseable = viewModel.TabModel.CanClose;
  12.         newContentPane.Icon = img.Source;
  13.     }

It’s a little bit dirty but what we are trying to do here is to cast the View.DataContext to a type TabViewModel. It it’s the right type, as NET won’t throw an exception but simply returns an empty instance … we will populate the tab controls with our info.

The final result is this one:


The first can’t be closed and the second one can be, also we added a special icon to the context menu. More than that, this is still a WPF controls where you can apply your custom style. That’s it!

Ops, this is the new code in the MainView, of course:

XAML Avalon Dock adapter
  1. <ad:DockingManager Grid.Column=”1″ Grid.Row=”1″ >
  2.     <ad:DocumentPane cal:RegionManager.RegionName=”TabRegion” Name=”TabRegion”>
  3.         <ad:DockableContent Title=”Some title”>
  5.         </ad:DockableContent>
  6.     </ad:DocumentPane>
  7. </ad:DockingManager>

Final Note: As you can see building a custom region adapter is easy but you must know the control behind that, the one that will act as a region adapter. When you know that part you can play as you want. For example I have a region adapter for the ToolBar so every time you load a view, I pass the corresponding Toolbar to the main toolbar region. Same for the ribbon and for the Outlook bar.

Prism and WPF. Custom Tab region adapter. Part 01.

The second part of this tutorial is here. Enjoy!

One thing that I really hate when I started working on Prism was the fact that the developers didn’t provide a nice tab region adapter. Well, the items adapter provided in Prism fits perfectly with the tab control, but if you use it “as is” it pretty sucks.

I am pretty sure that as soon as you will release your first Prism “Visual Studio style” Prism application your manager will ask you for a cook tab like the one below:


This is an add-in of VS2010. As you can see it allows you to: 1) close the current active tab, 2) identify the tabs that have changes with a red dot, 3) use different colors in order to identify the active tab and the different types of files opened.

Another cool tab system, also present in VS 2010 is the one that splits the XAML code from the designer IDE, look here:


I simply love this style of a non–squared tab, it remembers me Chrome browser.

How Prism region adapter works?

First of all let’s have a look on how we create a new tab in a tab region adapter, using the default Prism settings. What you usually do is to add a view to a region and that’s it … The code below shows you how to define a tab region adapter and then how to add a new tab on it. Pretty straightforward.

First of all we initialize the bootstrapper in the Shell project.

The Shell bootstrapper
  1. protected override System.Windows.DependencyObject CreateShell()
  2. {
  3.     var shell = Container.Resolve<MainWindow>();
  4.     shell.Show();
  5.     return shell;
  6. }
  8. protected override IModuleCatalog GetModuleCatalog()
  9. {
  10.     var catalog = new ModuleCatalog()
  11.         .AddModule(typeof(TabModule));
  12.     return catalog;
  13. }


Then we prepare the Shell XAML Window to include the regions we need.

Shell XAML Window
  1. <ItemsControl Grid.Row=”0″ Grid.Column=”0″ Grid.ColumnSpan=”2″
  2.               cal:RegionManager.RegionName=”HeaderRegion” Name=”HeaderRegion”>
  4. </ItemsControl>
  5. <TabControl Grid.Row=”1″ Grid.Column=”1″
  6.             cal:RegionManager.RegionName=”TabRegion” Name=”TabRegion”>
  8. </TabControl>

And then, in the module, we just assign each view to the specific region in the shell.

Module initialization
  1. public sealed class TabModule : IModule
  2. {
  3.     private IRegionManager regionManager = null;
  5.     public TabModule(IRegionManager regionManager)
  6.     {
  7.         this.regionManager = regionManager;
  8.     }
  10.     public void Initialize()
  11.     {
  12.         regionManager
  13.             .AddToRegion(“TabRegion”, new FirstView())
  14.             .AddToRegion(“TabRegion”, new SecondView());
  15.     }
  16. }

And this is the ugly result you get …


As you can see the tab doesn’t know how to render the tab item because we didn’t specify any style.

Now in order to fix this there are two simple solutions. The first one is to create a custom region adapter and override the method on adding a region and provide all the info you need to the region adapter. This is cool especially if you are using third party controls. The second way is the Raf’s way, as I don’t use at all third party controls for the simple reason that as a blogger, I don’t have money … [:)]

Everything starts with the Dependency Property.

XAML is cool especially because it allows you to extend it as much as you want, so why don’t we start to consider its powerful engine before going mad adopting strange solutions?? So let’s build a simple .dll project that will be our “model” for the TabItem style. This model represents our properties to say that: 1) A view has a title, 2) A View can be or cannot be closed, 3) A view has changes that must be saved.

Model for a tab item
  1. public sealed class TabModel : DependencyObject
  2. {
  3.     public string Title
  4.     {
  5.         get { return (string)GetValue(TitleProperty); }
  6.         set { SetValue(TitleProperty, value); }
  7.     }
  9.     // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc…
  10.     public static readonly DependencyProperty TitleProperty =
  11.         DependencyProperty.Register(“Title”, typeof(string), typeof(TabModel));
  13.     public bool CanClose
  14.     {
  15.         get { return (bool)GetValue(CanCloseProperty); }
  16.         set { SetValue(CanCloseProperty, value); }
  17.     }
  19.     // Using a DependencyProperty as the backing store for CanClose.  This enables animation, styling, binding, etc…
  20.     public static readonly DependencyProperty CanCloseProperty =
  21.         DependencyProperty.Register(“CanClose”, typeof(bool), typeof(TabModel));
  23.     public bool IsModified
  24.     {
  25.         get { return (bool)GetValue(IsModifiedProperty); }
  26.         set { SetValue(IsModifiedProperty, value); }
  27.     }
  29.     // Using a DependencyProperty as the backing store for IsModified.  This enables animation, styling, binding, etc…
  30.     public static readonly DependencyProperty IsModifiedProperty =
  31.         DependencyProperty.Register(“IsModified”, typeof(bool), typeof(TabModel));
  33. }

Now we need just to create a ViewModel which represents a View rendered in a tab control. Of course you should have the entire infrastructure of your ViewModel toolkit but in this sample we don’t really need it! This is the code:


Now that everything is in place we need two more pieces of code. The first is a ViewModel for each view which implements also the TabItem model, the code below is just for the FirstView:

Concrete ViewModel for TabItem
  1. public sealed class FirstViewModel : TabViewModel
  2. {
  3.     public FirstViewModel()
  4.     {
  5.         this.TabModel = new TabModel
  6.         {
  7.             Title = “First View.”,
  8.             CanClose = true,
  9.             IsModified = false
  10.         };
  11.     }
  12. }

And the very easy xaml:

FirstView vm binding
  1.          xmlns:vm=”clr-namespace:PrismCustomModule.ViewModels”
  2.          mc:Ignorable=”d”
  3.          d:DesignHeight=”300″ d:DesignWidth=”300″>
  4. <UserControl.DataContext>
  5.     <vm:FirstViewModel />
  6. </UserControl.DataContext>

Customize the TabItem using styles.

The first step is very easy, just go in the Shell project and add a Resource dictionary that will be then referenced in the Shell Window. Create a custom style for the tab container and specify how to bind the header text with the TabItem model we created previously:

  1. <ResourceDictionary xmlns=””
  2.                     xmlns:x=””>
  3.     <Style TargetType=”{x:Type TabItem} x:Key=”HeaderStyle”>
  4.         <Setter Property=”Header”
  5.                 Value=”{Binding RelativeSource={RelativeSource Self},
  6.                     Path=Content.DataContext.TabModel.Title}” />
  7.     </Style>
  8. </ResourceDictionary>


So far so good, now we just customize the TabContainer in the shell and that’s the final result:

Code Snippet
  1. <TabControl Grid.Row=”1″ Grid.Column=”1″
  2.             cal:RegionManager.RegionName=”TabRegion” Name=”TabRegion”
  3.             ItemContainerStyle=”{StaticResource HeaderStyle}“>
  5. </TabControl>



Pretty good and because the TabItem model is a property that raises changes in the ViewModel, we can communicate with the user changing this property “on fly” when the tab is open. We can also use it to activate a specific tab, adding a behavior to the tab style and more. But the tab header still sucks, and a lot! I mean I really don’t like it. So here we come with Blend 4 and Design 4.

There are a lot of tutorials on the web about Blend and Design. I came out with a final result pretty minimal but functional, this one using this tutorial:

Another interesting project is AvalonDock on Codeplex. They just resemble the VS2010 style just using XAML code. This is the link for the style they used for the tab header and this is the result:


Next time I will show you how to add a custom behavior to the tab in order to broadcast with prism the closing event, so that prism can kill the corresponding view …

[;)] Stay tuned!