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

image

into the following slightly funkier TabControl

image

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

rows

So we can alter this, by changing the RowDefinition to be ColumnDefinition, which will give us something like

cols

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>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }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>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }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>

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And there you have it.

Here is a small demo project, should you wish to try it yourself.

Advertisements

10 thoughts on “Custom Tab Style

  1. karl shifflett says:

    Nice demo Sacha.

    Best to you Mate.

    Cheers,

    Karl

  2. sacha says:

    Thanks Karl

    Just a grain of sand

  3. Logan says:

    Thanks Sacha! This is just what I was looking for for my custom WPF options dialog. Cheers! 🙂

  4. sacha says:

    Glad it helped

  5. Sam Matthews says:

    Thanks so much… this is so helpful

  6. sacha says:

    Cheers

  7. Tim says:

    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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: