Расширители в сетке

Это будет прямо, без сомнения, но по какой-то причине мой разум рисует пробел.

У меня есть небольшое, не изменяемое по размеру окно (325x450), в котором есть 3 экспандера, уложенных вертикально. Каждый Expander содержит элемент ItemsControl, который может иметь много элементов и, следовательно, должен прокручиваться.

То, что я не могу понять, - это как развернуть Expanders так, чтобы они расширялись, чтобы заполнить любое пространство, которое доступно, не вытесняя другие элементы с экрана. Я могу добиться того, что мне нужно, используя Grid и помещая каждый расширитель в строку с высотой *, но это означает, что они всегда занимают 1/3 окна, каждый из которых побеждает точку Expander:)

Crappy диаграмма того, чего я пытаюсь достичь:

enter image description here

Ответы

Ответ 1

Если вы не против небольшого кода, вы, вероятно, можете подключиться к событиям Expanded/Collapsed, найти родительский Grid, получить RowDefinition для расширителя и установить значение равный *, если его расширен, или Auto, если нет.

Например,

Expander ex = sender as Expander;
Grid parent = FindAncestor<Grid>(ex);
int rowIndex = Grid.GetRow(ex);

if (parent.RowDefinitions.Count > rowIndex && rowIndex >= 0)
    parent.RowDefinitions[rowIndex].Height = 
        (ex.IsExpanded ? new GridLength(1, GridUnitType.Star) : GridLength.Auto);

И метод FindAncestor определяется следующим образом:

public static T FindAncestor<T>(DependencyObject current)
where T : DependencyObject
{
    // Need this call to avoid returning current object if it is the 
    // same type as parent we are looking for
    current = VisualTreeHelper.GetParent(current);

    while (current != null)
    {
        if (current is T)
        {
            return (T)current;
        }
        current = VisualTreeHelper.GetParent(current);
    };
    return null;
}

Ответ 2

Это требование немного мало, потому что вы хотите, чтобы состояние Children в Grid определяло Height для RowDefinition, в котором они находятся.
Мне действительно нравится идея компоновки, хотя я не могу поверить, что у меня никогда не было такого же требования...:)

Для многоразового решения я бы использовал Attached Behavior для Grid.
Поведение будет подписано на Attached Events Expander.Expanded и Expander.Collapsed, а в обработчиках событий получите RowDefinition из Grid.GetRow и соответствующим образом обновить Height. Он работает следующим образом

<Grid ex:GridExpanderSizeBehavior.SizeRowsToExpanderState="True">
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Expander Grid.Row="0" ... />
    <Expander Grid.Row="1" ... />
    <Expander Grid.Row="2" ... />
    <!-- ... -->
</Grid>

И вот GridExpanderSizeBehavior

public class GridExpanderSizeBehavior
{
    public static DependencyProperty SizeRowsToExpanderStateProperty =
        DependencyProperty.RegisterAttached("SizeRowsToExpanderState",
                                            typeof(bool),
                                            typeof(GridExpanderSizeBehavior),
                                            new FrameworkPropertyMetadata(false, SizeRowsToExpanderStateChanged));
    public static void SetSizeRowsToExpanderState(Grid grid, bool value)
    {
        grid.SetValue(SizeRowsToExpanderStateProperty, value);
    }
    private static void SizeRowsToExpanderStateChanged(object target, DependencyPropertyChangedEventArgs e)
    {
        Grid grid = target as Grid;
        if (grid != null)
        {
            if ((bool)e.NewValue == true)
            {
                grid.AddHandler(Expander.ExpandedEvent, new RoutedEventHandler(Expander_Expanded));
                grid.AddHandler(Expander.CollapsedEvent, new RoutedEventHandler(Expander_Collapsed));
            }
            else if ((bool)e.OldValue == true)
            {
                grid.RemoveHandler(Expander.ExpandedEvent, new RoutedEventHandler(Expander_Expanded));
                grid.RemoveHandler(Expander.CollapsedEvent, new RoutedEventHandler(Expander_Collapsed));
            }
        }
    }
    private static void Expander_Expanded(object sender, RoutedEventArgs e)
    {
        Grid grid = sender as Grid;
        Expander expander = e.OriginalSource as Expander;
        int row = Grid.GetRow(expander);
        if (row <= grid.RowDefinitions.Count)
        {
            grid.RowDefinitions[row].Height = new GridLength(1.0, GridUnitType.Star); 
        }
    }
    private static void Expander_Collapsed(object sender, RoutedEventArgs e)
    {
        Grid grid = sender as Grid;
        Expander expander = e.OriginalSource as Expander;
        int row = Grid.GetRow(expander);
        if (row <= grid.RowDefinitions.Count)
        {
            grid.RowDefinitions[row].Height = new GridLength(1.0, GridUnitType.Auto);
        }
    }
}