Stopbyte

How to make a custom and advanced TabControl in WPF, using XAML only?

I want to learn the best approach to style a TabControl and TabControlItem Elements in WPF. and possibly using XAML markup alone.

I’m new to WPF and XAML, and i need to make a new Theme from scratch for my WPF application, now i am stuck at styling the TabControl i don’t even know where to start, and how to start styling it. as all the styles i tried where replacing all my TabControl items, and content.

any help would be most appreciated ?

1 Like

As for all WPF Controls the best approach to style them is to use clean XAML markup and avoid code behind as you can. (forget the Windows Forms design approach).

Here is a full style for both TabControl and TabItem controls, i used this style in one of my WPF applications few months ago, so it should work for you as well:

TabItem can be styled this way:

<Style TargetType="{x:Type TabItem}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <Border x:Name="PART_Border" Background="{TemplateBinding Background}" BorderThickness="1" BorderBrush="LightGray" Margin="2">
                    <ContentPresenter ContentSource="Header" Margin="2" />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="PART_Border" Property="BorderBrush" Value="Black" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The only interesting element in this style is the Border "PART_Border", used to replace the default border inside a TabItem Element.


TabControl can be styled this way:

The style below will treat all cases of a TabStripPlacement {Left, Right, Top, Bottom}, The Critical Element in that style template is the TabPanel Control; it’s used to display TabItems. We only play around with Grid Columns and Rows to set the TabStripPlacement the way we want it.

<Style TargetType="{x:Type TabControl}">
    <Setter Property="TabStripPlacement" Value="Left" />
    <Setter Property="Margin" Value="2" />
    <Setter Property="Padding" Value="2"    />
    <Setter Property="Background" Value="White" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid ClipToBounds="True" SnapsToDevicePixels="True" KeyboardNavigation.TabNavigation="Local">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Name="ColumnDefinition0" />
                        <ColumnDefinition Width="0" Name="ColumnDefinition1" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" Name="RowDefinition0" />
                        <RowDefinition Height="*" Name="RowDefinition1" />
                    </Grid.RowDefinitions>
                    <Border x:Name="HeaderBorder"
                            BorderBrush="Black"
                            BorderThickness="1"
                            CornerRadius="5"
                            Background="#FAFAFA"
                            Margin="0,0,0,5">
                        <TabPanel IsItemsHost="True"
                                  Name="HeaderPanel"
                                  Panel.ZIndex="1"
                                  KeyboardNavigation.TabIndex="1"
                                  Grid.Column="0"
                                  Grid.Row="0" />
                    </Border>
                    <Grid Name="ContentPanel"
                          KeyboardNavigation.TabIndex="2"
                          KeyboardNavigation.TabNavigation="Local"
                          KeyboardNavigation.DirectionalNavigation="Contained"
                          Grid.Column="0"
                          Grid.Row="1">
                        <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                CornerRadius="5">
                            <ContentPresenter Content="{TemplateBinding SelectedContent}"
                                              ContentTemplate="{TemplateBinding SelectedContentTemplate}"
                                              ContentStringFormat="{TemplateBinding SelectedContentStringFormat}"
                                              ContentSource="SelectedContent"
                                              Name="PART_SelectedContentHost"
                                              Margin="2"
                                              SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                        </Border>
                    </Grid>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="TabControl.TabStripPlacement" Value="Bottom">
                        <Setter TargetName="HeaderPanel" Property="Grid.Row" Value="1" />
                        <Setter TargetName="ContentPanel" Property="Grid.Row" Value="0" />
                        <Setter TargetName="RowDefinition0" Property="RowDefinition.Height" Value="*" />
                        <Setter TargetName="RowDefinition1" Property="RowDefinition.Height" Value="Auto" />
                        <Setter TargetName="HeaderBorder" Property="FrameworkElement.Margin" Value="0,5,0,0" />
                    </Trigger>
                    <Trigger Property="TabControl.TabStripPlacement" Value="Left">
                        <Setter TargetName="HeaderPanel" Property="Grid.Row" Value="0" />
                        <Setter TargetName="ContentPanel" Property="Grid.Row" Value="0" />
                        <Setter TargetName="HeaderPanel" Property="Grid.Column" Value="0" />
                        <Setter TargetName="ContentPanel" Property="Grid.Column" Value="1" />
                        <Setter TargetName="ColumnDefinition0" Property="ColumnDefinition.Width" Value="Auto" />
                        <Setter TargetName="ColumnDefinition1" Property="ColumnDefinition.Width" Value="*" />
                        <Setter TargetName="RowDefinition0" Property="RowDefinition.Height" Value="*" />
                        <Setter TargetName="RowDefinition1" Property="RowDefinition.Height" Value="0" />
                        <Setter TargetName="HeaderBorder" Property="FrameworkElement.Margin" Value="0,0,5,0" />
                    </Trigger>
                    <Trigger Property="TabControl.TabStripPlacement" Value="Right">
                        <Setter TargetName="HeaderPanel" Property="Grid.Row" Value="0" />
                        <Setter TargetName="ContentPanel" Property="Grid.Row" Value="0" />
                        <Setter TargetName="HeaderPanel" Property="Grid.Column" Value="1" />
                        <Setter TargetName="ContentPanel" Property="Grid.Column" Value="0" />
                        <Setter TargetName="ColumnDefinition0" Property="ColumnDefinition.Width" Value="*" />
                        <Setter TargetName="ColumnDefinition1" Property="ColumnDefinition.Width" Value="Auto" />
                        <Setter TargetName="RowDefinition0" Property="RowDefinition.Height" Value="*" />
                        <Setter TargetName="RowDefinition1" Property="RowDefinition.Height" Value="0" />
                        <Setter TargetName="HeaderBorder" Property="FrameworkElement.Margin" Value="5,0,0,0" />
                    </Trigger>
                    <Trigger Property="UIElement.IsEnabled" Value="False">
                        <Setter Property="TextElement.Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

For further details of the mechanics behind the TabControl Style read this article on msdn.

1 Like

Really nice style @jms I love the way it looks. Thanks for sharing it.

Really nice WPF TabControl Header style @jms loved it, very basic and works smoothly.

1 Like