具有最小约束的GridSplitter

时间:2010-10-19 10:42:25

标签: wpf xaml 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仍然满足行约束,但如果我将分割器拖得太低,它会无限地开始增加顶行的高度。这导致底行完全低于窗口的底部边界。

我在GridSplitter代码上做了一些Reflector,看看如果行有Auto或Star高度,它会使用不同的逻辑。

任何建议如何“修复”它?

2 个答案:

答案 0 :(得分:6)

我自己曾经遇到过这个问题。似乎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>

答案 1 :(得分:6)

我已针对此问题制定了解决方法。点是在我们拖动拆分器时为顶行设置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;
    }
}

然后用FixedGridSplitter替换GridSplitter。

注意:此代码不是通用的 - 它不支持列,并假设底行指定了MinHeight。