You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF TabControl:如何强制TabItem标题单行显示并添加滚动按钮

WPF TabControl 单行滚动标签实现方案

问题描述

  • 当TabItem总宽度超出TabControl水平宽度时,TabItem自动换行重排,且Tab间距超出设置的边距
  • 选中某一行的TabItem时,整行下移,下方行上移

需求

保持Tab间距均匀,空间不足时强制所有TabItem标题单行显示,添加滚动按钮避免换行

原XAML代码

<TabControl Name="tabCtrlControl" Visibility="Visible" BorderBrush="#FFE3E3E2" 
          BorderThickness="0" Padding="0" TabStripPlacement="Top" Focusable="False" SelectedIndex="0">
            <TabControl.Resources>
                <Style TargetType="{x:Type TabItem}">
                    <Setter Property="BorderBrush" Value="#FFE3E3E2"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type TabItem}">
                                <Grid SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="auto"/>
                                        <RowDefinition Height="*" />
                                    </Grid.RowDefinitions>
                                    <ItemsControl>
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <WrapPanel Name="HeaderPanel" ZIndex="1" Grid.Column="0" Grid.Row="0" Margin="2,2,2,0" IsItemsHost="true" />
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                    </ItemsControl>
                                    <Border x:Name="Bd" BorderBrush="Black" BorderThickness="2" Margin="2,0,0,0" Grid.Row="0" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
                                        <ContentPresenter x:Name="Content" ContentSource="Header" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                                    </Border>
                                     </Grid>
                                <ControlTemplate.Triggers>
                                    <Trigger Property="IsSelected" Value="True">
                                        <Setter Property="Background" TargetName="Bd" Value="#FFF9F9F9"/>
                                        <Setter Property="Foreground" Value="#FF191919"/>
                                    </Trigger>
                                    <Trigger Property="IsSelected" Value="False">
                                        <Setter Property="Background" TargetName="Bd" Value="#FF191919"/>
                                        <Setter Property="Foreground" Value="#FFe3e3e2"/>
                                    </Trigger>
                                    <Trigger Property="IsEnabled" Value="false">
                                        <Setter Property="Background" TargetName="Bd" Value="#FFb3b3b3"/>
                                        <Setter Property="Foreground" Value="#FFe3e3e2"/>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </TabControl.Resources>
            <TabItem Name="tabItem1" HorizontalAlignment="Right" FontSize="14" Width="90" Height="Auto" MinHeight="30" FocusVisualStyle="{x:Null}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.FlowModeText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab1Frame" Source="FlowMode.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480"  FocusVisualStyle="{x:Null}" />
                </Grid>
            </TabItem>
            <TabItem Name="tabItem2" HorizontalAlignment="Right" FontSize="14" Width="108" MinHeight="30" Height="Auto" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}" FocusVisualStyle="{x:Null}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.StageOverrideText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab2Frame" Source="StageOverride.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
                </Grid>
            </TabItem>
            <TabItem Name="tabItem3" HorizontalAlignment="Right" FontSize="14" Width="108" MinHeight="30" Height="Auto" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}" FocusVisualStyle="{x:Null}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.ColourOverrideText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab3Frame" Source="ColourOverride.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480"  FocusVisualStyle="{x:Null}"/>
                </Grid>
            </TabItem>
            <TabItem Name="tabItem4" HorizontalAlignment="Right" FontSize="14" Width="108" MinHeight="30" Height="Auto" FocusVisualStyle="{x:Null}" Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=DataContext.IsDigitalOutputAvailable, Converter={StaticResource BooleanToVisibilityConverter}}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.DigitalOutputText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab4Frame" Source="DigitalOutput.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
                </Grid>
            </TabItem>
            <TabItem Name="tabItem5" Visibility="{Binding Path=IsAutomaticAdjustmentOfInteriorZoneEnabled, Converter={StaticResource BooleanToVisibilityConverter}}" HorizontalAlignment="Right" FontSize="14" Width="185" MinHeight="30" Height="Auto" FocusVisualStyle="{x:Null}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.InteriorZoneSensorStatusText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab5Frame" Source="InteriorZoneSensorStatusMonitor.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
                </Grid>
            </TabItem>
            <TabItem Name="tabItem6" Visibility="{Binding Path=ShowIndividualControlTab, Converter={StaticResource BooleanToVisibilityConverter}}" HorizontalAlignment="Right" FontSize="14" Width="128" MinHeight="30" Height="Auto" FocusVisualStyle="{x:Null}" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.IndividualControlText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab6Frame" Source="IndividualControl.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
                </Grid>
            </TabItem>
            <TabItem Name="tabItem7" Visibility="{Binding Path=ShowAdvancedControlTab, Converter={StaticResource BooleanToVisibilityConverter}}" HorizontalAlignment="Right" FontSize="14" Width="135" Height="Auto" MinHeight="30" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}" FocusVisualStyle="{x:Null}">
                <TabItem.Header>
                    <TextBlock TextWrapping="WrapWithOverflow" Text="{x:Static stringtranslation:Resources.AdvancedControlText}"/>
                </TabItem.Header>
                <Grid Background="Transparent">
                    <Frame Name="tab7Frame" Source="AdvancedControl.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
                </Grid>
            </TabItem>
        </TabControl>

问题现象截图

TabItem换行重排现象截图

解决方案代码

<TabControl Name="tabCtrlControl" Visibility="Visible" BorderBrush="#FFE3E3E2" 
            BorderThickness="0" Padding="0" TabStripPlacement="Top" Focusable="False" SelectedIndex="0">
    <TabControl.Template>
        <ControlTemplate TargetType="{x:Type TabControl}">
            <Grid SnapsToDevicePixels="true">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <!-- 顶部标签栏:添加ScrollViewer实现水平滚动 -->
                <ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" 
                              Margin="0" Padding="0">
                    <TabPanel x:Name="HeaderPanel" Panel.ZIndex="1" Margin="2,2,2,0" 
                              IsItemsHost="true" Background="Transparent"/>
                </ScrollViewer>
                <!-- 内容区域 -->
                <Border x:Name="ContentPanel" Grid.Row="1" BorderBrush="{TemplateBinding BorderBrush}" 
                        BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" 
                        KeyboardNavigation.DirectionalNavigation="Local" KeyboardNavigation.TabIndex="2" 
                        KeyboardNavigation.TabNavigation="Local">
                    <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" 
                                      Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </Border>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="IsEnabled" Value="false">
                    <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </TabControl.Template>
    <TabControl.Resources>
        <Style TargetType="{x:Type TabItem}">
            <Setter Property="BorderBrush" Value="#FFE3E3E2"/>
            <Setter Property="Padding" Value="6,2"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabItem}">
                        <Grid SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
                            <Border x:Name="Bd" BorderBrush="Black" BorderThickness="2" Margin="2,0,0,0" 
                                    Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}">
                                <ContentPresenter x:Name="Content" ContentSource="Header" 
                                                  HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" 
                                                  RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                                  VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsSelected" Value="True">
                                <Setter Property="Background" TargetName="Bd" Value="#FFF9F9F9"/>
                                <Setter Property="Foreground" Value="#FF191919"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="False">
                                <Setter Property="Background" TargetName="Bd" Value="#FF191919"/>
                                <Setter Property="Foreground" Value="#FFe3e3e2"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="Bd" Value="#FFb3b3b3"/>
                                <Setter Property="Foreground" Value="#FFe3e3e2"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </TabControl.Resources>
    <!-- TabItem保留原内容,移除水平对齐属性并禁用文本换行 -->
    <TabItem Name="tabItem1" FontSize="14" Width="90" MinHeight="30" FocusVisualStyle="{x:Null}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.FlowModeText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <Frame Name="tab1Frame" Source="FlowMode.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480"  FocusVisualStyle="{x:Null}" />
        </Grid>
    </TabItem>
    <TabItem Name="tabItem2" FontSize="14" Width="108" MinHeight="30" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}" FocusVisualStyle="{x:Null}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.StageOverrideText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <Frame Name="tab2Frame" Source="StageOverride.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
        </Grid>
    </TabItem>
    <TabItem Name="tabItem3" FontSize="14" Width="108" MinHeight="30" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}" FocusVisualStyle="{x:Null}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.ColourOverrideText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <Frame Name="tab3Frame" Source="ColourOverride.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480"  FocusVisualStyle="{x:Null}"/>
        </Grid>
    </TabItem>
    <TabItem Name="tabItem4" FontSize="14" Width="108" MinHeight="30" FocusVisualStyle="{x:Null}" Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=DataContext.IsDigitalOutputAvailable, Converter={StaticResource BooleanToVisibilityConverter}}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.DigitalOutputText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <Frame Name="tab4Frame" Source="DigitalOutput.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
        </Grid>
    </TabItem>
    <TabItem Name="tabItem5" Visibility="{Binding Path=IsAutomaticAdjustmentOfInteriorZoneEnabled, Converter={StaticResource BooleanToVisibilityConverter}}" FontSize="14" Width="185" MinHeight="30" FocusVisualStyle="{x:Null}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.InteriorZoneSensorStatusText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <Frame Name="tab5Frame" Source="InteriorZoneSensorStatusMonitor.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
        </Grid>
    </TabItem>
    <TabItem Name="tabItem6" Visibility="{Binding Path=ShowIndividualControlTab, Converter={StaticResource BooleanToVisibilityConverter}}" FontSize="14" Width="128" MinHeight="30" FocusVisualStyle="{x:Null}" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.IndividualControlText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <Frame Name="tab6Frame" Source="IndividualControl.xaml" NavigationUIVisibility="Hidden" Background="#FFF8FCFE" Height="480" FocusVisualStyle="{x:Null}"/>
        </Grid>
    </TabItem>
    <TabItem Name="tabItem7" Visibility="{Binding Path=ShowAdvancedControlTab, Converter={StaticResource BooleanToVisibilityConverter}}" FontSize="14" Width="135" MinHeight="30" IsEnabled="{Binding Path=SysModeRunning, Converter={StaticResource InverseBoolConverter}}" FocusVisualStyle="{x:Null}">
        <TabItem.Header>
            <TextBlock TextWrapping="NoWrap" Text="{x:Static stringtranslation:Resources.AdvancedControlText}"/>
        </TabItem.Header>
        <Grid Background="Transparent">
            <
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 00:00:56