Using Expression Blend with VS2008: Invalid project File Error
Selected posts on CLR, threading, Internationalization and WPF

Basic WPF: Stylize standard tabs into Aqua Gel

I was making this WPF based application on weekends and wanted it to have a gel like look. I did not want to create a new control instead I wanted to make sure that I used WPF styles to change the look of the tab. I decided to share this experience so that anyone who wants to do this does not have to do any research. The things that I will do to make this happen required very simple and basic WPF knowledge and is thus considered as kid stuff to many WPF pros. But none the less, I am interested to share this. Here goes ...

Download GelTab.zip

Styling and Templates

Basic WPF ControlWPF has the capability of applying styles like css does to html. Any WPF control that we use has something called a control template. The control template defines how the control will look. The control template may contain triggers also. Let try to understand from the diagram below how WPF renders a control UI.

The basic WPF control has a template which it uses to render the UI from the control definition. This can be changed by applying a custom style. In order to do that we must first define a custom style where we can modify the template for the control. By doing so we will able to completely change the look and behavior of the control. We can even make a tab look like a listbox a tree. The possibilities are endless. However today we will only try to make the tab look like blue gel based (Aqua) tab. Which should look like this (by the way. I have not yet decided on the blue shade ... it still looks a little ugly) ...

Tab Look

In order change a tab to look like this I will need to write a style and modify that style.

Basic WPF Control with Styling The control code will stay the same but the style template will change. In order to this we will create a resource dictionary. So we have created a resource dictionary called GelTabStyle.xaml. In order for this to be used by the application the this will need to be added into the application dictionary ( we can also put it in the file with the tab instance, but that is not a good practice). When we add such a resource dictionary from the Visual studio or Microsoft Expression Blend the IDE automatically modifies the App.xaml to include the resource dictionary. Here is how our App.xaml should look like ...

<Application.Resources>        
  <
ResourceDictionary>
    <
ResourceDictionary.MergedDictionaries>
      <
ResourceDictionary Source="GelTabStyle.xaml"/>
    </
ResourceDictionary.MergedDictionaries>
  </
ResourceDictionary>       
</
Application.Resources>

WPF Control Composition & Templates

As I have already stated that a WPF control has templates to define its UI, what are these templates made of? They are made of WPF shapes, other WPF controls, triggers and GDI resource definitions. To have a very simple idea see the diagram below...

WPF Control Anatomy 

A control has a template which is made of WPF basic elements and controls, and each of those controls have templates which have WPF elements and controls in them. We can define a style in a resource dictionary (in our case the GelTabStyle.xaml) and inside a style we can define our custom control template. Before we go any further lets try to look up a tab controls template.

Anatomy of a Tab

If you pry open the template that renders the tab control with Expresssion Blend then you would find that there is a grid with 2 rows. The first row contains the tab item headers and second part contains the tab body. See the diagram below ...

TabControlStructure

Since we want the tab headers to be gradient gel like we will define a style for those and will call them "GelTabItem" and also will need to change the bottom container and will call that "GelTab" style. For our case we only the the tab headers to placed at top ... so we wont bother with placing them at left, right or bottom.

Inside the resource dictionary we have defined some colors and some gradient brushes to do our painting, which looks like this ...

 <Color x:Key="TabItemGradStart" R="#AA" G="#AA" B="#BB"/>
<
Color x:Key="TabItemGradEnd" R="#EE" G="#EE" B="#FF" A="#FF"/>
...
...

<
LinearGradientBrush x:Key="TabBackgorundBrush" StartPoint="0,0" EndPoint="0,1" >
     <
GradientStop Offset="0" Color="{StaticResource TabItemGradStart}"/>
     <
GradientStop Offset="1" Color="{StaticResource TabItemGradEnd}"/>
</
LinearGradientBrush>
<
LinearGradientBrush x:Key="TabBackgorundBrushSel" StartPoint="0,0" EndPoint="0,1" >
     ...
     ...

So we have defined the basic drawing resources, lets look at the style header

<Style x:Key="GelTabItem" TargetType="{x:Type TabItem}">

If you look carefully that you would find that this style can only apply to a TabItem object and also it will only apply to those tab items which have their style set to "GelITabtem". By the way if we wanted to make sure all the tab items in the application would use this style (except the ones that has explicit style defined) then we would remove the x:Key keyword from the style. That would ensure that all tab items by default will have the style applied. However that is not our goal.

TabItems

Lets see how the xaml code makes the header part with tab items to be styled. The diagram below will make it clear.

TabHeader

As you can see that we have used a border with rounded corners to make a border for the gel tab item, we have also used a rectangle to fill the gradient area of the button. We used the gradient brush that we defined at the start of the resource file.

Triggers

In order make sure that the selected tab has one color and the unselected tabs have different color we used triggers to change the back color. See the code snippet below

<ControlTemplate.Triggers>
  <
Trigger Property="IsSelected" Value="True">
    <
Setter TargetName="TabItemBackgorund" Property="Fill"
      Value="{
StaticResource TabBackgorundBrushSel}" />
  </
Trigger>
  <
Trigger Property="IsSelected" Value="False">
    <
Setter TargetName="TabItemBackgorund" Property="Fill"
      Value="{
StaticResource TabBackgorundBrush}" />
  </
Trigger>
  <
Trigger Property="IsEnabled" Value="False">
    <
Setter TargetName="TabItemBackgorund" Property="Fill"
      Value="{
StaticResource TabBackgorundBrushDisabled}" />
  </
Trigger>
</
ControlTemplate.Triggers>

When the tab is selected use a brush called "TabBackgorundBrushsel" as the fill value of the rectangle and when it is not selected we use the brush called "TabBackgorundBrush" to have an effect of unselected item. Similarly we use "TabBackgorundBrushDisabled" to fill the rectangle for the disabled tab item. See figure ...

SelectionBrush

Tab Body

If you observe the tab body style then you would see a control with the "controlpresenter" tag which actually contains the controls that we place inside the tab.

<ControlTemplate TargetType="TabControl">
  <
Grid>
  <
Grid.RowDefinitions>
    <
RowDefinition Height="Auto"/>
    <
RowDefinition Height="*"/>
  </
Grid.RowDefinitions>
  <
Border BorderBrush="Transparent" BorderThickness="0" Grid.Row="1"
    
CornerRadius="5,5,5,5" Background="Transparent" Padding
="5,5,5,5" >
     <
Border BorderBrush="Gray" BorderThickness="1" CornerRadius="5,5,5,5"
       
Background="{
StaticResource TabBackgorundBrushSel}"  >
        <
ContentPresenter ContentSource="SelectedContent" />               
     </
Border>
   </
Border>
   <
TabPanel Grid.Row="0" IsItemsHost="true"/>
   </
Grid>
</
ControlTemplate>

If we observe the xaml above we would see that the body resides in second row and the header panel (<TabPanel> in xaml) resides at top row this is how the bottom and top placement of the two different parts are defined. We have made the body rounded and also padded it and used the selected brush to paint it.

Using the styles with the tabs

Styling Strategy  There are two ways to apply the style. Depending on how we want the tabs to look it may change. One strategy is to use css class like style that we made and apply them the control items. Another is to make the apply the style whole application wide. In order to make it application wide we would have to remove the x:key that names the style.

Now if we want to make it specific for a control we use the style tag to specify the style like the code below ( the part marked in bold letters)

<TabControl Style="{DynamicResource GelTabControl}" >            
   <
TabItem Header="Rapid Note" Style="{DynamicResource GelTabItem}">


kick it on DotNetKicks.com

Comments

Lutfar Rahman Nirjhar

Great Article Shafqat! I really enjoyed the drawing and format of the article. Its very smooth and polite. Keep the good works going :)

Moim Hossain

Nice one indeed. Informative and useful for the WPF people..
BTW, the glyph's and images are quite handsome...how did you do this..? by means of office 2007 apps or visio?

Shafqat Ahmed

Thanks, Moim. I used an alternative software. By the way ... it is very easy to make such a diagram designer software in WPF.

Luiz Arruda

Very good and interesting article. Any peace of XAML/WPF sample with explanation like this one, is worth the reading, when it comes to increase our knowledge in this magnificent technology from Microsoft.

Thanks.

MB

Thank you for this article. In one post you have moved me from a state of confusion and frustration to a better understanding of not just the tab control, but all WPF controls. The diagrams pointing to the xaml code and the element on the control helped me put my finger on what I was trying to change. Microsoft could only dream that their documentation is this helpful. Thanks again!

Rich Altamura

I thought I would reach out to you to see if you might be able to pass this unique opportunity around to people who might be interested in this type of role here in NYC. I am not sure of your current status but was hoping at least to do some networking. Would you be open to having a quick chat?
Rich Altamura 201-947-7050 x107

3D/Visualization/Advanced Graphics in WPF/C# (Silicon Valley meets Wall Street) Tremendous opportunity to help design and build from scratch a revolutionary User Interface for Algorithmic Trading.

If you are a top flight C# software engineer who is interested in taking your Graphics and Visualization skills to the next level this
will be right up your alley.
This is all about building a truly revolutionary UI that will give Equity Traders the same richness of information as a Heads Up Display in an F-16 cockpit.
The right candidate will be someone with deep Object Oriented/Computer Science expertise and real world C#/Smart Client/Desktop skills.
The system is being built with Microsoft's Windows Presentation Foundation with a real time back-end in Core Java.
Will be leveraging Agile methods (NUnit, TeamCity for Continuous Integration, etc) as well as Open Source IOC Frameworks.
All in all, opportunity to become part of world-class software engineering team building the next generation of tools for Wall Street.
This Rich Visualization effort needs someone with deep Object Oriented/Computer Science expertise and real world C#/Smart Client/Desktop skills.

All in all, opportunity to become part of world-class software engineering team building a 3D/Graphics system in WPF/C#/3.5

Anu

Hi,Problem in TabStripPlacement="Left"
Im trying to use like yours.
But i want to use TabStripPlacement="Left"
When i use that,what happened is all TabHeaders gone to top,tabitems are below...like
Header1
Header2
Tabitems...
But i want is
Header1 Tabitems
Header2
How to do that

Anandakumar

Excellent man!!! very usefyul

Impotence causes

A very good greetings to all readers. I think everyone should agree with me in thinking that the anatomy is the foundation of all learning, whether any medical science or a motor or other device, first to learn of any branch must be known physical or anatomical structure. From this it should start then delve into the subject, here is the importance of anatomy.

Cheap viagra

Good afternoon colleagues and friends, my name is Rich, I share with you my humble opinion on this article in person, when he started reading it I thought it might not be as interesting, but when developing reading I fully realize it's great, is great, I look forward to reading many more contributions like this.

FAisal

Good One

オラセフ

Great tips, I would like to join your blog anyway,

The comments to this entry are closed.