Simplify Grid definitions, with VerticalGrid and HorizontalGrid
See original GitHub issueDescription
MAUI simplifies stack layouts with VerticalStackLayout
and HorizontalStackLayout
, allowing for more concise, readable XAML.
The proposal here is to do something similar for the other common layout type: Grid
, introducing VerticalGrid
and HorizontalGrid
.
A VerticalGrid
is a list of rows, while a HorizontalGrid
is a list of columns. They have these advantages over classic Grid
:
- Allows grid definitions without a need to keep track of row/column indices - the numbering is implicit. Rows/columns can be most easily added/removed.
- Produces XAML markup that more closely matches the actual grid structure - each row/column is a separate indented group, making the grid easier to read and understand.
Public API Changes
Current Syntax
Here’s a typical grid today:
<Grid ColumnDefinitions="0.5*, 0.5*", RowDefinitions="50, 50">
<Label Text="Column 0, Row 0" />
<Label Grid.Column="1"
Text="Column 1, Row 0" />
<Label Grid.Row="1"
Text="Column 0, Row 1" />
<Label Grid.Column="1" Grid.Row="1"
Text="Column 1, Row 1" />
</Grid>
Proposed Syntax
The same grid as above with the proposed syntax is below:
<VerticalGrid ColumnDefinitions="0.5*, 0.5*">
<Row Height="50">
<Label Text="Column 0, Row 0" />
<Label Text="Column 1, Row 0" />
</Row>
<Row Height="50">
<Label Text="Column 0, Row 1" />
<Label Text="Column 1, Row 1" />
</Row>
</VerticalGrid>
Note that there’s no longer a need to specify Grid.Row / Grid.Column, as the row/column index are computed automatically.
Occasionally, a grid is better conceptualized as a list of columns. In that case, it can be expressed with HorizontalGrid
:
<HorizontalGrid RowDefinitions="50, 50">
<Column Width="0.5*">
<Label Text="Column 0, Row 0" />
<Label Text="Column 0, Row 1" />
</Column>
<Column Width="0.5*">
<Label Text="Column 1, Row 0" />
<Label Text="Column 1, Row 1" />
</Column>
</HorizontalGrid>
Handling Row/Column spans
If a Row element has a child cell that spans adjacent columns, that’s expressed like with a traditional Grid
:
<VerticalGrid ColumnDefinitions="0.5*, 0.5, 0.5*">
<Row Height="50">
<Label VerticalGrid.ColumnSpan="2" Text="Columns 0 and 1, Row 0" />
<Label Text="Column 2, Row 0" />
</Row>
<Row Height="50">
<Label Text="Column 0, Row 1" />
<Label Text="Column 1, Row 1" />
<Label Text="Column 2, Row 1" />
</Row>
</VerticalGrid>
If a Row element has a child cell that spans rows below it, it’s expressed as:
<VerticalGrid ColumnDefinitions="0.5*, 0.5, 0.5*">
<Row Height="50">
<Label VerticalGrid.RowSpan="2" Text="Column 0, Rows 0 and 1" />
<Label Text="Column 1, Row 0" />
<Label Text="Column 2, Row 0" />
</Row>
<Row Height="50">
<SpannedCell/>
<Label Text="Column 1, Row 1" />
<Label Text="Column 2, Row 1" />
</Row>
</VerticalGrid>
Using the SpannedCell
dummy element helps makes the grid structure explicit - a cell would have been here, but instead its contents are provided by a row above. In both cases you look at a single Row and see how the children map to columns.
Similarly, an EmptyCell
can be used for cases a grid cell is intentionally blank, without spanning:
<VerticalGrid ColumnDefinitions="0.5*, 0.5, 0.5*">
<Row Height="50">
<EmptyCell/>
<Label Text="Column 1, Row 0" />
<Label Text="Column 2, Row 0" />
</Row>
<Row Height="50">
<Label Text="Column 0, Row 1" />
<Label Text="Column 1, Row 1" />
<Label Text="Column 2, Row 1" />
</Row>
</VerticalGrid>
The rules above mean that tooling can (ideally) enforce that every row/column has the expected number of children, helping minimize mistakes. Row/column indices aren’t used with VerticalGrid
/HorizontalGrid
.
Prior Art
- The Flutter Table widget is similar to
VerticalGrid
. It contains a list ofTableRow
elements. Flutter doesn’t have an equivalent toHorizontalGrid
. - HTML tables are a list of
tr
(table row) elements. HTML developers should find this new Grid syntax intuitive, as it’s the same model. - Comet has VGrid and HGrid, which is where I got the idea originally. Comet’s syntax is more concise, as it omits the Row and Column elements, making them implicit based on item index. But I prefer the explicit Row/Column elements, especially for XAML, as it makes the grid structure more obvious and editing less error prone.
Intended Use-Case
Just as VerticalStackLayout
/ HorizontalStackLayout
will likely replace most uses of StackLayout
, it’s expected that most uses of Grid
will move to using VerticalGrid
/ HorizontalGrid
instead, given its advantages.
Issue Analytics
- State:
- Created a year ago
- Reactions:1
- Comments:9 (5 by maintainers)
I really like this idea, and the naming. The only bit I’m unsure about is whether it’s a good idea for
HorizontalGrid
andVerticalGrid
to not be capable of everythingGrid
is capable of.Something that springs to mind is usage from C#. I get that its intent is to make life easier in XAML, but some people would use these types from C#. In this scenario, what would be the advantage of the new types (
VerticalGrid
,HorizontalGrid
) over using theAddHorizontal
andAddVertical
methods that were present on a Xamarin.Forms Grid (I don’t think these methods are in .NET MAUI)? These methods added children to a single row or single column Grid. TheGrid
then expanded its columns/rows as these calls are made, as well as automatically assigning Grid.Column/Row on the children.We’ve moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.