让 WPF Tabcontrol 的高度自适应最大项的高度?

15
有没有办法让选项卡控件(TabControl)的大小与最大的选项卡项(实际上是选项卡项的内容)一样大?由于TabControl没有指定特定的大小,因此应该自动调整大小:它确实可以正确地完成这一点,但当您切换选项卡时,它会自动调整大小以适应当前选定选项卡的内容的高度(和宽度)。
我不希望发生自动调整大小,并让TabControl假定最大项的高度,但仍然让它自动调整大小。有什么提示吗?我尝试使用多绑定将用作内容的每个元素的Height属性数据绑定到TabControl的ActualHeight和Items属性的绑定,但是Content元素的ActualHeight始终为0。
        <TabItem Header="Core" > 
            <Grid Margin="5">
                <Grid.Height>
                    <MultiBinding Converter="{Converters1:AllTabContentEqualHeightConverter}">
                        <Binding Path="ActualHeight" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}"/>
                        <Binding Path="Items" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type TabControl}}"/>
                    </MultiBinding>
                </Grid.Height>

            ...

这能做到吗?

5个回答

28

可以做到: 如何为每个TabItem重复使用Grid的行定义

示例:

    <TabControl Grid.IsSharedSizeScope="True">
        <TabItem Header="Tab 1">
            <Grid >
                <Grid.RowDefinitions>
                    <RowDefinition SharedSizeGroup="xxx"/>
                </Grid.RowDefinitions>
            </Grid>
        </TabItem>
        <TabItem Header="Tab 2">
            <Grid >
                <Grid.RowDefinitions>
                    <RowDefinition SharedSizeGroup="xxx"/>
                </Grid.RowDefinitions>
            </Grid>
        </TabItem>
   </TabControl>

4
关键是要包括 Grid.IsSharedSizeScope="True",第一次我错过了它 - 如果没有它,对我来说什么也没改变。另外请注意,您不必将 TabControl 放在一个网格中来应用该属性。 - Jon Peterson
1
有没有办法通过DataTemplate来应用这个? 我已经将一个TabControl绑定到一个集合以填充其选项卡。 我尝试设置行和列定义以在所有选项卡之间共享宽度/高度,但没有成功(我在TabControl上设置了Grid.IsSharedSizeScope =“True”) - Zepee
这个解决方案确实有效。我在数据模板中尝试过它。但是,根据数据模板的使用位置,它可能会导致用户界面非常缓慢。 - Florian

7
问题在于TabControl在切换选项卡时会卸载并重新加载其内容。因此,它只知道当前活动选项卡中内容的大小。您应该能够更改TabControl,使其永远不会销毁其子元素,并且它们始终存在(但可能是隐藏的)。
Eric Burke的这篇博客文章可以帮助您入门。从我粗略浏览他的文章得出的结论是,您需要进行以下更改:
  • TabControl加载时加载所有子元素。
  • 当子元素处于非活动状态时,将其隐藏而不是折叠。

那似乎是一个不错的方法,但我已经自己解决了。无论如何,还是谢谢。 - Inferis

5

实际上,解决这个问题比我想象的要容易。由于我已经有了一个控制TabControl的控件模板,因此我设置了呈现所选选项卡内容的ContentPresenter的高度。我使用绑定到TabControl的项目的转换器来完成这个操作,如果需要的话(使用Measure),对它们进行测量,并检查所需大小DesiredSize是否符合我需要的尺寸。

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var items = value as ItemCollection;

        if (items == null)
            return null;

        double max = 0;
        foreach (TabItem item in items)
        {
            var content = item.Content as FrameworkElement;
            if (content == null) continue;

            if (!content.IsMeasureValid)
                content.Measure(new Size(int.MaxValue, int.MaxValue));

            var height = content.DesiredSize.Height;
            if (max < height)
                max = height;
        }

        return max;
    }

这个方案是可行的,但有一些注意事项:

  • 每个选项卡的内容应该是一个FrameworkElement
  • 一旦加载完成,内容大小不会改变(因为转换器只在Items属性更改时调用一次)。

1
嗨@Inferis,你能告诉我如何绑定选项卡控件中的TabItem集合吗?我似乎找不到答案。 - Zepee

3

这个方法和上述展示的Grid.IsSharedSizeScope方法结合使用适用于我。

请注意,这里使用了SetCurrentValue而不是仅设置SelectedIndex属性——这样我们就保留了可能存在的数据绑定:

private void TabControl_OnLoaded(object sender, RoutedEventArgs e)
{
    //NOTE: loop through tab items to force measurement and size the tab control to the largest tab
    TabControl tabControl = (TabControl)sender;

    // backup selection
    int indexItemLast = tabControl.SelectedIndex;

    int itemCount = tabControl.Items.Count;

    for (
        int indexItem = (itemCount - 1);
        indexItem >= 0;
        indexItem--)
    {
        tabControl.SetCurrentValue(Selector.SelectedIndexProperty, indexItem);
        tabControl.UpdateLayout();
    }

    // restore selection
    tabControl.SetCurrentValue(Selector.SelectedIndexProperty, indexItemLast);
}

1

这可能不是正宗的 WPF 方式,但如果你已经拥有了所有内容元素,你可以在加载时通过循环来编程设置 TabControl 的高度。


这是唯一对我有效的解决方案(结合Grid.IsSharedSizeScope),尽管必须在每个通过编程方式选择的选项卡上调用TabControl.UpdateLayout()以强制进行布局测量。 - Libor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接