I am trying to make a custom TabControl
that supports scrolling but keeps the original look and feel of the TabControl
, obviously with the exception that it scrolls.
To begin I chose to edit a copy of the original template TabControl
used.
Then I put a ScrollViewer
around the TabPanel
. However, this has caused a minor issue where the tabs now have a border at the bottom of them when they are selected. This can be seen below by comparing the normal TabControl
and the styled TabControl
in the image.
At first I assumed this was the z indexing of the scroll viewer but after trying different values and making sure the z index of the scroll viewer and TabPanel
are both explicitly higher than the Border
's z index, it made no difference.
How can I achieve the same effect where there is no border at the bottom of the selected tab, whilst it is wrapped in a ScrollViewer
?
MainWindow.xaml
<Window x:Class="ScrollableTabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
<Style x:Key="TabControlStyle1" TargetType="{x:Type TabControl}">
<Setter Property="Padding" Value="2"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled"
Grid.Column="0"
Grid.Row="0"
Panel.ZIndex="1"
Background="Transparent">
<TabPanel IsItemsHost="true"
Margin="2,2,2,0"
Panel.ZIndex="2"
Background="Transparent"
KeyboardNavigation.TabIndex="1"
x:Name="headerPanel"/>
</ScrollViewer>
<Border x:Name="contentPanel"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Grid.Column="0"
Panel.ZIndex="0"
KeyboardNavigation.DirectionalNavigation="Contained"
Grid.Row="1"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabControl Margin="5"
Grid.Row="0">
<TabItem Header="Tab 1"/>
<TabItem Header="Tab 2"/>
<TabItem Header="Tab 3"/>
</TabControl>
<TabControl Margin="5"
Grid.Row="1"
Style="{DynamicResource TabControlStyle1}">
<TabItem Header="Tab 1"/>
<TabItem Header="Tab 2"/>
<TabItem Header="Tab 3"/>
</TabControl>
</Grid>
</Window>
So if we go take a look at a ScrollViewer
style template notice there's a Border
in there with a set color for it which is the artifact you're seeing.
We could go in and edit a Style template of ScrollViewer and remove that....or for this instance we could just let it keep its Border and override the style inheritance so in your template you could do something like;
<ScrollViewer ...>
<ScrollViewer.Resources>
<Color x:Key="BorderMediumColor">#FFFFFFFF</Color>
</ScrollViewer.Resources>
....
</ScrollViewer>
Wherein it should inherit that new color for the Border
in there which in this case I just made white, or you could change the alpha channel too '00' so it's just transparent. Or you could do the previously mentioned and define a new style template without the hardcoded border values.
Hope this helps, cheers!
ADDENDUM : If you can't find the culprit causing the visual border line you can always sort of cheat with the layout of elements within the DOM and utilize margins to overlay the line and achieve the same desired visual result. The line may still technically exist but the illusion that it doesn't can suffice just the same. :)
Working code example
<Window x:Class="ScrollableTabControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
<Style x:Key="TabControlStyle1" TargetType="{x:Type TabControl}">
<Setter Property="Padding" Value="2"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot"
ClipToBounds="true"
SnapsToDevicePixels="true"
KeyboardNavigation.TabNavigation="Local"
UseLayoutRounding="True"> <!-- Gets rid of pixel rounding errors which cause small bugs when window is a certain size -->
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
Grid.Column="0"
Grid.Row="0"
Panel.ZIndex="1"
Margin="0, 0, 0, -1.25"
Background="Transparent"> <!-- +- 1.25 seems to be required when mixed with the ZIndex to hide the border underneath the selected tab -->
<TabPanel IsItemsHost="true"
Margin="2,2,2,1.25"
Background="Transparent"
KeyboardNavigation.TabIndex="1"
x:Name="headerPanel"/>
</ScrollViewer>
<Border x:Name="contentPanel"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Grid.Column="0"
KeyboardNavigation.DirectionalNavigation="Contained"
Grid.Row="1"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TabControl Margin="5"
Grid.Row="0">
<TabItem Header="Tab 1"/>
<TabItem Header="Tab 2"/>
<TabItem Header="Tab 3"/>
</TabControl>
<TabControl Margin="5"
Grid.Row="1"
Style="{DynamicResource TabControlStyle1}">
<TabItem Header="Tab 1"/>
<TabItem Header="Tab 2"/>
<TabItem Header="Tab 3"/>
</TabControl>
</Grid>
</Window>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With