10
votes

The WPF TabControl with its default TabPanel arranges tab items into multiple rows when the horizontal size is too small. Then the tab selection changes the order of these rows, so the selected tab item is always in the first row.

I found several articles on how to replace TabPanel with another items control so instead of the multiline behavior they get scrolling tabs.

I would like to keep the multiple rows (no scrolling), but disable the rearrangement of rows. Once the tabs are created, they should stay in position, no matter how the selection changes. Is this possible?

2
this probably is possible, but note that if you select a tab in the upper row and the rows don't switch you have a tab header that's disconnected from a tab (which might be awkward). so is that really what you want?? - Markus Hütter
You may be right but I have customized its appearance as well, and in this particular case the disconnection is not a problem. - vigoo
This is a problem here too, pity there is no solution. The row switching of multirow tabs stops the eye remembering their pattern and makes reordering by dragging difficult. It taxes attention and can be frustrating once recognised. - strainer

2 Answers

4
votes

have you tried overriding the default style with something like this? ie: using a wrappanel instead of a TabPanel?

<Style x:Key="{x:Type TabControl}" TargetType="{x:Type TabControl}">        
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">                    
                <Grid TabNavigation="Local" SnapsToDevicePixels="true" ClipToBounds="true">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Name="ColumnDefinition0" />
                        <ColumnDefinition Name="ColumnDefinition1" Width="0" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Name="RowDefinition0" Height="Auto" />
                        <RowDefinition Name="RowDefinition1" Height="*" />
                    </Grid.RowDefinitions>
                    <WrapPanel Name="HeaderPanel" ZIndex="1" TabIndex="1" Column="0" Row="0" Margin="2,2,2,0" IsItemsHost="true" />
                    <Border Name="ContentPanel" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" TabNavigation="Local" DirectionalNavigation="Contained" TabIndex="2" Column="0" Row="1">
                        <ContentPresenter Name="PART_SelectedContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="{TemplateBinding Padding}" ContentSource="SelectedContent" />
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
0
votes

The only solution I found was modifying the framework's TabPanel class so that its int GetActiveRow(int[] solution) method always returns 0. Although this solves the problem, I'm not sure it is legal to use the framework's source in this way.