Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Styling WPF TabControl Borders

0.00/5 (No votes)
11 Nov 2014 1  
Style a WPF TabControl border to be thicker and have rounded corners.

Introduction

In this tip, I want to show you a simple way to style the borders of a TabControl. This method allows the border to be any thickness. I will show you how to create a ControlTemplate that will make this happen and a little trick to get the borders of selected/unselected tabs to look right. Below, you can see a stock TabControl and a TabControl with thicker and rounded borders.

Overview of TabControl

First, let’s go over the TabControl. A TabControl consists of two main parts. A TabPanel that serves as the header and a ContentPresenter for the SelectedContent which holds the selected view. The TabPanel has a collection of TabItems that each hold another ContentPresenter for the Header content of each tab. Since the tabs are contained in a separate TabPanel, a little trick needs to be added to make the selected tab look right. Below, you can see all the parts.

TabControl Implementation

We create a control template for the TabControl. We will use a Grid to position the ContentPresenter and the TabPanel in the TabControl. The Grid needs 3 rows. You also want to set the Grid property UseLayoutRounding to True. This way the TabPanel tabs and the ContentPresetner border will line up.

<ControlTemplate TargetType="TabControl">
    <Grid UseLayoutRounding="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="31" />
            <RowDefinition Height="4" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
    </Grid>
    ...
</ControlTemplate>

The top row will hold the TabPanel. The second row will be an overlap area for the TabPanel and the ContentPresenter. This will allow for tabs to have a solid border on the bottom matching the border around the ContentPresenter when they are not selected and allow for selected tabs to have no border on the bottom and look like the tab is part of the ContentPresenter. The trick is to set the TabPanel properties Grid.Row=0, RowSpan=2 and Panel.ZIndex=1.

<TabPanel IsItemsHost="True"
          Grid.Row="0"
          Grid.RowSpan="2"
          Panel.ZIndex="1" />

Then set the Border around the ContentPresenter properties Grid.Row=1, RowSpan=2 and Panel.ZIndex=0. Here, we also set the corners to be rounded.

<Border Grid.Row="1"
        Grid.RowSpan="2"
        CornerRadius="{StaticResource TabConrol_Corner_Radius}"
        Panel.ZIndex="0"
        BorderThickness="{StaticResource Tab_Border_Thickness}"
        Background="{TemplateBinding Background}"
        BorderBrush="{TemplateBinding BorderBrush}">
    <ContentPresenter Margin="4"
                      ContentSource="SelectedContent" />
</Border>

The middle row needs to be the same width as the border and the top row needs to be the selected tab height minus the border width. In our case, selected tab height is 35, border is 4, so the top row height is 31(35-4) and the middle row height is 4. What this does is it puts the TabPanel with all the tabs on top of the top border. This will have the effect of showing the bottom border outside tabs and hiding it behind tabs.

<Style TargetType="{x:Type TabControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="TabControl">
                <Grid UseLayoutRounding="True">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="31" />
                        <RowDefinition Height="4" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TabPanel IsItemsHost="True"
                              Grid.Row="0"
                              Grid.RowSpan="2"
                              Panel.ZIndex="1" />
                    <Border Grid.Row="1"
                            Grid.RowSpan="2"
                            CornerRadius="{StaticResource TabConrol_Corner_Radius}"
                            Panel.ZIndex="0"
                            BorderThickness="{StaticResource Tab_Border_Thickness}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <ContentPresenter Margin="4"
                                          ContentSource="SelectedContent" />
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Each TabItem also needs a ControlTemplate that wraps the ContentPresenter in a border. The border is the same thickness as the border around the main ContentPresenter.

<ControlTemplate TargetType="{x:Type TabItem}">
    <Border x:Name="tabBorder"
            Height="30"
            Width="50"
            BorderBrush="{StaticResource Tab_Border_Color}"
            BorderThickness="{StaticResource Tab_Border_Thickness}"
            VerticalAlignment="Bottom"
            Margin="{StaticResource TabItem_Spacer_Between_Tabs}"
            CornerRadius="{StaticResource TabItem_Corner_Radius}"
            Background="{StaticResource Tab_Background_Color}">
        <ContentPresenter x:Name="tabContent"
                          ContentSource="Header"
                          RecognizesAccessKey="True"
                          VerticalAlignment="Center"
                          HorizontalAlignment="Center" />
    </Border>
</ControlTemplate>

There is a style trigger when the tab is selected, IsSelected=True, which sets properties to make the tab look selected. The border around the tab here is swapped from 4,4,4,4 to 4,4,4,0.

<Window.Resources>
    <Thickness x:Key="Tab_Border_Thickness">4,4,4,4</Thickness>
    <Thickness x:Key="TabItem_Border_Thickness_Selected">4,4,4,0</Thickness>


<ControlTemplate.Triggers>
    <Trigger Property="IsSelected"
             Value="True">
        <Setter TargetName="tabBorder"
                Property="BorderThickness"
                Value="{StaticResource TabItem_Border_Thickness_Selected}" />
        <Setter TargetName="tabBorder"
                Property="Height"
                Value="35" />
        <Setter TargetName="tabBorder"
                Property="BorderBrush"
                Value="{StaticResource TabItem_Border_Color_Selected}" />
        <Setter TargetName="tabBorder"
                Property="Background"
                Value="{StaticResource TabItem_Background_Color_Selected}" />
    </Trigger>
</ControlTemplate.Triggers>

Notice the bottom no longer has a border which allows the tab background to flow into the main ContentPresenter for a selected tab. The rest of the unselected tabs have a bottom border which look like they are part of the border around the main ContentPresenter. The trigger also increases the height of the selected tab and also adds some highlighting. This is the trick to get the border to look right for selected and unselected tabs.

Conclusion

Hopefully, this little trick will help you in styling a TabControl. Please leave any comments or suggestions. You can also get the source at https://github.com/mw12345/TabControlDemo.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here