3
votes

I am new to Xamarin.Forms. Now I need to design a grid in my page and the grid is 3X2 (2 rows, 3 columns). But I need all the cells in this grid square-shaped and the width of this grid matches its parent view.

In another word, suppose the width of the screen(or the parent view of this grid) is 3, so the width of each column is 1. How can I force the Height of each row to be exactly 1 (so that each cell of the grid the height equals the width)?

Here is my design but not work:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="*"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="*"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>
</Grid>

Is there a pure XAML solution to this issue?

Thank you for any help.

4

4 Answers

3
votes

Star sizing adjust itself with available space by orientation, so you need to adjust by hand via overriding OnSizeAllocated method(be careful nested method calling when resizing child).

Xaml:

<Grid x:Name="mainGrid">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="*"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="*"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>
</Grid>

Xaml.cs

protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height);

    if (3 * height > 2 * height)
        mainGrid.HeightRequest = 2.0 / 3 * width;
    else
        mainGrid.WidthRequest = 3.0 / 2 * height;
}
1
votes
protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height);

    double colsCount = MainGrid.ColumnDefinitions.Count;
    double rowsCount = MainGrid.RowDefinitions.Count;
    if (colsCount > 0 & rowsCount > 0)
    {
        MainGrid.HeightRequest = width / colsCount * rowsCount;
    }
}
1
votes
    // Calculate the height of a Grid containing square cells
    protected override void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);

        if (_grid.ColumnDefinitions.Count == 0 || _grid.RowDefinitions.Count == 0)
            return;

        var totalColSpacing = (_grid.ColumnDefinitions.Count - 1) * _grid.ColumnSpacing;
        var totalRowSpacing = (_grid.RowDefinitions.Count - 1) * _grid.RowSpacing;

        var cellWidth = (width - totalColSpacing) / _grid.ColumnDefinitions.Count;

        _grid.HeightRequest = (cellWidth * _grid.RowDefinitions.Count) + totalRowSpacing;
    }
0
votes

I've got a much easier way to do this. The trick is to name the first cell, and retrieve its width.

Layout definition first:

<Grid x:Name="mainGrid">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="*"/>
    <ColumnDefinition Width="*"/>
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
    <RowDefinition Height="*"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>

  <StackLayout Grid.Row="0" Grid.Column="0" x:Name="firstCell" />

  <!-- Further declaration of elements inside the grid... -->
</Grid>

Then: (accounting for rowspacing, plus deducting 1 x [RowSpacing] because cells in the last row don't have rowspacing below themselves.)

protected override void OnSizeAllocated(double width, double height)
{
    base.OnSizeAllocated(width, height);

    int colCount = mainGrid.ColumnDefinitions.Count;
    int rowCount = mainGrid.RowDefinitions.Count;

    if (colCount > 0 && rowCount > 0)
    {
        mainGrid.HeightRequest = (firstCell.Width + mainGrid.RowSpacing) * rowCount - mainGrid.RowSpacing;
    }
}