-
Notifications
You must be signed in to change notification settings - Fork 8
Grids
Grids
are a layout container that arrange their children according to the row+column (cell) they're placed in. You must configure at least one RowDefinition
and at least one ColumnDefinition
. These definitions define sizing constraints that are used to arrange the cells' contents. Then you must specify the Row
and Column
zero-based index that each child resides in.
Grids
use a list of RowDefinitions
and ColumnDefinitions
to arrange its layout.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Length="45px" />
<RowDefinition Length="75px" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Length="100px" />
<ColumnDefinition Length="80px" />
</Grid.ColumnDefinitions>
<Border Row="0" Column="0" Background="Red" Content="Content of Cell=(0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of Cell=(0,1)" />
<Border Row="1" Column="0" Background="Green" Content="Content of Cell=(1,0)" />
<Border Row="1" Column="1" Background="MediumPurple" Content="Content of Cell=(1,1)" />
</Grid>
</Window>
Each RowDefinition
or ColumnDefinition
must have a Length
value. Lengths
can be defined using one of these 3 formats:
Length Type | Examples | Description |
---|---|---|
Auto |
Length="Auto" |
Auto-sized definitions will compute the minimally-required size needed to display all the contents within that row or column. For a RowDefinition , the Grid measures all children within that row, and sets the Row's Height to the maximum Height of those children. For a ColumnDefinition , the Grid measures all children within that column, and sets the Column's Width to the maximum Width of those children. |
Pixel |
Length="100px" Length="45"
|
Pixel-sized definitions are explicitly sized by a given number of pixels. Pixels must be a positive integer value, optionally suffixed with px
|
Weighted |
Length="*" Length="0.4*"
|
Weighted-sized definitions consume a percentage of the grid's available space. If you have multiple weighted definitions, they will each receive a proportional percentage of the remaining space. Example: Row1's Length is 0.8* , Row2's Length is 1.3* . Row1 will receive 0.8/(0.8+1.3)=38.1% of the space, Row2 will receive 1.3/(0.8+1.3)=61.9%Weighted definitions are allocated space after Auto and Pixel sized definitions. |
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="White" Height="300" Width="450">
<Grid.RowDefinitions>
<RowDefinition Length="Auto" />
<RowDefinition Length="100px" />
<RowDefinition Length="0.65*" />
<RowDefinition Length="0.35*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Length="Auto" />
<ColumnDefinition Length="80" />
<ColumnDefinition Length="*" />
</Grid.ColumnDefinitions>
<Border Row="0" Column="0" Background="Red" Content="(0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="(0,1)" />
<Border Row="0" Column="2" Background="Green" Content="(0,2)" />
<Border Row="1" Column="0" Background="MediumPurple" Content="(1,0)" />
<Border Row="1" Column="1" Background="Purple" Content="(1,1)" />
<Border Row="1" Column="2" Background="LightGray" Content="(1,2)" />
<Border Row="2" Column="0" Background="Magenta" Content="(2,0)" />
<Border Row="2" Column="1" Background="Cyan" Content="(2,1)" />
<Border Row="2" Column="2" Background="Navy" Content="(2,2)" />
<Border Row="3" Column="0" Background="Gold" Content="(3,0)" />
<Border Row="3" Column="1" Background="Crimson" Content="(3,1)" />
<Border Row="3" Column="2" Background="Coral" Content="(3,2)" />
</Grid>
</Window>
You may specify minimum/maximum constraints on each RowDefinition
or ColumnDefinition
.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="White" Height="300" Width="200">
<Grid.RowDefinitions>
<RowDefinition Length="*" MaxHeight="70" />
<RowDefinition Length="*" />
<RowDefinition Length="*" MinHeight="190" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Length="*" />
</Grid.ColumnDefinitions>
<Border Row="0" Column="0" Background="Red" Content="(0,0)" />
<Border Row="1" Column="0" Background="Orange" Content="(0,1)" />
<Border Row="2" Column="0" Background="Magenta" Content="(0,2)" />
</Grid>
</Window>
The Grid
is explicitly set to Height="300"
. It contains 3 RowDefinitions
, each requesting 1/3 of the height. Normally, each of these rows would receive 1/3*300=100px of height. But because there are MinHeight
and MaxHeight
constraints, the calculation changes as follows:
- (There are currently 300px of remaining unallocated Height, and the sum of the weights is currently 3.0)
-
RowDefinitions
with aMaxHeight
are processed first (because if the Row's Height is truncated, it frees up more Height for the next Row to consume)- This row is processed:
<RowDefinition Length="*" MaxHeight="70" />
- It receives
Math.Clamp(1.0 / 3.0 * 300, 0, 70)
= 70px.
- This row is processed:
- (There are now 300-70=230px of remaining unallocated Height, and the sum of the remaining weights is now 2.0)
-
RowDefinitions
with aMinHeight
are processed next (because if the Row's Height is increased to it'sMinHeight
, it consumes more Height than usual, leaving less leftover Height for the nextWeighted
Rows to consume)- This row is processed:
<RowDefinition Length="*" MinHeight="190" />
- It receives
Math.Clamp(1.0 / 2.0 * 230, 190, int.MaxValue)
= 190px.
- This row is processed:
- (There are now 230-190=40px of remaining unallocated Height, and the sum of the remaining weights is now 1.0)
-
RowDefinitions
without aMinHeight
nor aMaxHeight
are processed last- This row is processed:
<RowDefinition Length="*" />
- It receives
Math.Clamp(1.0 / 1.0 * 40, 0, int.MaxValue)
= 40px.
- This row is processed:
For convenience, you can also specify several RowDefinitions
or ColumnDefinitions
using a single string value that is parsed to a list of values, by utilizing the RowLengths
and ColumnLengths
string properties instead of the RowDefinitions
and ColumnDefinitions
List properties.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="White" Height="300" Width="200" RowLengths="*[,70],*,*[190,]" ColumnLengths="*">
<Border Row="0" Column="0" Background="Red" Content="(0,0)" />
<Border Row="1" Column="0" Background="Orange" Content="(0,1)" />
<Border Row="2" Column="0" Background="Magenta" Content="(0,2)" />
</Grid>
</Window>
Values are delimited by a comma ,
, and can optionally contain Minimum/Maximum size constraints inside Brackets [
]
-
RowLengths="*[,70],*,*[190,]"
parses to 3RowDefinitions
:-
*[,70]
-
Length="*"
,MinHeight=null
,MaxHeight="70"
-
-
*
-
Length="*"
,MinHeight=null
,MaxHeight=null
-
-
*[190,]
-
Length="*"
,MinHeight="190",
MaxHeight=null`
-
-
Complex example: ColumnLengths="Auto[20,50],1.2*[,200],16px,60,*,1.5*[80,300]"
Use the RowSpan
or ColumnSpan
properties to allow a child to span multiple cells.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="LightGray" RowLengths="70,60" ColumnLengths="140,140">
<Border Row="0" Column="0" Background="Red" Content="Content of (0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of (0,1)" />
<Border Row="1" Column="0" Background="Magenta" Content="Content of (1,0)" />
<Border Row="1" Column="1" Background="MediumPurple" Content="Content of (1,1)" />
<Border Row="0" Column="0" ColumnSpan="2" Background="LightBlue" Opacity="0.8" Content="Stretches (0,0) to (0,1), centered" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Border Row="1" Column="0" ColumnSpan="2" Background="Green" Opacity="0.8" Content="Stretches (1,0) to (1,1), aligned bottom" VerticalAlignment="Bottom" Margin="0,0,0,5" />
<Border Row="0" Column="0" RowSpan="2" ColumnSpan="2" Background="Blue" Opacity="0.8" Content="Stretches (0,0) to (1,1), centered"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Window>
Warning - If a child spans multiple cells, the Grid
measurement logic still treats it as if it only exists in a single cell. This may cause unexpected results if your RowDefinitions
or ColumnDefinitions
are using Length="Auto"
.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="LightGray" RowLengths="50" ColumnLengths="Auto,Auto">
<Border Row="0" Column="0" Background="Red" Content="Content of (0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of (0,1)" />
<Border Row="0" Column="0" ColumnSpan="2" Background="MediumPurple" Content="Stretches from (0,0) to (0,1)" VerticalAlignment="Bottom" Margin="5" />
</Grid>
</Window>
As a workaround, consider setting GridAffectsMeasure="false"
on the child that spans multiple cells:
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="LightGray" RowLengths="50" ColumnLengths="Auto,Auto">
<Border Row="0" Column="0" Background="Red" Content="Content of (0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of (0,1)" />
<Border Row="0" Column="0" ColumnSpan="2" GridAffectsMeasure="False" Background="MediumPurple" Content="Stretches from (0,0) to (0,1)" VerticalAlignment="Bottom" Margin="5" />
</Grid>
</Window>
You can add multiple children to the same cell. The children are drawn in the order they were added, so children added last appear on top.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="LightGray" Padding="5" RowLengths="Auto" ColumnLengths="Auto">
<Border Row="0" Column="0" Background="Red" Content="First Child of (0,0)" Margin="0,0,12,12" />
<Border Row="0" Column="0" Background="LightBlue * 0.85" Content="Second Child of (0,0)" Margin="12,12,0,0" />
</Grid>
</Window>
Use the RowSpacing
and ColumnSpacing
properties to apply padding between each row or column.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="LightGray" Padding="2" RowLengths="45,75,60" ColumnLengths="100,80" RowSpacing="8" ColumnSpacing="20">
<Border Row="0" Column="0" Background="Red" Content="Content of Cell=(0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of Cell=(0,1)" />
<Border Row="1" Column="0" Background="Green" Content="Content of Cell=(1,0)" />
<Border Row="1" Column="1" Background="MediumPurple" Content="Content of Cell=(1,1)" />
<Border Row="2" Column="0" Background="Magenta" Content="Content of Cell=(2,0)" />
<Border Row="2" Column="1" Background="Coral" Content="Content of Cell=(2,1)" />
</Grid>
</Window>
Use the GridLinesVisibility
, GridLineIntersectionHandling
, GridLineMargin
, HorizontalGridLineBrush
, and VerticalGridLineBrush
properties to manage the Grid's
gridlines.
Type | Property | Possible Values | Description |
---|---|---|---|
GridLinesVisibility |
GridLinesVisibility |
None , InnerHorizontal , TopEdge , BottomEdge , InnerVertical , LeftEdge , RightEdge , All , AllHorizontal , AllVertical
|
Determines which grid lines, if any, will be visible. This is a Flags enum, so you may use bitwise operators to combine them, such as TopEdge | BottomEdge | LeftEdge if you only wanted gridlines on certain locations. |
GridLineIntersection |
GridLineIntersectionHandling |
HorizontalThenVertical , VerticalThenHorizontal
|
Determines the order in which grid lines are drawn, which affects how the intersection points of the gridlines will appear. |
int |
GridLineMargin |
Can be used to reserve a defined amount of empty space around the gridlines. You can also think of this as a margin around each cell in the Grid
|
|
IFillBrush |
HorizontalGridLineBrush |
The brush used to draw horizontal gridlines. | |
IFillBrush |
VerticalGridLineBrush |
The brush used to draw vertical gridlines. |
Example:
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="Cyan" Padding="2" RowLengths="45,75,60" ColumnLengths="100,80" RowSpacing="12" ColumnSpacing="12"
GridLinesVisibility="All" GridLineMargin="3" GridLineIntersectionHandling="HorizontalThenVertical" HorizontalGridLineBrush="Purple" VerticalGridLineBrush="MediumPurple">
<Border Row="0" Column="0" Background="Red" Content="Content of Cell=(0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of Cell=(0,1)" />
<Border Row="1" Column="0" Background="Green" Content="Content of Cell=(1,0)" />
<Border Row="1" Column="1" Background="MediumPurple" Content="Content of Cell=(1,1)" />
<Border Row="2" Column="0" Background="Magenta" Content="Content of Cell=(2,0)" />
<Border Row="2" Column="1" Background="Coral" Content="Content of Cell=(2,1)" />
</Grid>
</Window>
The horizontal gridlines are 4px tall because RowSpacing-GridLineMargin*2=4
.The vertical gridlines are 4px wide because ColumnSpacing-GridLineMargin*2=4
. So the spacing properties define how much empty space is between consecutive rows or columns, and then the GridLineMargin
is reserved on each edge of that space. All leftover space is used to fill in the gridline.
(Note: The GridLineMargin
is not reserved along the outer edges of the Grid
. If you want empty space along the outer edge, set Grid.Padding
to a non-zero value instead.)
Use the SelectionMode
, SelectionBackground
, SelectionOverlay
, and CanDeselectByClickingSelectedCell
properties to manage Grid's
selection capabilities. Use Grid.CurrentSelection.GetCells()
method to get the cells that are currently selected (CurrentSelection
may be null).
Type | Property | Values | Description |
---|---|---|---|
GridSelectionMode |
SelectionMode |
None , Row , Column , Cell
|
Determines what type of selection the user may make by clicking within the Grid . |
IFillBrush |
SelectionBackground |
A brush that is drawn underneath selected cell(s). The content of the cell is drawn after this brush. | |
IFillBrush |
SelectionOverlay |
A brush that is drawn overtop of selected cell(s). The content of the cell is drawn before this brush. This brush should typically have some transparency. | |
bool | CanDeselectByClickingSelectedCell |
If true, user may click on a selected cell to set the Grid's CurrentSelection back to null. |
|
GridSelection |
CurrentSelection |
Retrieves the current selection of the Grid , if any (can be null) |
Example: (SelectionMode="Cell"
)
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Background="Cyan" Padding="2" RowLengths="45,75" ColumnLengths="100,80" RowSpacing="12" ColumnSpacing="12" SelectionMode="Cell" SelectionOverlay="Yellow * 0.5">
<Border Row="0" Column="0" Background="Red" Content="Content of Cell=(0,0)" />
<Border Row="0" Column="1" Background="Orange" Content="Content of Cell=(0,1)" />
<Border Row="1" Column="0" Background="Green" Content="Content of Cell=(1,0)" />
<Border Row="1" Column="1" Background="MediumPurple" Content="Content of Cell=(1,1)" />
</Grid>
</Window>
Try setting SelectionMode
to Row
:
GridSplitters
can be placed within a Grid
, allowing the user to click and drag them to dynamically resize the Grid's
rows or columns.
<Window xmlns="clr-namespace:MGUI.Core.UI.XAML;assembly=MGUI.Core"
Left="200" Top="200" MinHeight="0" SizeToContent="WidthAndHeight" WindowStyle="None">
<Grid Width="500" Height="500" Background="LightGray" RowLengths="200[100,],16,*[100,]" ColumnLengths="*[100,],12,200[100,]">
<Border Row="0" Column="0" Background="Red" Content="Content of Cell=(0,0)" />
<Border Row="2" Column="0" Background="Magenta" Content="Content of Cell=(2,0)" />
<Border Row="0" Column="2" Background="Orange" Content="Content of Cell=(0,2)" />
<Border Row="2" Column="2" Background="MediumPurple" Content="Content of Cell=(2,2)" />
<GridSplitter Row="1" Column="0" ColumnSpan="3" />
<GridSplitter Row="0" Column="1" RowSpan="3" />
</Grid>
</Window>
It's common to have vertical GridSplitters
span multiple rows via RowSpan
, or horizontal GridSplitters
span multiple columns via ColumnSpan
). Also notice that the resizing still respected the MinHeight
and MinWidth
values applied to the rows and columns in the above example.