Пусть WPF Tabcontrol height принимает высоту самого большого элемента?

Можно ли использовать tabcontrol для размера самого большого элемента табуляции (ну, собственно, содержимого tabitem)?

Поскольку tabcontrol не имеет определенного назначенного размера, он должен авторизоваться: он делает это правильно, но при переключении вкладок он автоматически изменяет размеры до высоты (и ширины) содержимого текущей выбранной вкладки.

Я не хочу, чтобы изменение размера происходило, и пусть tabcontrol принимает высоту самого большого элемента, но все же автоматически выполняет его автоопределение.

Любые подсказки? Я попробовал привязку данных к свойству Height каждого элемента, используемого в качестве содержимого, для использования многосвязности, с привязками к свойствам ActualHeight и Items Tabcontrol. Но, увы, элемент 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>

            ...

Можно ли это сделать?

Ответ 1

На самом деле, было легче решить, что я думал. Так как в любом случае у меня была контрольная таблица для 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, т.е. только один раз).

Ответ 2

Да, это можно сделать: reuse-grid-rowdefinitions-for-each-tabitem

Пример:

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

Ответ 3

Проблема заключается в том, что TabControl выгружает и перезагружает свой контент при переключении вкладок. Поэтому он знает только размер содержимого на текущей активной вкладке. Вы должны иметь возможность изменять TabControl таким образом, чтобы он никогда не уничтожал своих детей, и они всегда присутствуют (но могут быть скрыты).

Это сообщение в блоге от Эрика Берка должно начать вас. Из того, что я могу сказать, сократив его пост, вам нужно будет изменить его так, чтобы:

  • Все дети загружаются при загрузке TabControl.
  • Дети скрываются, а не рушится, когда они неактивны.

Ответ 4

Вероятно, это не соответствует правильному пути WPF, но, если у вас уже есть все элементы контента, вы могли бы пропустить их при загрузке и программно установить высоту TabControl.

Ответ 5

Это работало для меня в сочетании с подходом Grid.IsSharedSizeScope, показанным выше.

Обратите внимание, что вместо установки свойства SelectedIndex используется SetCurrentValue - таким образом мы сохраняем возможные существующие привязки:

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);
}