Skip to content

Fix GridSplitter runaway resize when a neighboring star definition is clamped - #11900

Open
akon47 wants to merge 1 commit into
dotnet:mainfrom
akon47:fix-gridsplitter-runaway-resize
Open

Fix GridSplitter runaway resize when a neighboring star definition is clamped#11900
akon47 wants to merge 1 commit into
dotnet:mainfrom
akon47:fix-gridsplitter-runaway-resize

Conversation

@akon47

@akon47 akon47 commented Sep 5, 2026

Copy link
Copy Markdown

Fixes #1687

Description

When a GridSplitter resizes only one of the two definitions (SplitBehavior.Resize1/Resize2, i.e. at least one of them is not star-sized), GetDeltaConstraints limits the drag delta only by the Min/Max constraints of the definition being resized — the constraints of the neighboring star definition are ignored.

The neighboring star definition is the one that absorbs the space freed up or consumed by the resized definition. Once it gets clamped by one of its own constraints (MinWidth/MaxWidth/MinHeight/MaxHeight, including the implicit minimum of 0), the layout can no longer honor the lengths the splitter sets and the splitter stops moving on screen. Thumb reports drag changes relative to its own position, so from that point the reported DragDelta change keeps accumulating while the mouse moves, and MoveSplitter re-applies the ever-growing accumulated change to the current actual lengths on every mouse move. The result is that the resized definition changes much faster than the mouse (10px mouse steps produce 20/30/40/…px changes), or keeps growing forever past the Grid/window bounds (#1687).

This change constrains the delta by the neighboring star definition's Min/Max as well, matching what SplitBehavior.Split already does for both definitions. The extra clamp is only applied when the neighboring definition is star-sized — the only configurations in which the layout redistributes the space and the bug can occur. All other configurations keep the existing behavior (verified below).

Minimal repro (columns)

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" MaxWidth="520" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="300" />
    </Grid.ColumnDefinitions>
    <Border Grid.Column="0" Background="LightBlue" />
    <GridSplitter Grid.Column="1" Width="6" HorizontalAlignment="Center" VerticalAlignment="Stretch" />
    <Border Grid.Column="2" Background="LightSalmon" />
</Grid>

Drag the splitter to the right. As soon as the first column reaches its MaxWidth, the right column starts shrinking much faster than the mouse. The rows variant from #1687 (star row above a fixed-height row, splitter bottom-aligned in the star row, drag up past the window top) is the same bug with the implicit minimum of 0.

Before / after

Top: current behavior, bottom: with this change. The red line is the (emulated) mouse position; every step moves the mouse by exactly 10px.

[ *(MaxWidth=520) | splitter | 300px ], dragging right — the right column collapses at an accelerating rate while the mouse moves at constant speed:

MaxWidth columns before/after

[ *(MinWidth=400) | splitter | 300px ], dragging left — the right column grows at an accelerating rate and overflows the grid:

MinWidth columns before/after

Rows layout from #1687, dragging up past the top — the bottom row grows forever (reaches 832px in a 150px-tall grid); with the fix it stops once the star row is fully collapsed:

Issue 1687 rows before/after

Excerpt of the per-step drag log (current behavior, MaxWidth scenario; full logs in the assets link below):

step  4: mouse= +40  change=   10.0  col0= 519.0  col2= 260.0  col2 step-delta=  -10.0
step  5: mouse= +50  change=   10.0  col0= 520.0  col2= 250.0  col2 step-delta=  -10.0   <- col0 reaches MaxWidth
step  6: mouse= +60  change=   19.0  col0= 520.0  col2= 231.0  col2 step-delta=  -19.0   <- faster than the mouse
step  7: mouse= +70  change=   29.0  col0= 520.0  col2= 202.0  col2 step-delta=  -29.0
step  8: mouse= +80  change=   39.0  col0= 520.0  col2= 163.0  col2 step-delta=  -39.0
step  9: mouse= +90  change=   49.0  col0= 520.0  col2= 114.0  col2 step-delta=  -49.0
step 10: mouse=+100  change=   59.0  col0= 520.0  col2=  55.0  col2 step-delta=  -59.0

Customer Impact

Dragging a GridSplitter whose neighboring star column/row has a Min/Max constraint (or can collapse to 0) resizes the layout much faster than the mouse, and can push content far outside the Grid/window with no way to control it precisely. The rows variant is reported in #1687.

Regression

No. The behavior is long-standing (reported against .NET Core 3.0 in #1687 and present in .NET Framework as well).

Testing

  • Manual dragging plus emulated drags (the emulation feeds DragDelta events computed exactly the way Thumb reports them — change relative to the thumb) across the scenarios above, for both columns and rows.
  • A per-step regression matrix comparing the current and fixed behavior over representative configurations. Configurations not affected by the bug are pixel-for-pixel identical across every drag step:
Configuration (10px mouse steps) Result with this change
star(MaxWidth) | pixel, drag right fixed — was resizing up to 54px per step
star(MinWidth) | pixel, drag left fixed — was resizing up to 106px per step
pixel | star, drag right fixed — splitter no longer pushes past the grid edge (was 800px in a 760px grid)
pixel | star, drag left identical (0.00px difference on every step)
star | star, drag right identical (Split path is untouched)
pixel | pixel, drag right identical (neighbor is not star; clamp not applied)
pixel | auto, drag right identical (neighbor is not star; clamp not applied)
star(MaxWidth) | star, drag right identical (Split path is untouched)

The repro/verification app, recordings, and full drag logs are available here: https://github.com/akon47/wpf/tree/gridsplitter-runaway-repro-assets

Risk

Low. The delta is additionally clamped only when SplitBehavior is Resize1/Resize2 and the neighboring definition is star-sized — exactly the configurations that are broken today. Resize2 implies definition1 is star-sized (see SetupDefinitionsToResize), so no condition is needed there. Keyboard resizing and ShowsPreview go through the same constraint helper, so they get the same consistent clamping (the preview now stops where the actual resize would stop). The regression matrix above verifies non-affected configurations are unchanged.

Microsoft Reviewers: Open in CodeFlow

@akon47
akon47 requested a review from a team September 5, 2026 04:12
@akon47
akon47 requested a review from a team as a code owner September 5, 2026 04:12
@dotnet-policy-service dotnet-policy-service Bot added PR metadata: Label to tag PRs, to facilitate with triage Community Contribution A label for all community Contributions labels Sep 5, 2026
@miloush

miloush commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community Contribution A label for all community Contributions PR metadata: Label to tag PRs, to facilitate with triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GridSplitter does not work correctly, expands child outside window

2 participants