Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF TabItem HeaderTemplate

Tags:

c#

wpf

xaml

Sample code:

 <TabControl>
    <TabItem>
        <TabItem.Header>
            <StackPanel Orientation="Horizontal" Margin="5">
                <Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
                <TextBlock Text="Blue" Foreground="Blue" />
            </StackPanel>
        </TabItem.Header>
        <Label Content="Content goes here..." />
    </TabItem>
    <TabItem>
        <TabItem.Header>
            <StackPanel Orientation="Horizontal" Margin="5">
                <TextBlock Text="Red" Foreground="Red" />
            </StackPanel>
        </TabItem.Header>
    </TabItem>
    <TabItem>
        <TabItem.Header>
            <StackPanel Orientation="Horizontal" Margin="5">
                <Rectangle Fill="Red" width="20" height="16" />
            </StackPanel>
        </TabItem.Header>
    </TabItem>
</TabControl>

As you can see, the TabItem Header is Always a stackpanel with different content:

<TabItem.Header>
    <StackPanel Orientation="Horizontal" Margin="5">

    </StackPanel>
</TabItem.Header>

How can you put this in a template so that I don't have duplicate stackpanel code?

Trying to do it like this:

<TabControl>
    <TabControl.Resources>
        <Style TargetType="TabItem">
        <Setter Property="HeaderTemplate">
            <Setter.Value>
            <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="5">
                <ContentPresenter Content="{TemplateBinding ContentControl.Content}"
                            ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"  
                            ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" 
                            HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" 
                            RecognizesAccessKey="True" 
                            SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"  
                            VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}">
                </ContentPresenter>
            </StackPanel>
            </DataTemplate>
            </Setter.Value>
        </Setter>
        </Style>
    </TabControl.Resources>
    <TabItem>
        <TabItem.Header>
            <!-- ??? -->
            <TextBlock Text="X" />
            <TextBlock Text="Y" />
        </TabItem.Header>
</TabControl>

results in:

  • "The property 'Header' is set more than once."
  • "The object 'Object' already has a child and cannot add 'TextBlock'. 'Object' can accept only one child."
like image 259
juFo Avatar asked Aug 25 '15 08:08

juFo


1 Answers

The only thing the tree headers really have common is the Margin="5". In second and third tab the stackpanel is irrelavant, because it has only one child. You can achive it either by using HeaderTemplate or ItemContainerStyle:

<TabControl>
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="Padding" Value="5" />
        </Style>
    </TabControl.ItemContainerStyle>

    <TabItem>
        <TabItem.Header>
            <StackPanel Orientation="Horizontal">
                <Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
                <TextBlock Text="Blue" Foreground="Blue" />
            </StackPanel>
        </TabItem.Header>
        <Label Content="Content goes here..." />
    </TabItem>
    <TabItem>
        <TabItem.Header>
            <TextBlock Text="Red" Foreground="Red" />
        </TabItem.Header>
    </TabItem>
    <TabItem>
        <TabItem.Header>
            <Rectangle Fill="Red" Width="20" Height="16" />
        </TabItem.Header>
    </TabItem>
</TabControl>

now you don't repeat anything.

You could also extract the properties of stackpanel to style to avoid repeating them:

    <TabItem.Header>
        <StackPanel Style="{StaticResource TabHeaderPanel}">
            <Image Source="/WpfTutorialSamples;component/Images/bullet_blue.png" />
            <TextBlock Text="Blue" Foreground="Blue" />
        </StackPanel>
    </TabItem.Header>

    <TabItem.Header>
        <StackPanel Style="{StaticResource TabHeaderPanel}">
            <TextBlock Text="Red" Foreground="Red" />
        </StackPanel>
    </TabItem.Header>

    <TabItem.Header>
        <StackPanel Style="{StaticResource TabHeaderPanel}">
            <Rectangle Fill="Red" width="20" height="16" />
        </StackPanel>
    </TabItem.Header>

If you want even more code reuse, you should consider MVVM-like approach:

<TabControl ItemsSource="{Binding Tabs}">
    <TabControl.ItemContainerStyle>
        <Style TargetType="TabItem">
            <Setter Property="HeaderTemplate">
                <Setter.Value>
                    <DataTemplate DataType="local:TabViewModel">

                        <StackPanel Orientation="Horizontal" Margin="5">
                            <Image x:Name="Icon" Source="{Binding Icon, Converter={StaticResource UriToBitmapSourceConverter}}" />
                            <Rectangle x:Name="ColorRect" Height="16" Width="16" Fill="{Binding Color}" Visibility="Collapsed" />
                            <TextBlock Text="{Binding Title}" Foreground="{Binding Color}"/>
                        </StackPanel>

                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding Icon}" Value="{x:Null}">
                                <Setter TargetName="Icon" Property="Visibility" Value="Collapsed" />
                                <Setter TargetName="ColorRect" Property="Visibility" Value="Visible" />
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TabControl.ItemContainerStyle>
</TabControl>

if you can't use single DataTemplate for all headers, you can use HeaderTemplateSelector

like image 96
Liero Avatar answered Oct 02 '22 17:10

Liero