具有最小约束的 GridSplitter

发布于 2024-09-28 17:08:27 字数 1508 浏览 1 评论 0原文

我想要一个带有两行和它们之间的分隔符的网格布局。行的最小高度应为 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>

还有一个问题。拆分器仍然满足行约束,但如果我将拆分器拖得太低,它就会开始无限增加顶行的高度。这会导致底部行完全位于窗口底部边框下方。

我在 GridSplitter 代码上做了一些 Reflector,发现如果行具有“自动”或“星形”高度,它会使用不同的逻辑。

有什么建议我如何“修复”它?

I want a Grid layout with two rows and splitter between them. Rows should have a minimum height of 80 pixels.

This code works great:

<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>

But I want top row to have an Auto height until user manually change it using the splitter. So I changed the code to this:

<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>

And there is a problem. Splitter still satisfies row constraints, but it begins to increase top row's height infinitely if I drag splitter too low. This results in bottom row to be completely below window's bottom border.

I have done some Reflector on GridSplitter code and see that it uses different logic if rows has Auto or Star height.

Any suggestions how can I "fix" it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一枫情书 2024-10-05 17:08:28

我已经为这个问题开发了一个解决方法。要点是在拖动分隔符时为顶行设置 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 替换为 FixGridSplitter。

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

I have developed a workaround for this problem. Point is to set MaxHeight for the top row while we are dragging splitter. Here the code:

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

Then just replace GridSplitter with FixedGridSplitter.

Note: this code is not general - it doesn't support columns and assume that bottom row has MinHeight specified.

◇流星雨 2024-10-05 17:08:27

我自己也遇到过几次这个问题。看起来 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>

I've run into this problem a few times myself. It appears as though the GridSplitter doesn't play well with Auto. That said, I have found a potential workaround.

You are able to specify the value of a GridLength object using "star coefficients". This acts as a multiplier for the length in question.

In your example, if you take the row you want to remain as star, and set the the star coefficient to a really large number, the row will take up all available space (forcing the other row to become its min-height). While this isn't the same behavior as "auto" (the height of the first row is not determined by its contents height), it might get you closer.

<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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文