停止Gridsplitter将内容拉伸超出窗口

24

对于以下的XAML,如何让网格分隔条遵守第三个行的MinHeight,并保持内容在窗口内部?

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition MinHeight="40" />
    </Grid.RowDefinitions>
    <Expander Grid.Row="0" ExpandDirection="Down" VerticalAlignment="Top">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" MinHeight="40" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Border Grid.Row="0" MinHeight="100" Background="Black" />
            <GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="LightBlue" ResizeBehavior="PreviousAndCurrent" />
        </Grid>
    </Expander>
    <Expander Grid.Row="1" ExpandDirection="Down" VerticalAlignment="Top">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" MinHeight="40" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Border Grid.Row="0" MinHeight="100" Background="Black" />
            <GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="LightBlue" ResizeBehavior="PreviousAndCurrent" />
        </Grid>
    </Expander>
    <Border DockPanel.Dock="Bottom"  Grid.Row="2" Background="Lime" MinHeight="30" >
        <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DockPanel},Path=ActualHeight,StringFormat={}{0:f0}}" />
    </Border>
</Grid>

您需要一个纯Xaml的解决方案,还是在代码后台添加代码可以接受? - Ed Bayiates
3个回答

23

按照你现有的代码,无法实现这个功能。这是由于GridSplitter的工作方式所决定的。

以下是一些要点:

  • GridSplitter只会在直接相邻的行/列上起作用
  • 实际上,虽然你的MinHeight被尊重了,但是GridSplitter请求扩大大小也被尊重了,这导致Grid比你的窗口更大
  • 当设置为Auto时,行/列将始终根据其内容进行调整大小,不会变得更大或更小
  • 因此,如果一个GridSplitter夹在两个*大小的行/列之间,那么它将隐式尊重你的MinHeight,因为实际上它并没有接触它

你有几种解决方案:

  1. 在第三个位置添加另一行,其大小为星号(*),并在第三行上具有RowSpan=2的边框(因此第三行实际上是被真正调整大小的行,而你的第四行则不受影响。也会产生副作用。
  2. 在GridSplitter上处理DragEnter和PreviewMouseMove事件的混合,跟踪焦点,并在达到一定大小时取消(e.Handled = true)该事件。

这是我所能想到的,希望对你有所帮助。


3

我创建了一个自定义的网格拆分器类,它不会允许网格拆分器超出窗口边缘(无论是底部还是侧边)。

Public Class CustomGridSplitter
Inherits GridSplitter

Public Enum SplitterDirectionEnum
    Horizontal
    Vertical
End Enum

Public Property SplitterDirection As SplitterDirectionEnum
Public Property MinimumDistanceFromEdge As Integer

Private _originPoint As Point

Private Sub customSplitter_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles MyBase.MouseDown
    _originPoint = e.GetPosition(Window.GetWindow(Me))
End Sub

Private Sub customSplitter_PreviewMouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.PreviewMouseMove

    If e.LeftButton = MouseButtonState.Pressed Then
        Dim pwindow As Window = Window.GetWindow(Me)
        Dim newPoint As Point = e.GetPosition(pwindow)

        If SplitterDirection = SplitterDirectionEnum.Horizontal Then
            If newPoint.Y >= _originPoint.Y Then
                If newPoint.Y >= pwindow.ActualHeight - MinimumDistanceFromEdge Then
                    e.Handled = True
                End If
            Else
                If newPoint.Y > pwindow.ActualHeight - (MinimumDistanceFromEdge + 2) Then
                    e.Handled = True
                End If
            End If
        Else
            If newPoint.X >= _originPoint.X Then
                If newPoint.X >= pwindow.ActualWidth - MinimumDistanceFromEdge Then
                    e.Handled = True
                End If
            Else
                If newPoint.X > pwindow.ActualWidth - (MinimumDistanceFromEdge + 2) Then
                    e.Handled = True
                End If
            End If
        End If


        _originPoint = newPoint
    End If
End Sub

结束类

在XAML中使用:

<CustomGridSplitter SplitterDirection="Vertical" MinimumDistanceFromEdge="100" x:Name="splitterCenter" ResizeDirection="Columns" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Width="2" Margin="2,0,2,0"/>

定制的属性包括 "SplitterDirection" 和 "MinimumDistanceFromEdge"。一切都和基础的网格分隔器类似。
该程序使用鼠标事件来确定用户在窗口中拖动分隔器时的位置,并在其靠近边缘时处理事件。

在将其转换为C#并包含对基类的必要调用后,它按预期工作。正是我所需要的。点赞。 - josh

-1

我找到了另一个解决这个问题的方法,尽管在一个更简单的情况下,我只是想调整窗口内的两列。

我想出的解决方案(在这里有更详细的描述:https://dev59.com/v3XYa4cB1Zd3GeqP98Oy#46924893)是添加事件回调,当网格被调整大小、GridSplitter移动和窗口被调整大小时(处理调整窗口大小以不再适合内容的情况,因为网格不会自动调整大小以适应较小的窗口)。

这是一些简化的代码:

XAML:

<Grid x:Name="ResizeGrid" SizeChanged="ResizeGrid_SizeChanged">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="C0" Width="150" MinWidth="50" />
        <ColumnDefinition Width="5" />
        <ColumnDefinition x:Name="C2" Width="*" MinWidth="50" />
    </Grid.ColumnDefinitions>

    <Grid Grid.Column="0" Background="Green" />
    <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" DragCompleted="GridSplitter_DragCompleted" />
    <Grid Grid.Column="2" Background="Red" />
</Grid>

C# 代码后台:

C0.MaxWidth = Math.Min(ResizeGrid.ActualWidth, ActualWidth) - (C2.MinWidth + 5);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接