Custom Tab Style
A little while ago I mentioned about how important I thought it was to create a brand, and I showed you that you can create some interesting looking controls by Templating/Styling the standard System.Windows controls to create a brand.
I also showed you a ScrollViewer and a Tab control which I had altered. I have since then decided there is no harm in sharing the TabControl code that I created.
So in this post we will discuss what you need to do to change a standard TabControl from
into the following slightly funkier TabControl
As one would expect its all about the Templates and Styles applied.
The first thing to understand is how the TabControl is made, its actually made of a Grid with 2 rows by default, which is something like this
So we can alter this, by changing the RowDefinition to be ColumnDefinition, which will give us something like
This is done with the following XAML
1: <Style x:Key="tab" TargetType="{x:Type TabControl}">
2: <Setter Property="Template">
3: <Setter.Value>
4: <ControlTemplate TargetType="{x:Type TabControl}">
5: <Grid>
6: <Grid.ColumnDefinitions>
7: <ColumnDefinition Width="auto"/>
8: <ColumnDefinition Width="*"/>
9: </Grid.ColumnDefinitions>
10: <StackPanel Orientation="Vertical"
11: Background="{TemplateBinding Background}"
12: Grid.Column="0"
13: Panel.ZIndex="1"
14: IsItemsHost="True"/>
15: <Border
16: Grid.Column="1"
17: BorderBrush="Black"
18: BorderThickness="0"
19: Background="{TemplateBinding Background}"
20: CornerRadius="0">
21: <ContentPresenter
22: ContentSource="SelectedContent" />
23: </Border>
24: </Grid>
25: </ControlTemplate>
26: </Setter.Value>
27: </Setter>
28: </Style>
So that’s that part. The next thing to understand is that the TabItem. This is easily achieved by a little Style to replace the standard TabItem look and feel. This is what creates the individual TabItems with the arrows and disabled state.
1: <Style TargetType="{x:Type TabItem}">
2: <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
3: <Setter Property="Template">
4: <Setter.Value>
5: <ControlTemplate TargetType="{x:Type TabItem}">
6: <Grid>
7: <Border
8: Name="Border"
9: Margin="4"
10: BorderBrush="Transparent"
11: BorderThickness="0"
12: CornerRadius="0" >
13: <StackPanel Orientation="Horizontal">
14:
15: <Polygon x:Name="Arrow"
16: HorizontalAlignment="Left"
17: VerticalAlignment="Center" Margin="4"
18: Width="14" Height="14"
19: Fill="{TemplateBinding Foreground}"
20: Visibility="Hidden">
21: <Polygon.Points>
22: <Point X="0" Y="0" />
23: <Point X="0" Y="14" />
24: <Point X="14" Y="7" />
25: </Polygon.Points>
26: </Polygon>
27:
28: <!-- Header placeholder-->
29: <ContentPresenter x:Name="ContentSite"
30: VerticalAlignment="Center"
31: HorizontalAlignment="Left"
32: ContentSource="Header"
33: Margin="4"
34: RecognizesAccessKey="True"/>
35: </StackPanel>
36: </Border>
37: </Grid>
38: <ControlTemplate.Triggers>
39: <Trigger Property="IsSelected"
40: Value="True">
41: <Setter Property="Panel.ZIndex"
42: Value="100" />
43: <Setter TargetName="Border"
44: Property="Background"
45: Value="Transparent" />
46: <Setter TargetName="Arrow"
47: Property="Visibility"
48: Value="Visible" />
49: </Trigger>
50: <Trigger Property="IsEnabled"
51: Value="False">
52: <Setter TargetName="Arrow"
53: Property="Fill"
54: Value="{StaticResource Disabled}" />
55: </Trigger>
56: </ControlTemplate.Triggers>
57: </ControlTemplate>
58: </Setter.Value>
59: </Setter>
60: </Style>
And lastly, we need to change the actual TabItem.Header DataTemplate to respect the disabled state. This is done as follows
1: <DataTemplate x:Key="tabHeader">
2: <Label x:Name="lbl" Margin="0" Content="{Binding}"
3: Foreground="{Binding RelativeSource={RelativeSource
4: Mode=FindAncestor, AncestorType={x:Type TabItem},
5: AncestorLevel=1}, Path=Foreground}"
6: VerticalAlignment="Center" />
7: <DataTemplate.Triggers>
8: <Trigger Property="IsEnabled" Value="False">
9: <Setter TargetName="lbl"
10: Property="Foreground"
11: Value="{StaticResource Disabled}" />
12: </Trigger>
13: </DataTemplate.Triggers>
14: </DataTemplate>
So that’s it, all we need to do now is apply these Styles/Templates to a TabControl like so
1: <TabControl Style="{StaticResource tab}"
2: Background="White" Foreground="Orange">
3: <TabItem Header="Item1 is here"
4: HeaderTemplate="{StaticResource tabHeader}"
5: Foreground="Orange" IsEnabled="False" >
6: <Button Content="Btn1" Margin="5"/>
7: </TabItem>
8: <TabItem Header="Item2"
9: HeaderTemplate="{StaticResource tabHeader}"
10: Foreground="Orange" >
11: <ScrollViewer >
12: <Canvas x:Name="canv" Width="1200" Height="1200">
13: </Canvas>
14: </ScrollViewer>
15: </TabItem>
16: <TabItem Header="Item3"
17: HeaderTemplate="{StaticResource tabHeader}"
18: Foreground="Orange" >
19: <Button Content="Btn3" Margin="5"/>
20: </TabItem>
21: <TabItem Header="Item4"
22: HeaderTemplate="{StaticResource tabHeader}"
23: Foreground="Orange" >
24: <Button Content="Btn4" Margin="5"/>
25: </TabItem>
26: </TabControl>
And there you have it.
Here is a small demo project, should you wish to try it yourself.


























karl shifflett said
am July 19 2008 @ 12:17 pm
Nice demo Sacha.
Best to you Mate.
Cheers,
Karl
sacha said
am July 19 2008 @ 4:28 pm
Thanks Karl
Just a grain of sand
Logan said
am December 16 2008 @ 1:06 am
Thanks Sacha! This is just what I was looking for for my custom WPF options dialog. Cheers!
sacha said
am December 16 2008 @ 9:20 am
Glad it helped
Sam Matthews said
am June 5 2009 @ 5:24 pm
Thanks so much… this is so helpful
sacha said
am June 6 2009 @ 6:57 am
Cheers
Tim said
am November 10 2009 @ 8:26 am
thanks Sacha! I was looking for something similar to your example. I need to capture the the double click event on the tab header but haven’t figured out how to do it yet. My guess is to make the header label type and use the label’s double click event. It was simple for me to override the TabPage class in Windows form but it is a challenge to me in WPF. Have you worked on something similar to this problem before? really appreciate it if you could share some experience for similar problem. Thanks again. TD.
sacha said
am November 10 2009 @ 8:46 am
If you did want a double click header, that is more than likely what I would do too.