Stop Gridsplitter stretching content beyond window
Asked Answered
T

3

24

Given the below XAML, how do I have the gridsplitter respect the MinHeight given to the 3rd row and have the content remain inside my window?

<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>
Tentacle answered 4/5, 2011 at 6:46 Comment(1)
Do you require a solution purely in Xaml, or is adding code to the codebehind acceptable?Footplate
W
23

The way your code is, this cannot be done mate. This is due to how the GridSplitter works.

A few points

  • A GridSplitter will always work on directly adjacent rows/columns
  • In reality, your MinHeight IS being respected, but so is the GridSplitter's request to grow being respected, which results in the Grid growing larger than your Window
  • When sized to Auto, a row/column will always resize according to its content, not bigger, and not smaller
  • Therefore if a GridSplitter is sandwiched between two * sized rows/columns, then it would implicitly respect your MinHeight, since in reality, it would not be touching it

You have a few solutions

  1. Add another row in the 3rd position which is * sized, and have your border on row 3 with a RowSpan of 2 (so the 3rd row is the one being really resized, and your 4th row isn't touched. Though this will also have side-effects.
  2. Handle a mixture of DragEnter and PreviewMouseMove events on the GridSplitter, keeping track of focus, and cancelling (e.Handled = true) the event when a certain size is reached.

This is what I can think of mate, hope I've been of some help.

Wholesale answered 23/6, 2011 at 22:19 Comment(0)
P
3

I created a custom grid splitter class that will not allow the grid splitter to go off the edge of a window (either the bottom or the side).

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

End Class

To use it in 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"/>

The custom properties to set are the "SplitterDirection" and "MinimumDistanceFromEdge". Everything works like the base grid splitter.

This uses mouse events to determine where in the window the user is dragging the splitter and handles the events if they get too close to the edge.

Precipitation answered 3/5, 2016 at 13:37 Comment(1)
After having converted it to C# and included the necessary call to the base class, it worked as expected. Was exactly what I needed. Upvoted.Mace
D
-1

I found another solution to this problem, though in a much more simple case where I just had two columns inside a window that I wanted to resize.

The solution that I came up with (described in more detail here: https://mcmap.net/q/583450/-is-there-a-way-to-have-the-gridsplitter-not-to-push-elements-out-of-the-window) was to add event callbacks for when the grid was resized, when the GridSplitter moved, and when the window was resized (to handle the case where you resize the window to no longer fit the content because the grid doesn't automatically resize itself to fit the smaller window).

Here is some simplified code:

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# Code Behind:

C0.MaxWidth = Math.Min(ResizeGrid.ActualWidth, ActualWidth) - (C2.MinWidth + 5);
Dwaynedweck answered 25/10, 2017 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.