使用UniformGrid在TabControl WPF中设置标题高度

6za6bjd0  于 2023-05-30  发布在  其他
关注(0)|答案(1)|浏览(182)

我正试图使一个WPF应用程序的垂直TabControl部分,并希望标题按钮,以填补屏幕的高度(下降到MinHeight),并统一的大小。我希望这可以通过为TabControl和TabItem模板创建自定义模板来完成。

标签控件模板

<Style TargetType="{x:Type TabControl}">
            <Setter Property="Padding" Value="2"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Background" Value="{StaticResource TabItem.Selected.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource TabItem.Selected.Border}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TabControl}">
                        <Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition x:Name="ColumnDefinition0"/>
                                <ColumnDefinition x:Name="ColumnDefinition1"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
                                <RowDefinition x:Name="RowDefinition1" Height="*"/>
                            </Grid.RowDefinitions>
                            <TabPanel x:Name="headerPanel" Background="Transparent" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
                            <Border x:Name="contentPanel" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" 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="TabStripPlacement" Value="Left">
                                <Setter Property="Width" TargetName="headerPanel" Value="80"/>
                                <Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
                                <Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
                                <Setter Property="Grid.Column" TargetName="headerPanel" Value="0"/>
                                <Setter Property="Grid.Column" TargetName="contentPanel" Value="1"/>
                                <Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
                                <Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
                                <Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
                                <Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
                                <Setter Property="Margin" TargetName="headerPanel" Value="2,2,0,2"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

我已经能够通过设置属性来手动设置每个TabItem标题的高度,但是当用户更改窗口大小时,这不是动态的

kmpatx3s

kmpatx3s1#

您不应该使用UniformGrid来替换TabPanel,因为这样会丢失相关的特性,比如正确的多行选项卡项目换行和其他特定于TabControl的项目布局行为。UnformGridTabPanel都使用完全不同的算法来排列它们的子节点。UnformGrid没有明确地荣誉TabControl的性质。

方案一

如果您不关心丢失功能或略有不同的布局,那么最简单的解决方案就是将TabPanel替换为UniformGrid。因为您希望垂直堆叠TabItem元素,所以无论如何都会丢失多行特性(默认情况下,多行排列仅适用于水平对齐,即TabStripPlacement设置为Dock.TopDock.Bottom)。

<ControlTemplate TargetType="{x:Type TabControl}">
  <Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
    <Grid.ColumnDefinitions>
      <ColumnDefinition x:Name="ColumnDefinition0"/>
      <ColumnDefinition x:Name="ColumnDefinition1"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
      <RowDefinition x:Name="RowDefinition1" Height="*"/>
    </Grid.RowDefinitions>
    
    <UniformGrid x:Name="headerPanel" 
                 Background="Transparent" 
                 Grid.Column="0" 
                 IsItemsHost="true" 
                 Margin="2,2,2,0" 
                 Grid.Row="0" 
                 KeyboardNavigation.TabIndex="1" 
                 Panel.ZIndex="1" />

    <Border x:Name="contentPanel" 
            Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}" 
            Grid.Column="0" 
            KeyboardNavigation.DirectionalNavigation="Contained" 
            Grid.Row="1" 
            KeyboardNavigation.TabIndex="2" 
            KeyboardNavigation.TabNavigation="Local">
      <ContentPresenter x:Name="PART_SelectedContentHost" 
                        ContentSource="SelectedContent" 
                        Margin="{TemplateBinding Padding}" 
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> 
    </Border>
  </Grid>
</ControlTemplate>

方案二

一个更简洁的解决方案,尊重TabControl的特殊布局要求,是扩展TabPanel
在下面的示例中,新类UniformTabPanel扩展了TabPanel并覆盖了垂直排列的布局排列。
对于水平排列,UniformTabPanel面板将回退到TabPanel的默认布局算法。
要使用UniformTabPanel,只需将TabPanel替换为TabControlControlTemplate

UniformTabPanel.cs

public class UniformTabPanel : TabPanel
{
  protected override Size ArrangeOverride(Size arrangeSize)
  {
    Dock itemPlacement = this.TemplatedParent is TabControl tabControl
      ? tabControl.TabStripPlacement
      : Dock.Left;  // Prefer vertical stacking for this panel

    if (itemPlacement is Dock.Left or Dock.Right)
    {
      ArrangeVertical(arrangeSize);
      return arrangeSize;
    }
    else
    {
      // Apply default layout behavior
      return base.ArrangeOverride(arrangeSize);
    }
  }

  private void ArrangeVertical(Size arrangeSize)
  {
    double uniformDesiredChildHeight = GetUniformItemHeight(arrangeSize);
    double childOffsetY = 0d;

    foreach (UIElement child in this.InternalChildren)
    {
      if (child.Visibility != Visibility.Collapsed)
      {
        Size childSize = GetDesiredSizeWithoutMargin(uniformDesiredChildHeight, child);
        child.Arrange(new Rect(0, childOffsetY, arrangeSize.Width, childSize.Height));

        // Calculate the offset for the next child
        childOffsetY += childSize.Height;
      }
    }
  }

  private double GetUniformItemHeight(Size arrangeSize)
  {
    double totalDesiredHeight = this.TemplatedParent is TabControl tabControl
      ? tabControl.DesiredSize.Height
      : arrangeSize.Height;

    return totalDesiredHeight / Math.Max(1, this.InternalChildren.Count);
  }

  private Size GetDesiredSizeWithoutMargin(double desiredChildHeight, UIElement element)
  {
    Thickness margin = (Thickness)element.GetValue(MarginProperty);
    Size desiredSizeWithoutMargin = new Size
    {
      Height = Math.Max(0d, desiredChildHeight - margin.Top - margin.Bottom),
      Width = Math.Max(0d, element.DesiredSize.Width - margin.Left - margin.Right)
    };

    return desiredSizeWithoutMargin;
  }
}
<ControlTemplate TargetType="{x:Type TabControl}">
  <Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
    <Grid.ColumnDefinitions>
      <ColumnDefinition x:Name="ColumnDefinition0"/>
      <ColumnDefinition x:Name="ColumnDefinition1"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition x:Name="RowDefinition0" Height="Auto"/>
      <RowDefinition x:Name="RowDefinition1" Height="*"/>
    </Grid.RowDefinitions>
    
    <local:UniformTabPanel x:Name="headerPanel" 
                           Background="Transparent" 
                           Grid.Column="0" 
                           IsItemsHost="true" 
                           Margin="2,2,2,0" 
                           Grid.Row="0" 
                           KeyboardNavigation.TabIndex="1" 
                           Panel.ZIndex="1" />

    <Border x:Name="contentPanel" 
            Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}" 
            Grid.Column="0" 
            KeyboardNavigation.DirectionalNavigation="Contained" 
            Grid.Row="1" 
            KeyboardNavigation.TabIndex="2" 
            KeyboardNavigation.TabNavigation="Local">
      <ContentPresenter x:Name="PART_SelectedContentHost" 
                        ContentSource="SelectedContent" 
                        Margin="{TemplateBinding Padding}" 
                        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> 
    </Border>
  </Grid>
</ControlTemplate>

相关问题