GridSplitter с минимальными ограничениями

Я хочу создать сетку с двумя строками и разделителем между ними. Строки должны иметь минимальную высоту 80 пикселей.

Этот код отлично работает:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" MinHeight="80" />
        <RowDefinition Height="5" />
        <RowDefinition Height="*" MinHeight="80" />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
    <GridSplitter Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Red" />
    <TextBlock Grid.Row="2" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
</Grid>

Но я хочу, чтобы верхняя строка имела автоматическую высоту, пока пользователь не изменил ее вручную с помощью разделителя. Поэтому я изменил код на это:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" MinHeight="80" />
        <RowDefinition Height="5" />
        <RowDefinition Height="*" MinHeight="80" />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
    <GridSplitter Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Red" />
    <TextBlock Grid.Row="2" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
</Grid>

И есть проблема. Splitter все еще удовлетворяет ограничениям строк, но он начинает увеличивать высоту верхней строки бесконечно, если я слишком сильно перетаскиваю сплиттер. Это приведет к тому, что нижняя строка будет полностью ниже нижней границы окна.

Я сделал некоторый Reflector на коде GridSplitter и вижу, что он использует другую логику, если строки имеют Auto или Star height.

Любые предложения, как я могу "исправить" его?

Ответ 1

Я разработал обходной путь для этой проблемы. Точка - установить MaxHeight для верхней строки, пока мы перетаскиваем разделитель. Здесь код:

public class FixedGridSplitter : GridSplitter
{
    private Grid grid;
    private RowDefinition definition1;
    private double savedMaxLength;

    #region static

    static FixedGridSplitter()
    {
        new GridSplitter();
        EventManager.RegisterClassHandler(typeof(FixedGridSplitter), Thumb.DragCompletedEvent, new DragCompletedEventHandler(FixedGridSplitter.OnDragCompleted));
        EventManager.RegisterClassHandler(typeof(FixedGridSplitter), Thumb.DragStartedEvent, new DragStartedEventHandler(FixedGridSplitter.OnDragStarted));
    }

    private static void OnDragStarted(object sender, DragStartedEventArgs e)
    {
        FixedGridSplitter splitter = (FixedGridSplitter)sender;
        splitter.OnDragStarted(e);
    }

    private static void OnDragCompleted(object sender, DragCompletedEventArgs e)
    {
        FixedGridSplitter splitter = (FixedGridSplitter)sender;
        splitter.OnDragCompleted(e);
    }

    #endregion

    private void OnDragStarted(DragStartedEventArgs sender)
    {            
        grid = Parent as Grid;
        if (grid == null)
            return;            
        int splitterIndex = (int)GetValue(Grid.RowProperty);
        definition1 = grid.RowDefinitions[splitterIndex - 1];
        RowDefinition definition2 = grid.RowDefinitions[splitterIndex + 1];
        savedMaxLength = definition1.MaxHeight;            

        double maxHeight = definition1.ActualHeight + definition2.ActualHeight - definition2.MinHeight;            
        definition1.MaxHeight = maxHeight;
    }

    private void OnDragCompleted(DragCompletedEventArgs sender)
    {
        definition1.MaxHeight = savedMaxLength;
        grid = null;
        definition1 = null;
    }
}

Затем просто замените GridSplitter на FixedGridSplitter.

Примечание: этот код не является общим - он не поддерживает столбцы и предполагает, что нижняя строка указана в MinHeight.

Ответ 2

Я столкнулся с этой проблемой несколько раз сам. Кажется, что GridSplitter не очень хорошо работает с Auto. Тем не менее, я нашел потенциальное обходное решение.

Вы можете указать значение объекта GridLength, используя "звездные коэффициенты". Это действует как множитель для рассматриваемой длины.

В вашем примере, если вы берете строку, которую хотите оставить звездой, и установите коэффициент звезды на действительно большое число, строка займет все свободное пространство (заставляя другую строку становиться ее мини-высотой). Хотя это не то же поведение, что и "auto" (высота первой строки не определяется ее высотой содержимого), она может приблизиться к вам.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" MinHeight="80" />
        <RowDefinition Height="5" />
        <RowDefinition Height="10000*" MinHeight="80" />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
    <GridSplitter Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Red" />
    <TextBlock Grid.Row="2" Text="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}" />
</Grid>