【问题标题】:WPF Flexible TabControl HeaderWPF 灵活的 TabControl 标头
【发布时间】:2015-04-14 07:55:34
【问题描述】:

我想要一个TabControl 和多个TabItems。这些TabItems 每个都有一个标题文本。这些文本的长度可能相差很大(如 5 个字符长和 15 个字符长)。

我希望TabControl 仅在一行中对齐标题。

所有选项卡标题应使用相同的宽度,并且当有足够的可用空间时,我希望它们使用所有可用空间,最多为 MaxWidth,这对于所有项目都是相同的。

因此,如果我想为 7 个项目使用 100 的 vMaxWidth`,则选项卡标题的最大宽度应为 700。如果有更多可用空间,则应忽略。

如果可用空间较少,我希望该空间在项目之间平均分配。如果文本被截断,我想使用TextWrapping

我已经尝试了多种方法来解决这个问题,这是我目前的设置:

<Style x:Key="Style-TabControl-Main" TargetType="{x:Type TabControl}">
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid KeyboardNavigation.TabNavigation="Local">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                    </Grid.ColumnDefinitions>

                    <Border BorderThickness="0,0,0,1" Margin="13,0,0,0" BorderBrush="{StaticResource Brush-White}">
                        <StackPanel Panel.ZIndex="1" x:Name="HeaderPanel" IsItemsHost="True" KeyboardNavigation.TabIndex="1" Background="Transparent" 
                                    Orientation="Horizontal"/>
                    </Border>

                    <Border x:Name="Border"
                            Grid.Row="1" Grid.ColumnSpan="2"
                            KeyboardNavigation.TabNavigation="Local"
                            KeyboardNavigation.DirectionalNavigation="Contained"
                            KeyboardNavigation.TabIndex="2"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

还有TabItemStyle

<Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}">
    <Setter Property="Height" Value="31"/>
    <Setter Property="Width" Value="180" />
    <Setter Property="Foreground" Value="{DynamicResource Brush-BrightRegular-Foreground}"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabItem}">
                <Border x:Name="Border" Cursor="Hand" 
                        Margin="2,0,0,0"
                        BorderThickness="1,1,1,0"
                        CornerRadius="4,4,0,0"
                        BorderBrush="{DynamicResource Brush-BrightRegular-Background}"
                        Background="{DynamicResource Brush-White}">
                    <ContentPresenter x:Name="Content" VerticalAlignment="Center" HorizontalAlignment="Stretch" ContentSource="Header" RecognizesAccessKey="True"
                                        TextBlock.TextAlignment="Center" TextBlock.FontSize="16" />
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected"   Value="True">
                        <Setter  Property="Foreground" Value="{DynamicResource Brush-White}"/>
                        <Setter TargetName="Border"  Property="Background" Value="{DynamicResource Brush-DefaultDark-Background}" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我使用StackPanel 而不是TabPanel 来消除在调整默认TabControl 大小时发生的“堆叠”。但是,我无法满足我的其余要求。我尝试将MaxWidth(而不是固定宽度)应用于TabItem 标头,但这当然不起作用,因为该项目会缩小到所需的最小尺寸。

【问题讨论】:

  • 我之前在 TabControl 上工作过,所以我不能保证这会起作用,但首先你不需要&lt;Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}"&gt; 一个简单的&lt;Style TargetType="{x:Type TabItem}"&gt; 就可以了,其次删除@987654338 @ 和HorizontalAlignment 来自您的ContentControl,我注意到这会弄乱拉伸内容的逻辑。顺便说一句,尝试将 ContentPresenter 包装在 DockPanel 中。 HTH
  • 我实际上需要键入,因为我使用了多种不同的 TabControl 和 TabItem 样式。这只是其中之一!

标签: wpf xaml tabcontrol


【解决方案1】:

第 1 步(第一次尝试):将标题放在一行中,并为每个标题提供相同的宽度。

这可以通过使用 UniformGrid 而不是标准 TabPanel 来实现,并将其行数锁定为 1。这是您的 TabControl 样式的精简版:

<Style x:Key="Style-TabControl-Main" TargetType="{x:Type TabControl}">
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type TabControl}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>

                    <Border>
                        <UniformGrid x:Name="HeaderPanel" IsItemsHost="True" 
                                     Rows="1" />
                    </Border>

                    <Border x:Name="Border" Grid.Row="1" 
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" />
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

第 2 步:将标题限制为 MaxWidth 并应用文本换行。

MaxWidth 可以设置为TabItem 样式,以及用于包裹文本的HeaderTemplate(您仍然可以在此处使用自定义ControlTemplate 来设置 TabItem 部分的样式):

<Style x:Key="Style-TabItem-Main" TargetType="{x:Type TabItem}">
    <Setter Property="MaxWidth" Value="100" />
    <!--https://social.msdn.microsoft.com/forums/vstudio/en-US/df4f7fc3-f0ec-4ed1-a022-a32650e49cb3/how-to-wrap-header-text-in-tabcontrol-->
    <Setter Property="HeaderTemplate" >
        <Setter.Value>
            <DataTemplate>
                <TextBlock Text="{Binding}" TextWrapping="Wrap" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        ...
    </Setter>
</Style>


疑难解答:现在,如果您在步骤 2 中应用 MaxWidth,您可能希望在TabControl 太宽了..

<UniformGrid x:Name="HeaderPanel" IsItemsHost="True" 
             Rows="1" HorizontalAlignment="Left" />

..但您不希望在尚未达到 MaxWidth 时出现这种情况,并且项目应该延伸到 TabControl 的整个宽度(又名 Step 1)。因此,我们需要一种方法来切换 HorizontalAlignment,具体取决于是否已达到项目的 MaxWidth(如果已设置)。

第 1 步(重温):让我们尝试制作自己的 UniformGrid:

public class UniformTabPanel : UniformGrid
{
    public UniformTabPanel()
    {
        this.IsItemsHost = true;
        this.Rows = 1;

        //Default, so not really needed..
        this.HorizontalAlignment = HorizontalAlignment.Stretch;
    }

    protected override Size MeasureOverride(Size constraint)
    {
        var totalMaxWidth = this.Children.OfType<TabItem>().Sum(tab => tab.MaxWidth);
        if (!double.IsInfinity(totalMaxWidth))
        {
            this.HorizontalAlignment = (constraint.Width > totalMaxWidth) 
                                                ? HorizontalAlignment.Left 
                                                : HorizontalAlignment.Stretch;
        }

        return base.MeasureOverride(constraint);
    }
}

现在,我们可以在这个新面板的TabControl 样式中替换UniformGrid

                ...
                    <Border>
                        <mycontrols:UniformTabPanel x:Name="HeaderPanel" />
                    </Border>
                ...

...TabControl 应该按预期运行。

【讨论】:

  • 我试过你的代码,它几乎就在那里。但是,如果我调整 tabcontrol 的大小,使其大于所有 maxwidths 的总和,则选项卡项标题将仅采用可用的最小可用大小。它看起来仍然很奇怪。
【解决方案2】:

Sphinxx 的答案是正确的,但是我需要将以下代码添加到 UniformTabPanel 中,以使其按我想要的方式工作(当有足够的可用空间时将标题调整为 maxwidth)

我将以下代码添加到 UniformTabPanel,它现在可以满足我的需要:

protected override Size MeasureOverride(Size constraint)
{
    var children = this.Children.OfType<TabItem>();
    var totalMaxWidth = children.Sum(tab => tab.MaxWidth);
    if (!double.IsInfinity(totalMaxWidth))
    {
        this.HorizontalAlignment = (constraint.Width > totalMaxWidth)
                                            ? HorizontalAlignment.Left
                                            : HorizontalAlignment.Stretch;
        foreach (var child in children)
        {
            child.Width = this.HorizontalAlignment == System.Windows.HorizontalAlignment.Left
                    ? child.MaxWidth
                    : Double.NaN;
        }
    }
    return base.MeasureOverride(constraint);
}

【讨论】:

  • 很高兴你解决了它:) 如果你不想改变每个 TabItem 的宽度,在这种情况下另一个选项可能是设置this.MinWidth = totalMaxWidth
猜你喜欢
  • 2021-09-11
  • 1970-01-01
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2011-07-05
相关资源
最近更新 更多