How can I add a separator between tabs in a tabcontrol? - wpf

I'm styling my tabs so that it just displays the header text but I want to add a separator between them. Right now the tabs look like:
Content1 Content2
Is there a way I can add a Separator between the tabs so that it looks like:
Content1 | Content2
I can already style a vertical separator like that but I can't figure out how to place it in between the tabs. I don't want to use a tab border to fake a divider and I would like to stay away from using
<TabItem Header="|" IsEnabled="False" /> to fake it too. Is there an elegant solution for this?
This is what I have so far:
<TabControl Background="Transparent" BorderThickness="0">
<TabItem Header="Content1">
<!--content1-->
</TabItem>
<TabItem Header="Content2">
<!--content2-->
</TabItem>
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border Margin="2,0">
<ContentPresenter ContentSource="Header" Margin="10,2" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Foreground" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
</TabControl>
Thanks in advance!

I ended up just going with this:
<TabItem IsEnabled="False" IsHitTestVisible="False">
<TabItem.Header>
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" Height="10" Background="Black" />
</TabItem.Header>
</TabItem>

For anyone who came here too to see the possible answer (like me).
I did it this way:
In your Resources define three styles
Style 1 (with both separators):
<Style TargetType="TabItem" x:Key="TabItemWithBothSeparators">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border BorderBrush="DarkGray" BorderThickness="1,0">
<Grid Name="Panel">
<ContentPresenter x:Name="ContentSite" ContentSource="Header" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Style 2 (with left separator):
<Style TargetType="TabItem" x:Key="TabItemWithLeftSeparator">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border BorderBrush="DarkGray" BorderThickness="1,0,0,0">
<Grid Name="Panel">
<ContentPresenter x:Name="ContentSite" ContentSource="Header" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Style 3 (without separators):
<Style TargetType="TabItem" x:Key="TabItemWithoutSeparators">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Grid Name="Panel">
<ContentPresenter x:Name="ContentSite" ContentSource="Header" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then set the styles this way (code example below):
On odd TabItems count (1,3,5,7,...) set style to TabItemWithoutSeparators
On even TabItems count (2,4,6,8,...) set style to TabItemWithBothSeparators
When your last TabItem count is even, set its style to TabItemWithLeftSeparator otherwise set to TabItemWithoutSeparators
Example with even TabItems count
<TabItem Header="Item 1" Style="{StaticResource TabItemWithoutSeparators}">
<!--content 1-->
</TabItem>
<TabItem Header="Item 2" Style="{StaticResource TabItemWithBothSeparators}">
<!--content 2-->
</TabItem>
<TabItem Header="Item 3" Style="{StaticResource TabItemWithoutSeparators}">
<!--content 3-->
</TabItem>
<TabItem Header="Item 4" Style="{StaticResource TabItemWithLeftSeparator}">
<!--content 4-->
</TabItem>
Example with odd TabItems count
<TabItem Header="Item 1" Style="{StaticResource TabItemWithoutSeparators}">
<!--content 1-->
</TabItem>
<TabItem Header="Item 2" Style="{StaticResource TabItemWithBothSeparators}">
<!--content 2-->
</TabItem>
<TabItem Header="Item 3" Style="{StaticResource TabItemWithoutSeparators}">
<!--content 3-->
</TabItem>
Hope, this helps! Fore sure there is a more elegant way to get this separators...

Related

How to define a TabItem Header Template in WPF

I'm learning WPF and I read an article about Templating. So I wanted to write some code, but i got stuck.
What do I want to do? My Application has A TabControl and I want that all the tabs has the same Layout. A stackpanel and in the stackpanel an Image and a Textblock.
Now i don't know how the content can be set afterwards. Do I need a ContentPresenter?
<ControlTemplate x:Key="TabTemplate">
<StackPanel Orientation="Horizontal">
<Image></Image>
<TextBlock></TextBlock>
</StackPanel>
</ControlTemplate>
In your resource dictionary add a Style with your desired template:
<Style x:Key="CustomTabItemStyle"
TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="Root"
Width="180"
Height="45"
Margin="0,0,0,0"
SnapsToDevicePixels="true">
<StackPanel Orientation="Horizontal">
<Image Width="90"
Margin="10"
VerticalAlignment="Center"
Source="pack://Application:,,,/img/myTabImage.png"
Stretch="Uniform" />
<TextBlock x:Name="contentPresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Focusable="False"
FontSize="16"
Foreground="White"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Text="{TemplateBinding Header}" />
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Don't forget to edit your Image. If all tabs has same image then just change a Source link, otherwise, you may need another binding, e.g Content.
And then simply use this style in your TabItems:
<TabControl Margin="0,5,0,0"
FocusVisualStyle="{x:Null}">
<TabItem Header="My First Tab"
IsSelected="{Binding FirstTabItemSelected}"
Style="{DynamicResource CustomTabItemStyle}">
...
</TabItem>
<TabItem Header="My Second Tab"
IsSelected="{Binding SecondTabItemSelected}"
Style="{DynamicResource CustomTabItemStyle}">
...
</TabItem>
</TabControl>

TabItem ControlTemplate element Visibility

I have defined a custom ControlTemplate for TabItem where I am having besides the ContentPresenter a Button (to hide the tab when clicked).
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter ContentSource="Header"/>
<Button Grid.Column="1" Content="x" Visibility="Visible"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want the Button to be Visible only for certain TabItems and Collapsed for others. Collapsed state should be the default and I don't understand how to set it to Visible for certain tabs.
If the tab items are like
<TabItem Header="Normal tab">
<Grid />
</TabItem>
<TabItem Header="Closable tab">
<Grid/>
</TabItem>
and I want to have the "Closable tab" to be showing this Button but the "Normal tab" not showing this Button, how can this be done ?
You can use TabItem's Tag property to identify its type, and watch this type from inside Button with RelativeSource binding.
<TabControl>
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border BorderThickness="1" BorderBrush="Green" Margin="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter ContentSource="Header"/>
<Button Grid.Column="1" Content="x">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TabItem}, Path=Tag}" Value="Closable">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Normal tab">
<Grid />
</TabItem>
<TabItem Tag="Closable" Header="Closable tab">
<Grid />
</TabItem>
</TabControl>
I edited Border attributes a little bit just to visual separate tabs, they are not necessary for this example to work.

Need some suggestions how to set custom tabcontrol

How could I make the tabitem(header) border in alignment with the content part. Below I put the item header in grid. I've attached a picture. There's some tiny space at left. The same thing to the top part. I want it to be filled.
<Grid>
<TabControl Margin="10" BorderThickness="2" Background="LightGray" Padding="0">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Grid Name="Panel">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="30,3"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Panel" Property="Background" Value="LightSkyBlue" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Panel" Property="Background" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="General">
<Label Content="Content goes here..." />
</TabItem>
<TabItem Header="Security" />
<TabItem Header="Details" />
</TabControl>
</Grid>
Default template of TabItem have margin of -1 but in your case you have border thickness of 2 set. So margin of -2 should work for you.
Set margin on Grid:
<ControlTemplate TargetType="TabItem">
<Grid Name="Panel" Margin="-2,-2,-2,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="30,3"/>
</Grid>
.....
Set the margin for the TabItem instead of Tab
<TabItem Header="General", Margin="0,0,0,0"/>
Set the margin for the TabItem to 0, this will override the default margin and work as per your requirements

Modify expander header children when expanded

I can't figure out how to do it in XAML. I came up with workarounds that use Expanded/Collapsed events, but they just don't feel right.
I have a DataGrid, with groups, that are templated as expanders. I have a button inside expander, that's hidden by default, and only needs to be shown when an expander is expanded.
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Some Text"/>
<Button Name="MyButton" Visibility="Collapsed" Content="Add All"/>
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
So basically the code in question is right in the middle:
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Some Text"/>
<Button Name="MyButton" Visibility="Collapsed" Content="Add All"/>
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
I'm trying to set Visibility of the Button to false, when the expander is expanded. I tried using a trigger like the following:
<Expander.Style>
<Style TargetType="Expander">
<Style.Triggers>
<DataTrigger Binding="{Binding IsExpanded, RelativeSource={RelativeSource Self}}" Value="False">
<Setter TargetProperty="MyButton" Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Expander.Style>
, but the compiler doesn't accept it, because it can't find MyButton (I'm guessing because it's inside a header). Any ideas on how I can get this to work?
You have to move the DataTrigger in a ControlTemplate like this:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Name="Expander" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Some Text" />
<Button Name="MyButton"
Visibility="Collapsed"
Content="Add All" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsExpanded, ElementName=Expander}" Value="False">
<Setter TargetName="MyButton" Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
Notes
I explicitly set the name for the Expander, because the construction RelativeSource={RelativeSource Self} points to control himself, and there in GroupItem is no property IsExpanded.
In Setter does not have a TargetProperty property, but there are TargetName.

Change TabItem.Header, when selected, from image to text

I have bumped into a problem to which I am seeking the solution.
I have a tabControl with styles added to it.
I want to display only images on each TabItem.Header and when that tab is selected then hide the image from header and display text (the other, inactive headers will be showing images).
Can anybody provide any help?
<Window.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Name="Borderer" Background="#FF777777" Height="70" Width="90">
<ContentPresenter x:Name="ContentTabItem" ContentSource="Header" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="#FF000000" />
<Setter TargetName="Borderer" Property="Background" Value="WhiteSmoke" />
<Setter Property="Header" >
<Setter.Value>
<Grid>
<ContentPresenter ContentSource="Tag" />
</Grid>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Height="189" HorizontalAlignment="Left" Margin="94,123,0,0" Name="tabControl1" VerticalAlignment="Top" Width="500">
<TabItem Name="tabItem1" IsSelected="True" >
<TabItem.Tag>
<TextBlock Text="blab lab la" />
</TabItem.Tag>
<TabItem.Header>
<Image Source="images/img1.png" Width="35" Height="35" />
</TabItem.Header>
<TabItem.Content>
<Grid>
<TextBlock Name="aaa" />
</Grid>
</TabItem.Content>
</TabItem>
<TabItem >
<TabItem.Tag>
<TextBlock Text="la lab blab" />
</TabItem.Tag>
<TabItem.Header>
<Image Source="images/img2.png" Width="35" Height="35" />
</TabItem.Header>
<TabItem.Content>
<Grid>
<TextBlock Name="bbb" />
</Grid>
</TabItem.Content>
</TabItem>
</TabControl>
</Grid>
You can actually do this without replacing the whole ControlTemplate on the TabItem. You can just alter the TabItem's HeaderTemplate when the TabItem is selected, making your Style a lot shorter:
<DataTemplate x:Key="SelectedHeaderTemplate" >
<ContentControl Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=TabItem},
Path=Tag}" />
</DataTemplate>
<Style TargetType="{x:Type TabItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="HeaderTemplate" Value="{StaticResource SelectedHeaderTemplate}" />
</Trigger>
</Style.Triggers>
</Style>

Resources