Skip to content

matthewrdev/Xamarin.Forms.GridLocationExtension

Repository files navigation

Xamarin.Forms Grid Markup Extension

🚨Experimental🚨.

Specify grid locations by name for simpler, more maintainable XAML.

Before

<Label Grid.Row="0" />

After

<Label Grid.Row="{local:GridLocation titleRow}"/>

See:

Using the GridLocation and GridSpan markup extensions

The Problem With Grids In Xamarin.Forms

Grids, while powerful, have one fundamental drawback; we place controls using 0-based indices. Consider the following code:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="2"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <Label Grid.Row="0" /> <!-- Title label -->
    <ContentView Grid.Row="1" /> <!-- Content Divider-->
    <StackLayout Grid.Row="2"/> <!-- Content -->
    <ActivityIndicator Grid.RowSpan="3" /> <!-- Loading Indicator that covers all rows -->

</Grid>

Each time we place a control in the grid, we do so using a 0-based location. This has a few problems:

  • Firstly, we can accidentally use an index not declared in the RowDefinitions/ColumnDefinitions of the grid, creating a rendering bug.
  • Secondly, if we add or remove a row/column, we now need to update the RowDefinitions/ColumnDefinitions and all affected indices in the grid.
  • Lastly, it is difficult to tell if we have correctly placed our control in the grid when looking at syntax such as Grid.Row="1". There is a significant amount of thinking required to validate this location.

To solve these issues, this repository introduces two new markup extensions that allow grid locations to be referenced by name:

  • Use the GridLocationExtension to lookup the index of a named row or column in XAML: Grid.Row={local:GridLocation namedRow}.
  • Use the GridSpanExtension to calculate the span between two named rows or columns in XAML: Grid.Row={local:GridSpan From=startRow, To=endRow}.

These extensions require that each row and column definition includes an x:Name attribute to expose it to the extension. For example: <RowDefinition x:Name="contentRow" Height=Auto/>.

Here is an example these extensions applied to the previous code sample:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition x:Name="titleRow"  Height="Auto"/>
        <RowDefinition x:Name="dividerRow" Height="2"/>
        <RowDefinition x:Name="contentRow" Height="*"/>
    </Grid.RowDefinitions>

    <Label Grid.Row="{local:GridLocation titleRow}" /> <!-- Title label -->
    <ContentView Grid.Row="{local:GridLocation dividerRow}" /> <!-- Content Divider-->
    <StackLayout Grid.Row="{local:GridLocation contentRow}"/> <!-- Content -->
    <ActivityIndicator Grid.RowSpan="{local:GridSpan From=titleRow, To=contentRow}" /> <!-- Loading Indicator that covers all rows -->

</Grid>

There are a few benefits here:

  • Each row/column definition is now documented via the x:Name attribute.
  • As rows/columns are now resolved at runtime via the GridLocation extension, we can freely move and delete rows/columns without needing to readjust indices and spans.
  • We can use GridSpan extensions From and To to easily calculate the correct spans through names. This reduces code complexity and makes it very clear what the intended behaviour of a span is.

If you like this work and would like to see it continue, please raise an issue to start a discussion. 😊

Disclaimer

While stable and tested, it is not recommended that you use this code in your production apps for the following reasons:

  • These extensions do not have complete API documentation.
  • The extensions have no error logging to assist you in diagnosing runtime issues.
  • The extensions use reflection to perform the location and span calculations. This may have adverse runtime performance impacts.
  • The extensions execute a Xamarin.Forms internal API with the use of reflection. Unless Xamarin.Forms exposes the relevant APIs, this methodology may break at any point in the future.

About

Specify grid locations by name for simpler, more maintainable XAML.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages