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:

image

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:

image

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. }
  7.  
  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”>
  3.     
  4. </ItemsControl>
  5. <TabControl Grid.Row=”1″ Grid.Column=”1″
  6.             cal:RegionManager.RegionName=”TabRegion” Name=”TabRegion”>
  7.     
  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;
  4.  
  5.     public TabModule(IRegionManager regionManager)
  6.     {
  7.         this.regionManager = regionManager;
  8.     }
  9.  
  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 …

image

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.     }
  8.  
  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));
  12.  
  13.     public bool CanClose
  14.     {
  15.         get { return (bool)GetValue(CanCloseProperty); }
  16.         set { SetValue(CanCloseProperty, value); }
  17.     }
  18.  
  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));
  22.  
  23.     public bool IsModified
  24.     {
  25.         get { return (bool)GetValue(IsModifiedProperty); }
  26.         set { SetValue(IsModifiedProperty, value); }
  27.     }
  28.  
  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));
  32.  
  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:

image

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:

Dictionary
  1. <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
  2.                     xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”>
  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}“>
  4.     
  5. </TabControl>

 

image

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: http://stackoverflow.com/questions/561931/how-to-create-trapezoid-tabs-in-wpf-tab-control

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 http://avalondock.codeplex.com/SourceControl/changeset/view/60974#1218433 and this is the result:

image

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!

Comments are closed.

Post Navigation