2
votes

I've been struggling with this issue for too long. Something that is supposed to be very simple.. I'm making a Tic Tac Toe app in Xamarin.Forms. The board itself contains 3x3 buttons, each button is placed in its own Grid cell. I have 3 cells horizontally and 3 cells vertically.

The grid with these cells are placed in a Grid row from another Grid. The horizontal part of the board automatically expands, so that it takes up the entire width of the screen, as you can see from the picture. But I want the height of the buttons/grid rows to be the exact same, so that they create a perfect square. I can do that by hardcoding a value when creating the button using HeightRequest = someValue, but I want to bind it to the width of the button/gridColumn, so that it looks good no matter what device you are running on.

enter image description here

In this example I hardcoded the height of the Grid row. As you can see, it looks fine on the iPhone, but terrible on the iPad due to the larger display and higher amount of pixels. If only I could make the height the same as the width it would be perfect.

The grid is made in XAML:

 <Grid x:Name="rootGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="70" />
            <RowDefinition Height="60" />
            <RowDefinition Height="400"/>
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Label Text="Tic Tac Toe" HorizontalOptions="Center" VerticalOptions="StartAndExpand" Padding="0,50,0,0" FontFamily="Arial" FontAttributes="Bold" FontSize="Large" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="0"/>
        <Grid Grid.Row="1" Margin="0,20,0,0" >
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label HorizontalTextAlignment="Center" Text="X: 0 points" Grid.Column="0" TextColor="#FFFFFF" IsVisible="True"></Label>
            <Label HorizontalTextAlignment="Center" Text="O: 0 points" Grid.Column="1" TextColor="#FFFFFF" IsVisible="True"></Label>
        </Grid>

        <Grid Grid.Row="2" x:Name="theBoard">
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name="col1" Width="*" />
                <ColumnDefinition x:Name="col2" Width="*" />
                <ColumnDefinition x:Name="col3" Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <!--<RowDefinition BindingContext="{x:Reference Name=col1}" Height="{Binding Path=Width}"/>-->
            </Grid.RowDefinitions>
        </Grid>
    </Grid>

And the buttons are added like this:

        private void CreateFields()
    {


        // Iterate through the rows
        for (int i = 0; i < fields.GetLength(1); i++)
        {

            // Iterate through the columns
            for (int j = 0; j < fields.GetLength(0); j++)
            {
                Button field = new Button()
                {
                    BackgroundColor = Color.WhiteSmoke,
        
                };

                // Adds the new button to the two dimensional array
                fields[i, j] = field;

                theBoard.Children.Add(field, j, i);
                


            }

        }

        //Console.WriteLine("The current width of the button is: " + fields[0,0].Width);
        //Console.WriteLine("The current height of the button is: " + fields[0,0].Height);
        //Console.WriteLine("The current width of the column is: " + col1.Width);
       // Console.WriteLine("The current height of the column is: " + row1.Height);


    }

I really hope someone is able to help me. Its gotta be some simple detail I forgot. In WPF I was used to have something called "ActualWidth" but this doesn't seem to be the case here.

2
I guess you could use XF CollectionView wouldn't it make everything easier!FreakyAli
Did you try RelativLayout. It suits perfectly for your need. I have added an answer for this check it out.Nikhileshwar
Also if you want i can give you a solution using Grid Layout if you want to which would fit based on the Area!FreakyAli

2 Answers

2
votes

I have updated your XAML, please see the changes below with the comments. This should make your board grid always square.

    <Grid x:Name="rootGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="70" />
        <RowDefinition Height="60" />
        <!--set this row definition height to Auto -->
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Label Text="Tic Tac Toe" HorizontalOptions="Center" VerticalOptions="StartAndExpand" Padding="0,50,0,0" FontFamily="Arial" FontAttributes="Bold" FontSize="Large" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="0"/>
    <Grid Grid.Row="1" Margin="0,20,0,0" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Label HorizontalTextAlignment="Center" Text="X: 0 points" Grid.Column="0" TextColor="#FFFFFF" IsVisible="True"></Label>
        <Label HorizontalTextAlignment="Center" Text="O: 0 points" Grid.Column="1" TextColor="#FFFFFF" IsVisible="True"></Label>
    </Grid>

    <!--Bind the HeightRequest of the board grid to its own width-->
    <Grid Grid.Row="2" x:Name="theBoard" HeightRequest="{Binding Source={x:Reference theBoard}, Path=Width}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition x:Name="col1" Width="*" />
            <ColumnDefinition x:Name="col2" Width="*" />
            <ColumnDefinition x:Name="col3" Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    </Grid>
</Grid>
1
votes

RelativeLayouts are good for simple responsive layout like yours. Relate all the Constrains to the Width of the parent, since you want it to fill the Width of the Page. Since entire column is divided into 3 (3 columns), 1/3 = 0.33333333 is your Factor for the ConstrainExpression.

<Grid x:Name="rootGrid">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="70" />
        <RowDefinition Height="60" />
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Label Text="Tic Tac Toe" HorizontalOptions="Center" VerticalOptions="StartAndExpand" FontFamily="Arial" FontAttributes="Bold" FontSize="Large" TextColor="#FFFFFF" Grid.Row="0" Grid.Column="0"/>
    <Grid Grid.Row="1" Margin="0,20,0,0" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Label HorizontalTextAlignment="Center" Text="X: 0 points" Grid.Column="0" TextColor="#FFFFFF" IsVisible="True"></Label>
        <Label HorizontalTextAlignment="Center" Text="O: 0 points" Grid.Column="1" TextColor="#FFFFFF" IsVisible="True"></Label>
    </Grid>

    <RelativeLayout
        Grid.Row="2"
        x:Name="theBoard">
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.666666}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.666666}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.666666}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.666666}"/>
        <local:TicTacView
            RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.333333}"
            RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.666666}"
            RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.666666}"/>
    </RelativeLayout>
</Grid>

TicTacToe in relative layout

Hope it helps! As far as Landscape is concerned (Are you supporting Landscape?), Design has to be rethought, as expanding the TicTac View to the Width will not provide good UI.