Reputation: 121
I used this as a starting point to make a style for a TabControl, mine only works with TabStripPlacement="Bottom". I expect that the content I put in the TabItem would fill the entire area, designated for it, like it does with the default style and the style from microsoft I used as a starting point. But it does not, It starts with its minimal sizes, buttons don't get stretched at all and RichTextBoxes seem stretched only horizontally, if I add text to the RichTextBox it expands. Here is what it looks like:
The orange is the area I expect the content to take (with some margins), the blue is the background of the RichTextBox. The green is the global background. The selected tab has its header's border thickened (red). Don't mind the space between the tab headers and the content area, it is intentional.
I've been poking around a lot, but can't seem to find the cause, however I have found that I don't have this problem if I don't apply the style for the TabItem. And when I inspect the elements with the xaml debugging tools in visual studio I find that PART_SelectedContentHost has its horizontal alignment set to Left and vertical alignment set to Top, happens even if I have explicitly set them to Stretch in the style. I also tried applying "Stretch" to the RichTextBox, it didn't work. Here is the style:
<Style TargetType="{x:Type TabControl}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid KeyboardNavigation.TabNavigation="Local" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0" Value="#FFAAAAAA" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<TabPanel x:Name="HeaderPanel"
Grid.Row="2"
Panel.ZIndex="1"
Margin="0"
IsItemsHost="True"
KeyboardNavigation.TabIndex="1"
Background="Transparent" />
<Border x:Name="Border"
Grid.Row="0"
Panel.ZIndex="0"
BorderThickness="0"
CornerRadius="0"
KeyboardNavigation.TabNavigation="Local"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Border.Background>
<SolidColorBrush Color="Orange"/>
</Border.Background>
<ContentPresenter
x:Name="PART_SelectedContentHost"
Margin="4,4,4,4"
ContentSource="SelectedContent"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"/>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type TabItem}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected" />
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Storyboard.TargetName="ContentSite"
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"
From="White" To="Blue"
Duration="0:0:0.0" AutoReverse="True"/>
<ThicknessAnimationUsingKeyFrames
Storyboard.TargetProperty="(Border.BorderThickness)"
Storyboard.TargetName="Border">
<EasingThicknessKeyFrame KeyTime="0"
Value="1,5,1,5" />
</ThicknessAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="Border"
Margin="0,0,0,0"
BorderThickness="1,1,1,1"
CornerRadius="0,0,0,0"
BorderBrush="Red">
<Border.Background>
<SolidColorBrush Color="Black"/>
</Border.Background>
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"
RecognizesAccessKey="True"
TextBlock.Foreground="White"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Panel.ZIndex" Value="100"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT: For the sake clarity, the style is saved in a xaml file - "TabControlStyleDictionary.xaml" and this is the code of the main window:
<Window x:Class="TabControlStylingTest2.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"
xmlns:local="clr-namespace:TabControlStylingTest2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="TabControlStyleDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid Background="Green">
<TabControl TabStripPlacement="Bottom">
<TabItem Header="tab1">
<RichTextBox
Foreground="White"
Background="Blue"
AcceptsReturn="True"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"/>
</TabItem>
<TabItem Header="tab2">
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
some text 123456789
</Button>
</TabItem>
</TabControl>
</Grid>
Upvotes: 2
Views: 874
Reputation: 1
You need to set HorizontalContentAlignment and VerticalContentAlignment properties on TabItem.
TabControl's code-behind takes the values of these properties and assigns them to HorizontalAlignment and VerticalAlignment properties of the PART_SelectedContentHost ContentPresenter.
Upvotes: 0
Reputation: 11
I had the same issue with overwriting the style. For some reason the HorizontalAlignment and VerticalAlignment properties of the ContentPresenter are set locally on each LayoutUpdate of the PART_SelectedContentHost in the codebehind. In my solution I implement an Attached Behavior for UIElements which calls the ClearValue method on a LayoutUpdate event. This will delete the local value on each occurance of the event. A refrence to System.Windows.Interactivity is required for this.
public class ClearValueOnLayoutUpdateBehavior : Behavior<UIElement>
{
public static readonly DependencyProperty PropertyProperty =
DependencyProperty.Register(nameof(Property), typeof(DependencyProperty), typeof(ClearValueOnLayoutUpdateBehavior), new PropertyMetadata());
public DependencyProperty Property
{
get { return (DependencyProperty)GetValue(PropertyProperty); }
set { SetValue(PropertyProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
if(Property != null)
AssociatedObject.LayoutUpdated += AssociatedObject_LayoutUpdated;
}
private void AssociatedObject_LayoutUpdated(object sender, EventArgs e)
{
AssociatedObject.ClearValue(Property);
}
}
In your XAML file you have to add the xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
reference as well. Your ContentPresenter should be changed as following:
<ContentPresenter
x:Name="PART_SelectedContentHost"
ContentSource="SelectedContent"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<i:Interaction.Behaviors>
<behavior:ClearValueOnLayoutUpdateBehavior Property="{x:Static FrameworkElement.HorizontalAlignmentProperty}" />
<behavior:ClearValueOnLayoutUpdateBehavior Property="{x:Static FrameworkElement.VerticalAlignmentProperty}" />
</i:Interaction.Behaviors>
</ContentPresenter>
Upvotes: 1