WPF Standard Control Demo App › Grid

Grid Layout

Grid arranges its children in rows and columns. Each row and column is sized by a fixed value, by Auto (the size of its content), or by a star (*) share of the space that remains.

How the three sizing modes share the width

A Grid defines its rows with RowDefinition and its columns with ColumnDefinition, and each child picks a cell with the attached properties Grid.Row and Grid.Column (zero-based). The three sizing modes work together: in a Grid 440 wide with the columns Auto | 80 | 1* | 2* and 60-wide content in each column, the columns measured 60 / 80 / 100 / 200. The star columns split the remaining 300 in a 1:2 ratio. Widening the same Grid to 740 changed only the star columns, to 200 / 400.

MinWidth and MaxWidth limit Auto columns as well as fixed ones: MaxWidth="30" on an Auto column with 60-wide content kept the column at 30. When the limits conflict, MinWidth wins. The demo app starts with Width="25" MinWidth="30" MaxWidth="100", and the column is 30 wide; MinWidth="100" MaxWidth="50" gave 100. Rows follow the same rules. With the demo app’s starting values (Height="20" MinHeight="10" MaxHeight="100") the row is 20 high, Height="5" MinHeight="10" gave 10, and an Auto row with MaxHeight="15" and 40-high content was 15 high.

Mixing Auto and star definitions can make Grid measure a child twice. In a 2 × 2 Grid whose columns are Auto, * and rows are *, Auto, the child in the Auto-column/star-row cell was measured twice on the first layout; with only fixed, only Auto, or only star definitions, every child was measured once. This matters for children whose measurement is expensive.

Where a child ends up

The default of Grid.Row and Grid.Column is 0, so children that omit them all land in the first cell and overlap. Out-of-range values do not raise an error:

Only invalid numbers are rejected: a negative index or a span of 0 throws ArgumentException. Everything else is clamped silently. Grid.Column is a number, not a reference to a ColumnDefinition, so after a definition is removed a child keeps its number: it then sits in whichever column now has that number, or, if the number is past the new last column, in the last column without any warning (as the clamping above shows). Check the children after removing a definition; the removal itself was not measured.

case measured
default of Grid.Row / Grid.Column (metadata) 0 / 0
2x2, two children without Grid.Row/Column x=0 y=0 w=150 h=100 | x=0 y=0 w=150 h=100
2x2, Grid.Column=2 (out of range) x=150 y=0 w=150 h=100
2x2, Grid.Column=5, Grid.Row=5 x=150 y=100 w=150 h=100
3x2, Grid.ColumnSpan=5 x=0 y=0 w=300 h=100
3x2, Grid.Column=1, ColumnSpan=5 x=100 y=0 w=200 h=100
3x2, Grid.RowSpan=5 x=0 y=0 w=100 h=200
Grid.SetColumn(child, -1) ArgumentException
Grid.SetColumnSpan(child, 0) ArgumentException
no definitions, child A + child B (Column=1, 50x30) x=0 y=0 w=300 h=200 | x=125 y=85 w=50 h=30

Where children end up in a 300 × 200 Grid (x, y, width, height). Measured on .NET 10 / Windows 11.

When star columns keep their ratio

Star columns keep their ratio only when the Grid has a finite width to divide. They do not collapse to zero inside a StackPanel or ScrollViewer, but they can lose the ratio:

Rows behave the same way vertically. Putting Auto rows inside a ScrollViewer is not a problem: three 100-high Auto rows in a 120-high ScrollViewer made the Grid 300 high, and the vertical scroll bar appeared.

container sizes scroll bar
StackPanel Horizontal, width 400 columns 60 / 30 -
StackPanel Vertical, width 400 columns 133.33 / 266.67 -
ScrollViewer 400 wide, horizontal Disabled columns 133.33 / 266.67 horizontal Collapsed
ScrollViewer 400 wide, horizontal Auto columns 133.33 / 266.67 horizontal Collapsed
ScrollViewer 60 wide, horizontal Disabled columns 20 / 40 horizontal Collapsed
ScrollViewer 60 wide, horizontal Auto columns 60 / 30 horizontal Visible
ScrollViewer 300 high; rows 1* | 1* (contents 20) rows 150 / 150, scrollable 0 -
ScrollViewer 120 high, vertical Auto; rows Auto x3 (100 each) Grid 300, scrollable 180 vertical Visible

Star columns and rows by container. None of them collapse to zero. Measured on .NET 10 / Windows 11.

Aligning columns across separate Grids

SharedSizeGroup keeps columns (or rows) that share a group name at the same width across separate Grids. This is how form rows built as individual Grids, or items of an ItemsControl that each have their own Grid, keep their label columns aligned. Set Grid.IsSharedSizeScope="True" on a common ancestor. With 50-wide and 120-wide content, the two Auto columns measured 50 / 120 without the scope and 120 / 120 with it.

A star column in a shared group behaves like Auto: it also measured 120 / 120 instead of filling the remaining space. In the demo app, the check box switches the scope on and off, and the text boxes change the content of each Grid.

Overlap, ZIndex, and the empty area

Several children can share one cell; they overlap. Without Panel.ZIndex, the child added later is in front; with ZIndex="1", the earlier child came to the front. ZIndex only compares children of the same panel: an element with ZIndex="100" inside a nested Grid stayed behind a later sibling of that nested Grid, and came to the front only after the nested Grid itself was given a higher ZIndex. This is the mechanism behind an overlay such as a loading indicator placed in the same cell as the content.

The Background decides whether the empty parts of the Grid receive the mouse. With the default null, a hit test on an empty area of the Grid went through to the element behind it. With Transparent, the same hit test found the Grid. Set Transparent when the whole area should react to clicks or MouseEnter without painting a color.

Grid lines and nesting

ShowGridLines draws lines along the row and column boundaries, which helps you check how cells are sized. The lines are drawn by an extra internal visual (GridLinesRenderer), and ShowGridLines is the only public property of Grid that concerns them. Their color, thickness, and style cannot be set, so use Border elements for lines that are part of the design.

Nesting Grids costs little by itself. For 500 TextBlocks, putting each row in its own nested Grid kept the first layout within about 10% of a flat Grid over repeated runs. Wrapping every TextBlock in three extra Grids, 1,500 in all, cost roughly 10–25% more. Auto columns instead of * made no measurable difference. Choose the structure that is easiest to read.

Trying it in the demo app

The Grid page of the demo app, with the control list on the left and the first section, Column / Row

The Grid page of the demo app has a section for each property above, with input controls that change the value while the result is displayed, and a “Show Code” link that shows the XAML of that section. The following XAML is the ColumnSpan / RowSpan section (GridUsageControl.xaml), with the styles and the surrounding GroupBoxes left out and the namespace declarations added. The combo boxes are bound to the spans of the first and second text blocks:

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ComboBox x:Name="ColumnSpanComboBox" SelectedIndex="0" SelectedValuePath="Content">
    <ComboBoxItem Content="1" />
    <ComboBoxItem Content="2" />
  </ComboBox>
  <ComboBox x:Name="RowSpanComboBox" SelectedIndex="0" SelectedValuePath="Content">
    <ComboBoxItem Content="1" />
    <ComboBoxItem Content="2" />
  </ComboBox>

  <Grid x:Name="ColumnSpanRowSpanResultGrid">
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0"
               Grid.ColumnSpan="{Binding SelectedValue, ElementName=ColumnSpanComboBox}"
               Background="CadetBlue" Text="1x1,ColumnSpanSetting" />
    <TextBlock Grid.Row="0" Grid.Column="2"
               Grid.RowSpan="{Binding SelectedValue, ElementName=RowSpanComboBox}"
               Background="SteelBlue" Text="1x3,RowSpanSetting" />
    <TextBlock Grid.Row="1" Grid.Column="0" Background="CornflowerBlue" Text="2x1" />
    <TextBlock Grid.Row="1" Grid.Column="1" Background="LightBlue" Text="2x2" />
  </Grid>
</StackPanel>

Source code and how it was measured

Every behavior on this page was measured by running it on .NET 10 / Windows 11, using GridDemoScene in this site’s screenshot tool.

View Grid source code on GitHub →