3

I have a TabControl which is styled and templated in a way that the tab header and the tab content are separated by a line, but the current selected tab breaks this line (see picture 1).enter image description here

This works like a charm, except for two things.

First problem: In certain zoom states (unfortunately in default state 100% as well) there is a very thin line separating the tab header from the content (see picture 2; the height of the separation line is 2px).enter image description here

Where does it come from and how can I get rid of it?

Since the current selected tab might get a left, top and right border later on I can't just increase the negative margin, because the left and right border would be visible in the content.


Second problem: If I put the tab header inside a ScrollViewer to be able to horizontally scroll through the tabs (in case there are too many), the negative Margin gets clipped and the separation line is shown. Of course the ScrollViewer is styled and templated in a way to not use the horizontal scrollbar below the content.

How can I use negative Margin inside a ScrollViewer?

I commented out the ScrollViewer in my code below. Please remove the comment indicators and please remove the Margin on the TabPanel inside the ScrollViewer to see the problem in action.

Here's my styles and templates and code, in case someone needs it:

<Style x:Key="TabControlStyle" TargetType="{x:Type TabControl}">
    <Setter Property="Background" Value="LightGoldenrodYellow" />
    <Setter Property="BorderBrush" Value="Gray" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid SnapsToDevicePixels="True"
                      Background="{TemplateBinding Background}">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>

                    <Border x:Name="Content"
                            Grid.Row="1" Grid.Column="0"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="0,2,0,0"
                            Background="White">
                        <ContentPresenter x:Name="PART_SelectedContentHost"
                                          ContentSource="SelectedContent"
                                          Margin="{TemplateBinding Padding}"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                    </Border>

                    <!--<ScrollViewer Grid.Row="0" Grid.Column="0" Margin="5,5,5,0">-->
                    <TabPanel x:Name="PART_ScrollContentPresenter"
                              Grid.Row="0" Grid.Column="0"
                              Margin="5,5,5,0"
                              IsItemsHost="True" />
                    <!--</ScrollViewer>-->
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
    <Setter Property="Background" Value="LightGray" />
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="BorderThickness" Value="1,1,1,0" />
    <Setter Property="BorderBrush" Value="Gray" />
    <Setter Property="MinWidth" Value="50" />
    <Setter Property="Margin" Value="0,0,5,0" />
    <Setter Property="Padding" Value="0" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <Grid SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                      Margin="{TemplateBinding Margin}">
                    <Border x:Name="Border"
                            Background="{TemplateBinding Background}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding Background}">

                        <TextBlock x:Name="TabTitle"
                                   Margin="16,6"
                                   Text="{TemplateBinding Header}"
                                   HorizontalAlignment="Center"
                                   VerticalAlignment="Center"
                                   TextWrapping="NoWrap" 
                                   TextTrimming="CharacterEllipsis" />
                    </Border>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="White" TargetName="Border" />
                        <Setter Property="BorderBrush" Value="Gray" TargetName="Border" />
                        <Setter Property="Margin" Value="0,0,2,-2" />
                        <Setter Property="Padding" Value="0,-2,0,0" />
                    </Trigger>
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="IsMouseOver" Value="True" />
                            <Condition Property="IsSelected" Value="False" />
                        </MultiTrigger.Conditions>
                        <Setter Property="Background" Value="Gray" TargetName="Border" />
                        <Setter Property="BorderBrush" Value="Gray" TargetName="Border" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication" 
        Title="MainWindow"
        Height="350" Width="525">

    <Window.LayoutTransform>
        <ScaleTransform ScaleX="1" ScaleY="1" />
    </Window.LayoutTransform>

    <TabControl Style="{StaticResource TabControlStyle}"
                ItemContainerStyle="{StaticResource TabItemStyle}">
        <TabItem Header="Tab 1" />
        <TabItem Header="Tab Two" />
        <TabItem Header="Tab III" />
    </TabControl>

</Window>

Thank you for your time and help.

Nostromo
  • 1,177
  • 10
  • 28

1 Answers1

1

Regarding your first problem, you can increase your negative margins by separating the border from the content:

<Grid SnapsToDevicePixels="True" Background="{TemplateBinding Background}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <!--SeparatorLine will be rendered on bottom-->
    <Border x:Name="SeparatorLine"
            Grid.Row="1" Grid.Column="0"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="0,2,0,0">
    </Border>

    <!--Tabs will be rendered on top of the SeparatorLine-->
    <TabPanel x:Name="PART_ScrollContentPresenter"
            Grid.Row="0" Grid.Column="0"
            Margin="5,5,5,0"
            IsItemsHost="True" />

    <!--Content will be rendered on top. Content.Margin should equal the SeparatorLine.BorderThickness-->
    <Border x:Name="Content"
            Grid.Row="1" Grid.Column="0"
            Margin="0,2,0,0"
            Background="White">
        <ContentPresenter x:Name="PART_SelectedContentHost"
                          ContentSource="SelectedContent"
                          Margin="{TemplateBinding Padding}"
                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
    </Border>
</Grid>

Now you are free to use negative margin in TabItem, that are bigger than the content border size.

The second problem can also be solved with negative margins... as far as I know, you won't get the default Scrollviewer to allow any content overflow, no matter how bad you want it. See WPF clipping even when no clipping is desired - how to turn it off? for some discussion on the topic.

What you can do is increase the scrollviewers size with a negative margin, so the actual items will stay inside the supported area:

<ScrollViewer Grid.Row="0" Grid.Column="0" Margin="5,5,5,-3" VerticalScrollBarVisibility="Disabled">
    <TabPanel x:Name="PART_ScrollContentPresenter"
            Grid.Row="0" Grid.Column="0"
            Margin="0,0,0,3"
            IsItemsHost="True" />
</ScrollViewer>

This example would allow negative bottom margins on TabItem up to 3 px.

grek40
  • 13,113
  • 1
  • 24
  • 50
  • Thank you. Problem 2 was solved in my test program, I still got a very thin line in some zoom states (problem 1) though, but that's the minor problem of the two. – Nostromo Sep 20 '17 at 13:22
  • @Nostromo regarding problem 1, did you increase your negative margin for the selected `TabItem`? – grek40 Sep 20 '17 at 13:41
  • Yes, the line has a height of 2px. Even when I increase the negative margin to -10px this thin line appears in higher zoom states. And it appears at the same place, so it's not coming from the object with the negative margin. – Nostromo Sep 22 '17 at 05:24
  • @Nostromo try setting a margin for `PART_SelectedContentHost` to see if the thin line moves with the content or stays at the same position. If it's not comming from within the content area, I'm out of ideas... – grek40 Sep 22 '17 at 07:47
  • Putting a margin there changes nothing, but thank you anyway for your help. I think I can live with that thin line for now. – Nostromo Sep 25 '17 at 06:02