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.
Then we prepare the Shell XAML Window to include the regions we need.
And then, in the module, we just assign each view to the specific region in the shell.
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.
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:
And the very easy xaml:
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:
So far so good, now we just customize the TabContainer in the shell and that’s the final result:
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:
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!