Controls

GridSplitter

GridSplitter is the grid-resize handle for adjusting adjacent row or column definitions inside a Grid. It resolves a resize direction, selects a pair of neighboring definitions based on placement and alignment, and applies bounded pixel-size deltas while preserving each definition's minimum and maximum constraints.

Quick start

Use GridSplitter when a view should let the user rebalance space between adjacent regions in a Grid. Place it in the grid between the definitions you want to resize, then set either an explicit direction or let the control infer one from its size and alignment.

Vertical splitter between two columns

<Grid Width="480" Height="260">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="180" MinWidth="120" />
    <ColumnDefinition Width="6" />
    <ColumnDefinition Width="*" MinWidth="160" />
  </Grid.ColumnDefinitions>

  <Border Grid.Column="0" Background="#152230" />

  <GridSplitter Grid.Column="1"
                Width="6"
                HorizontalAlignment="Center"
                ResizeDirection="Columns"
                Background="#44576A"
                BorderBrush="#6D8298" />

  <Border Grid.Column="2" Background="#101820" />
</Grid>

Horizontal splitter between two rows

<Grid Width="420" Height="320">
  <Grid.RowDefinitions>
    <RowDefinition Height="180" MinHeight="100" />
    <RowDefinition Height="6" />
    <RowDefinition Height="*" MinHeight="120" />
  </Grid.RowDefinitions>

  <Border Grid.Row="0" Background="#152230" />

  <GridSplitter Grid.Row="1"
                Height="6"
                VerticalAlignment="Center"
                ResizeDirection="Rows" />

  <Border Grid.Row="2" Background="#0F151C" />
</Grid>

Placement rule: put the splitter in the row or column that visually separates the regions you want to resize. The control chooses neighboring definitions relative to its own Grid.Row or Grid.Column position.

How resize targeting works

GridSplitter only operates when its visual parent is a Grid. Once a resize direction is resolved, it chooses two neighboring definitions and treats them as the resizable pair.

Column mode

In Columns mode the control resizes adjacent ColumnDefinition entries, using the splitter's current Grid.Column value as the placement anchor.

Row mode

In Rows mode it resizes adjacent RowDefinition entries, using the splitter's current Grid.Row value as the placement anchor.

  • If the parent is not a Grid, no resize target can be resolved.
  • If the target dimension has fewer than two definitions, resizing cannot start.
  • The control clamps its anchor index into the valid row or column range before resolving neighbors.

ResizeBehavior resolution

BasedOnAlignment

Columns:
Left   -> PreviousAndCurrent
Right  -> CurrentAndNext
Other  -> PreviousAndNext

Rows:
Top    -> PreviousAndCurrent
Bottom -> CurrentAndNext
Other  -> PreviousAndNext

That means centered splitters usually resize the definitions on both sides, while edge-aligned splitters resize the current definition against either the previous or next one.

Direction resolution

ResizeDirection defaults to Auto. In that mode, the control infers whether it should operate on rows or columns from its size and alignment.

Explicit direction

Set ResizeDirection="Columns" or ResizeDirection="Rows" when the layout intent is clear and you do not want inference.

Auto direction

When Auto is active, the control compares width and height. Narrow vertical shapes resolve to columns; flatter horizontal shapes resolve to rows.

  • If both width and height are unresolved or non-positive, HorizontalAlignment="Stretch" resolves to column mode and all other cases resolve to row mode.
  • If both dimensions are available, width <= height resolves to columns and width > height resolves to rows.
  • The minimum desired size follows the resolved direction: at least 5x20 for columns, or 20x5 for rows.

Auto-resolved narrow splitter

<GridSplitter Grid.Column="1"
              Width="5"
              HorizontalAlignment="Stretch"
              Background="#58697A" />

Resize math and constraint handling

Once the target pair is chosen, the splitter measures the starting size of both definitions, applies a delta, and writes back new pixel lengths. The delta is bounded by the two definitions' minimum sizes, then the resulting values are clamped again against each definition's minimum and maximum range.

Source sizes

The control uses ActualWidth or ActualHeight when available. If the definition is pixel-sized and not yet measured, it falls back to the declared pixel value.

Write-back sizes

After applying the bounded delta, the control writes pixel GridLength values back to the affected definitions, preserving the combined size as closely as possible.

  • MinWidth and MaxWidth constrain column resizing.
  • MinHeight and MaxHeight constrain row resizing.
  • If a non-pixel definition has not produced an actual size yet, the fallback starting size is effectively 0.
  • Resizing converts the affected pair to pixel sizes for the new layout state.

Typical before and after model

Before drag:
Column A = 180 px
Column B = 300 px

Delta = +24 px

After drag:
Column A = 204 px
Column B = 276 px

Appearance and visual states

GridSplitter is a simple rendered control with no required template parts. It draws a filled rectangle and a one-pixel border using its current visual state.

Normal state

The control fills itself using Background and outlines itself with BorderBrush.

Hover and drag state

When IsMouseOver is true the fill shifts to a darker hover color. When IsDragging is true the fill switches to a stronger blue drag highlight.

  • Background defaults to a mid-gray Color.
  • BorderBrush defaults to a lighter gray Color.
  • There is no dedicated App.xml style for GridSplitter in the current theme; if you want branded visuals, style it directly.
  • The hover and drag fills are internal colors rather than theme-bound brush resources.

Simple styled splitter

<GridSplitter Grid.Column="1"
              Width="6"
              ResizeDirection="Columns"
              Background="#44576A"
              BorderBrush="#7A8EA4" />

Interaction surface

The public API exposes DragIncrement, KeyboardIncrement, and IsDragging, which describe how resize interactions should be quantized and surfaced visually. The control implementation also contains the internal logic for beginning a drag, applying a snapped delta, and ending the drag once a target pair has been resolved.

DragIncrement

Positive step value used to snap resize deltas to a predictable drag cadence. Values less than or equal to zero are coerced to 1.

KeyboardIncrement

Positive step value intended for keyboard-driven resize adjustments. Values less than or equal to zero are coerced to 1.

  • IsDragging is read-only from outside the control and exists primarily as a visual-state surface.
  • The control file contains the resize-targeting and delta-application logic; it does not define a template contract.
  • When documenting user input, treat these properties as part of the control's interaction model, not as a separate layout concern.

Quantized drag settings

<GridSplitter Grid.Column="1"
              Width="8"
              ResizeDirection="Columns"
              DragIncrement="8"
              KeyboardIncrement="16" />

Property reference

Resize model

ResizeDirection: Auto, Columns, or Rows.

ResizeBehavior: BasedOnAlignment, CurrentAndNext, PreviousAndCurrent, or PreviousAndNext.

DragIncrement: positive step for snapped drag movement.

KeyboardIncrement: positive step for keyboard-driven movement.

Visual state

Background: base fill color in the normal state.

BorderBrush: border stroke color.

IsMouseOver: hover-state flag.

IsDragging: drag-state flag.

Inherited layout inputs

Width, Height, HorizontalAlignment, and VerticalAlignment all influence the resolved resize direction and neighbor-selection behavior.

Grid attachment

Grid.Row and Grid.Column determine the placement anchor from which the target definitions are resolved.

Patterns and examples

Centered splitter that resizes both neighbors

<GridSplitter Grid.Column="1"
              Width="6"
              HorizontalAlignment="Center"
              ResizeBehavior="BasedOnAlignment"
              ResizeDirection="Columns" />

Current row against next row

<GridSplitter Grid.Row="1"
              Height="6"
              VerticalAlignment="Bottom"
              ResizeBehavior="CurrentAndNext"
              ResizeDirection="Rows" />

Explicit previous-and-current column pair

<GridSplitter Grid.Column="2"
              Width="8"
              HorizontalAlignment="Left"
              ResizeBehavior="PreviousAndCurrent"
              ResizeDirection="Columns" />

Notes and pitfalls

Grid-only control. GridSplitter must be parented by a Grid. Without a grid parent, the control has nothing to resize.

Placement matters more than visuals. The row or column where you place the splitter determines which definitions can be resized. A visually centered handle in the wrong slot will target the wrong pair or no pair at all.

Resizing writes pixel lengths. The control preserves total size but commits the affected pair back as pixel GridLength values, which is important if the original grid used star sizing.

No dedicated default theme style. The current theme does not define a specific GridSplitter style in App.xml, so appearance relies on the control defaults unless you style it yourself.