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>
问题现象截图

解决方案代码
<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"> <
相关产品推荐
相关产品推荐

