Question

I have created a TabControl in a WPF application I'm writing. I re-templated TabItem so that I could have a button on each tab header to close it. So far, all is well and good.

I decided that I now wanted shiny round buttons instead of the default square ugly things. Also, I wanted to use an image as my buttons content instead of simply setting the content to "X".

My XAML styles/templates:

<Style TargetType="{x:Type Button}" x:Key="EllipseButtonStyle">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Grid>
                     <Ellipse Fill="{TemplateBinding Background}"
                         Stroke="{TemplateBinding BorderBrush}"
                         StrokeThickness="{TemplateBinding BorderThickness}"
                         Width="{TemplateBinding Width}"
                         Height="{TemplateBinding Height}"/>
                     <ContentPresenter HorizontalAlignment="Center" 
                         VerticalAlignment="Center"
                         Content="{TemplateBinding Button.Content}"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<DataTemplate x:Key="ClosableTabItemTemplate">
    <DockPanel MinWidth="120" Margin="0,0,0,0">
        <ContentPresenter 
            Content="{Binding Path=DisplayName}" 
            VerticalAlignment="Center"
            HorizontalAlignment="Left"/>
        <Button
            Command="{Binding Path=UnSubscribeApplicationCommand}"
            CommandParameter="{Binding Path=DisplayName}"
            BorderBrush="Black"
            BorderThickness="2"
            VerticalContentAlignment="Center"
            VerticalAlignment="Center"
            HorizontalAlignment="Right"
            DockPanel.Dock="Right"
            Width="16" Height="16">
            <Image Source="closeicon.bmp" Height="8" Width="8" 
                VerticalAlignment="Center" HorizontalAlignment="Center"/>
                <Button.Style>
                    <Style TargetType="{x:Type Button}" 
                        BasedOn="{StaticResource EllipseButtonStyle}">
                        <Setter Property="Background"
                            Value="{StaticResource CloseOffButtonBrush}"/>
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="{StaticResource CloseOnButtonBrush}"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
        </DockPanel>
    </DataTemplate>

With the above code in place, however, a selected tabs content (and the background as well) seems to shift upwards because of what I assume is the TabItems content moving upwards due to it being selected. Why, then, is the ellipse not shifting with the other content? Anyone have any idea what is going on here?

Was it helpful?

Solution

Sorry for the delayed response - I ended up solving the issue by modeling my TabItem template after the one posted in this blogpost. I believe the issue surfaced due to the fact that my TabItem template was being defined as a DataTemplate, not as a ControlTemplate as it should have been. Here is the new template:

<ControlTemplate x:Key="ClosableTabItemTemplate" TargetType="TabItem">
    <Grid SnapsToDevicePixels="true">
        <Border x:Name="Bd" Background="{StaticResource TabItemUnselectedBrush}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" >
            <DockPanel x:Name="ContentPanel">
                <Button Command="{Binding Path=UnSubscribeApplicationCommand}" BorderBrush="Black" HorizontalAlignment="Center" Margin="3,0,3,0" VerticalAlignment="Center" Width="16" Height="16" DockPanel.Dock="Right" ToolTip="Close Tab">
                    <Button.Content>
                        <Image Source="pack://application:,,,/Resources/Close.png" Height="8" Width="8" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                    </Button.Content>
                    <Button.Style>
                        <Style TargetType="{x:Type Button}" BasedOn="{StaticResource EllipseButtonStyle}">
                            <Setter Property="Background" Value="{StaticResource CloseOffButtonBrush}"/>
                            <Style.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="{StaticResource CloseOnButtonBrush}"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Button.Style>
                </Button>
                <ContentPresenter x:Name="Content" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{Binding Path=DisplayName}" RecognizesAccessKey="True" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="{TemplateBinding Padding}"/>
            </DockPanel>
        </Border>
    </Grid>

    <!-- bunch of ControlTemplate triggers to style the TabItem background color/position -->

</ControlTemplate>

OTHER TIPS

Whenever I have controls inside a DockPanel, it always seems to play up if one of the controls I've explicitly attached DockPanel.Dock to comes AFTER the element I want to take up the fill portion. While I don't know if this will answer your question, try this instead in your ClosableTabItemTemplate DataTemplate:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>

  <ContentPresenter Grid.Column="0"/>

  <Button Grid.Column="1"/>
</Grid>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top