Skip to content
Home » Basics of .NET MAUI – Part 5 – Layouts

Basics of .NET MAUI – Part 5 – Layouts

Spread the love

In part 4 of this series we were talking were creating views like buttons, check boxes, sliders, entries and others. You might have noticed that they all were enclosed within a VerticalStackLayout element.

The source code for this article is available on Github.

This is where we left off:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <VerticalStackLayout>
        <Label 
            x:Name="label"
            FontSize="20"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />

        <Editor 
            Placeholder="Type something..."
            AutoSize="TextChanges" />

    </VerticalStackLayout>
</ContentPage>

As you can see, there are two elements in the VerticalStackLayout. But what is the VerticalStackLayout in the first place? Well, it’s one of the layouts that you can use to arrange your views. In this particular case the elements are stacked vertically. Let’s have a closer at this layout and the other layouts that you can use. We’re still going to use the TestPage.xaml and TestPage.xaml.cs files because this is all temporary code that we’re eventually not going to use in the app.

Layouts enable you to arrange the elements on the screen in any imaginable way. This is because they can be nested in any way you like so that you get what you want. But working with layouts requires from you some knowledge regarding child positioning and sizing. By children I mean the elements that are inside the layout, so, for example, in the code above the Label and the Editor are children of the VerticalStackLayout.

So, to begin with, let’s have a look at three closely-related layouts: StackLayout, VerticalStackLayout and HorizontalStackLayout.

StackLayout, VerticalStackLayout and HorizontalStackLayout

The StackLayout is the layout that organizes the children in a one-dimentional stack. You can set its Orientation property to determine whether the elements should be arranged horizontally or vertically. Let’s remove the editor from our VerticalStackLayout, change the layout itself to StackLayout and set the Orientation property to Vertical. Also, let’s add some buttons:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <StackLayout Orientation="Vertical">
        <Button Text="1" />
        <Button Text="2" />
        <Button Text="3" />
        <Button Text="4" />
        <Button Text="5" />
    </StackLayout>
</ContentPage>

Let’s test it on Windows this time:

As you can see, the elements are stacked vertically and stretched horizontally to occupy the whole available width. This is how it works if the sizes of the elements are not set explicitly.

Let’s add some margin around the whole layout and some spacing between its children:

...
    <StackLayout Orientation="Vertical" Margin="40" Spacing="10">
        <Button Text="1" />
        ...

Now it looks better:

And now let’s set Orientation to Horizontal:

...
    <StackLayout Orientation="Horizontal" Margin="40" Spacing="10">
        ...

This is what we get:

This time the buttons occupy the whole height, even if you resize the window (try it out). And now let’s set the children’s sizes to make them smaller:

...
    <StackLayout Orientation="Horizontal" Margin="40" Spacing="10">
        <Button Text="1" FontSize="80" WidthRequest="100" HeightRequest="100"/>
        <Button Text="2" FontSize="80" WidthRequest="100" HeightRequest="100" />
        <Button Text="3" FontSize="80" WidthRequest="100" HeightRequest="100" />
        <Button Text="4" FontSize="80" WidthRequest="100" HeightRequest="100" />
        <Button Text="5" FontSize="80" WidthRequest="100" HeightRequest="100" />
    </StackLayout>
</ContentPage>

I also changed the font size to make the numbers more visible. This code is pretty repetitive and, yes, it could be done in a better way, but don’t worry about it for now. Now the buttons look like this:

The buttons are vertically centered inside their parent element (which is the StackLayout). This is the default behavior, but you can change it by setting the VerticalOptions property to a different value, for example:

...
    <StackLayout 
        Orientation="Horizontal" 
        Margin="40" 
        Spacing="10" 
        VerticalOptions="Start">
        <Button Text="1" FontSize="80" WidthRequest="100" HeightRequest="100"/>
        ...

Now the children are at the top of the layout:

In a similar way you can change their horizontal alignment:

...
    <StackLayout 
        Orientation="Horizontal" 
        Margin="40" 
        Spacing="10" 
        VerticalOptions="Start"
        HorizontalOptions="Center">
        <Button Text="1" FontSize="80" WidthRequest="100" HeightRequest="100"/>
        ...

Now they are horizontally centered:

More performant alternatives to a StackLayout with its Orientation property set are the HorizontalStackLayout and the VerticalStackLayout. You already saw the latter in action. This time let’s replace the StackLayout with a HorizontalStackLayout. We can leave all the properties (except Orientation) untouched:

...
    <HorizontalStackLayout  
        Margin="40" 
        Spacing="10" 
        VerticalOptions="Start"
        HorizontalOptions="Center">
        <Button Text="1" FontSize="80" WidthRequest="100" HeightRequest="100"/>
        ...

You should still see the same output in your app window.

And now let’s have a look at another layout, the Grid.

Grid

While the layouts we’ve covered so far arrange their children in one dimension (vertical or horizontal), the Grid arranges them in two dimensions, in rows and columns. We use the RowDefinitions and ColumnDefinitions properties to define the rows and columns respectively. The rows and columns may have absolute or proportional sizes.

By default a Grid contains one row and one column. Let’s rewrite our XAML code to use such a basic Grid. Besides, we’re going to put a BoxView inside the grid as its only child. A BoxView is a simple view that consists of a rectangle of a specified size and color.

So, here’s the code:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <Grid Margin="40">
        <BoxView HeightRequest="100" WidthRequest="100" Color="LightSeaGreen" />
    </Grid>
</ContentPage>

Here it is, sitting in the center of the only row and column:

Now let’s define some more rows and columns. Let’s say four rows and three columns. We’ll also put some more BoxViews in the cells. To place an element in a specific cell in the Grid, you specify its row by setting the Grid.Row attached property and its column by setting the Grid.Column attached property. We’re going to talk about attached properties a bit later in the series, but for short these are properties of an element that are set on another element, like here where we set the row and column of the Grid parent element on its children. Besides, you don’t have to specify the first row and column, so whether you set Grid.Row=”0” or you omit this altogether, the effect will be the same. The same is true for the columns.

So, how do we add more rows and columns? Well, you can do it like so:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <Grid Margin="40">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <BoxView 
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="0"
            Grid.Column="1"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Column="2"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            Grid.Column="1"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            HeightRequest="100" 
            WidthRequest="100" 
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

As you can see in the code above, I skipped all the Grid.Row and Grid.Column properties with the value of 0 because this is the default value. If you now run the app, you will see the BoxViews arranged in four rows and three columns:

What if we didn’t specify the BoxViews’ sizes? Let’s try it out. Here’s the code:

...
    <Grid Margin="40">
        ...
        <BoxView 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="0"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

Now the children will occupy all the available space inside their cells:

Well, do we still have 12 separate BoxViews or just one big BoxView? We definitely have 12, which is hard to tell, though. It would be easier to tell them apart if we changed the colors of some of them:

...
        <BoxView 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="0"
            Grid.Column="1"
            Color="LightCyan" />
        <BoxView 
            Grid.Column="2"
            Color="LightCoral" />
        <BoxView 
            Grid.Row="1"
            Color="LightCyan" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            Color="LightCoral" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Color="LightCoral" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            Color="LightCyan" />
        <BoxView 
            Grid.Row="3"
            Color="LightCyan" />
        <BoxView 
            Grid.Row="3"
            Grid.Column="1"
            Color="LightCoral" />
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

Now we can see the separate children:

Or, let’s set the colors back to original and add some spaces between the rows and columns instead. We can do it by setting the RowSpacing and ColumnSpacing properties:

...
    <Grid Margin="40" RowSpacing="10" ColumnSpacing="10">
        <Grid.RowDefinitions>
            ...

This looks pretty decent:

Naturally, you don’t have to put elements in each and every cell. Let’s remove some of the children:

...
        <BoxView 
            Color="LightSeaGreen" />        
        <BoxView 
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="3"
            Color="LightSeaGreen" />        
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

I removed two elements in the middle column:

Also, you can put more than one element inside each cell. Have a look:

...
        <BoxView 
            Color="LightSeaGreen" />
        <Label             
            Text="[0,0]" 
            FontSize="80"
            FontAttributes="Bold"
            TextColor="White"
            HorizontalOptions="Center"
            VerticalOptions="Center" />
        <BoxView 
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <Button 
            Grid.Row="1"
            Grid.Column="1"
            WidthRequest="200"
            HeightRequest="100"
            BackgroundColor="Black"
            Text="Don't click"
            FontSize="30" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            HeightRequest="100"
            WidthRequest="100"
            Color="OrangeRed"
            HorizontalOptions="Start" />
        <Label   
            Grid.Row="2"
            Grid.Column="2"
            Text="[2,2]" 
            FontSize="80"
            FontAttributes="Bold"
            TextColor="White"
            HorizontalOptions="Center"
            VerticalOptions="Center" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            HeightRequest="100"
            WidthRequest="100"
            Color="OrangeRed"
            HorizontalOptions="End" />
        <BoxView 
            Grid.Row="3"
            Color="LightSeaGreen" />        
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

Here we additionally have a label in the first cell, a button in one of the middle cells and three elements inside one of the cells in the third column:

Now, the rows and columns are all very uniform. They have the same heights and widths, which often isn’t going to be the case in a real-life scenario. So, let’s differentiate them a bit. Also, for simplicity’s sake, let’s remove the elements we just added to the cells. We’re going to use both absolute and proportional sizing:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <Grid Margin="40" RowSpacing="10" ColumnSpacing="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="3*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="200"/>
        </Grid.ColumnDefinitions>
        <BoxView 
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            WidthRequest="240"
            HeightRequest="80"
            Color="Red" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            WidthRequest="150"
            HeightRequest="300"
            Color="Red" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="2"
            Color="LightSeaGreen" />        
        <BoxView 
            Grid.Row="3"
            Color="LightSeaGreen" />        
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

To make things more visible, I changed the color of the two BoxViews in the middle column and I set their sizes. Now, let’s analyze how we defined the row heights and column widths. Here’s the portion of the code where we did it:

...
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="3*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="200"/>
        </Grid.ColumnDefinitions>
        ... 

Let’s start with the rows. We used absolute sizing for the first row. Its height is set to 50 device-independent units, so it stays the same even if you resize the window (try it out).

The third row’s height is set to Auto. This means it autosizes based on the size of its children. The tallest child in the third row is the red BoxView in the middle column, so the whole row adjusts to it.

The second and fourth rows have heights set to 3* and * (which is the same as 1*) respectively. The star notation is used for proportional sizing. It just means that the leftover height (so without taking the rows with heights set to an absolute value or Auto into account) is allocated proportionally. Here the second row takes three times as much of it as the fourth row. This proportion will be retained if you resize the window as well.

Now the columns. We use absolute sizing for the third column. It just means the third column will always be 200 device-independent units wide.

The width of the middle column is set to Auto, so it takes the width of its widest child, which is the red BoxView in the second row.

Finally, the first column takes all the remaining width.

Here’s the result:

The RowDefinitions and ColumnDefinitions seem very verbose. Have a look one more time:

...
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="3*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="200"/>
        </Grid.ColumnDefinitions>
        ... 

Fortunately, this can be simplified. Let’s rewrite our code using this simplified notation:

...
    <Grid 
        Margin="40" 
        RowSpacing="10" 
        ColumnSpacing="10"
        RowDefinitions="50, 3*, Auto, *"
        ColumnDefinitions="*, Auto, 200">        
        <BoxView 
            Color="LightSeaGreen" />
        ...

Much more concise, isn’t it?

Sometimes you may want an element to span multiple rows or columns. This is possible by setting the Grid.RowSpan and Grid.ColumnSpan properties. Let’s remove some of the BoxViews to make some room and see how spanning multiple rows and columns may be implemented:

...
    <Grid 
        ...
        <BoxView 
            Color="LightSeaGreen" 
            Grid.ColumnSpan="3" />                        
        <BoxView 
            Grid.Row="1"
            Grid.Column="1"
            WidthRequest="240"
            HeightRequest="80"
            Color="Red" />
        <BoxView 
            Grid.Row="1"
            Grid.Column="2"
            Color="LightSeaGreen" 
            Grid.RowSpan="2" />
        <BoxView 
            Grid.Row="2"
            Color="LightSeaGreen" />
        <BoxView 
            Grid.Row="2"
            Grid.Column="1"
            WidthRequest="150"
            HeightRequest="300"
            Color="Red" />     
        <BoxView 
            Grid.Row="3"
            Color="LightSeaGreen" />        
        <BoxView 
            Grid.Row="3"
            Grid.Column="2"
            Color="LightSeaGreen" />
    </Grid>
</ContentPage>

Now the element in the first cell spans all three columns and the element in the last column of the second row span two rows.

That’s basically all you have to know about the Grid layout for now. Let’s move on to the next layout.

FlexLayout

And the next layout I’d like to briefly talk about is the FlexLayout. It’s similar to the StackLayout in that it also arranges its children in a horizontal or vertical stack, but it also combines the functionality available in the CSS flex display mode, if you have some CSS background.

Let’s remove the Grid and create a simple FlexLayout instead. We use the Direction property to define how the children are to be arranged. It can be set to Row, RowReverse, Column or ColumnReverse.

Here’s the code:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <FlexLayout Margin="40">
        <Button 
            Text="1" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="2" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="3" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="4" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="5" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="6" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="7" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="8" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />        
    </FlexLayout>
</ContentPage>

I used buttons with numbers on them to make it clear what order the elements are arranged in. Here’s what it looks like:

As you can see, we didn’t set the Direction attribute here, so the default value of Row is used. Let’s change it to RowReverse:

...
    <FlexLayout 
        Margin="40"
        Direction="RowReverse">
        <Button 
            ...

Now the elements are arranged like this:

For vertical arrangement we use Column:

...
    <FlexLayout 
        Margin="40"
        Direction="Column">
        <Button 
            ...

which results in this:

or ColumnReverse:

...
    <FlexLayout 
        Margin="40"
        Direction="ColumnReverse">
        <Button 
            ...

which results in this:

Yes, I know, now the elements don’t fit in the column. This is where the Wrap property comes in really handy. If set to Wrap, the elements that don’t fit in a single row or column are wrapped. Have a look:

...
    <FlexLayout 
        Margin="40"
        Direction="Column"
        Wrap="Wrap" >
        <Button 
            ...

Here I set Direction back to Column. Now all elements are displayed on the screen:

And if the window is resized, they will react accordingly:

You can also set Wrap to Reverse if you want the children to wrap in the other direction:

...
    <FlexLayout 
        Margin="40"
        Direction="Column"
        Wrap="Reverse" >
        <Button 
            ...

And here’s the result:

It works the same for the rows. Make sure to try it out. And now let’s test some other properties. First let’s remove some buttons and arrange them in a row. Then let’s justify them using the JustifyContent property. The default value of the property is Start, so the elements begin on the left if Direction is set to Row, on the right if Direction is set to RowReverse, at the top if Direction is set to Column and at the bottom if Direction is set to ColumnReverse. What matters is, as you can see, where the elements actually start. This rule applies to all values of JustifyContent. Anyway, as Start is the default value, let’s try End:

...
    <FlexLayout 
        Margin="40"
        Direction="Row"
        JustifyContent="End">
        <Button 
            Text="1" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="2" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="3" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="4" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />
        <Button 
            Text="5" 
            FontSize="100"
            WidthRequest="150"
            HeightRequest="150" />    
    </FlexLayout>
</ContentPage>

Now the buttons stick to the end of the row:

Here are the other values you can set JustifyContent to (provided Direction is set to Row):

JustifyContent
value

Result

Center

SpaceBetween

SpaceAround

SpaceEvenly

JustifyContent works for the main axis the elements are arranged in. Here it’s the horizontal axis, but if Direction were set to Column or ColumnReverse, it would be the vertical axis. For the other axis we use the AlignItems property. So, if Direction is set to Row like here, this property will be used to align the items vertically. The possible values are: Start, End, Center and Stretch. Let’s center the buttons both horizontally (with JustifyContent) and vertically (with AlignItems):

...
    <FlexLayout 
        Margin="40"
        Direction="Row"
        JustifyContent="Center"
        AlignItems="Center">
        <Button 
            ...

There are quite a few other properties you can use with FlexLayout, but for now this will do. Let’s move on to the next layout now, which is AbsoluteLayout.

AbsoluteLayout

In an AbsoluteLayout you position and size elements using explicit values or values relative to the layout’s position and size. The position is specified by the top-left corner of the child relative to the top-left corner of the parent (which is the AbsoluteLayout).

Let’s remove the FlexLayout with buttons and create an AbsoluteLayout with BoxViews. Let’s also set the background color of the AbsoluteLayout so that we can see it better. We use the AbsoluteLayout.LayoutBounds property to position and size the children. The value of this property consists of four numbers in a string. The first two numbers determine the position, the other two are for sizing. Have a look:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <AbsoluteLayout Margin="40" BackgroundColor="Beige">
        <BoxView 
            Color="Green"
            AbsoluteLayout.LayoutBounds="100, 60, 100, 50" />
        <BoxView 
            Color="Red"
            AbsoluteLayout.LayoutBounds="400, 380, 200, 100" />
        <BoxView 
            Color="Blue"
            AbsoluteLayout.LayoutBounds="750, 30, 400, 400" />
    </AbsoluteLayout>
</ContentPage>

Here we have three BoxViews. The first one is green. It’s positioned 100 units from the left (which means its top-left corner is 100 units from the left), 60 units from the top and its size is 100 by 50 units. The same rules apply to the other elements. Here’s the result:

Here we used absolute values to position and size the elements. But we can also use proportional values for the position or for the size. We can also mix absolute and proportional values. To enable proportional positioning, we need to set the AbsoluteLayout.LayoutFlags attached property to PositionProportional. Similarly, we use SizeProportional for proportional sizing. With proportional positioning and sizing we use the values from 0 to 1. Have a look:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <AbsoluteLayout Margin="40" BackgroundColor="Beige">
        <BoxView 
            Color="Green"
            AbsoluteLayout.LayoutBounds=".5, .5, 200, 200" 
            AbsoluteLayout.LayoutFlags="PositionProportional"/>
        <BoxView 
            Color="Red"
            AbsoluteLayout.LayoutBounds="100, 400, .2, .1"
            AbsoluteLayout.LayoutFlags="SizeProportional" />
        <BoxView 
            Color="Blue"
            AbsoluteLayout.LayoutBounds="1, 1, .25, .5"
            AbsoluteLayout.LayoutFlags="PositionProportional,SizeProportional" />
    </AbsoluteLayout>
</ContentPage>

The elements are positioned and sized in the following way:

BoxViewPositionSize
   
Greenproportional (.5, .5) – centeredabsolute (200, 200) – 200 x 200 units
Redabsolute (100, 400) – 100 units from the left and 400 units from the topproportional (.2, .1) – 20% of the width of the AbsoluteLayout and 10% of its height
Blueproportional (1, 1) – all the way to the right and all the way to the bottomproportional (.25, .5) – a quarter of the layout’s width and half its height

There are more options you can set the AbsoluteLayout.LayoutFlags property to, so feel free to check them out.

Well, these are all the basic layouts we should be familiar with for now.

Nested Layouts

Layouts can be nested. The example we’re going to build isn’t particularly useful. It’s just to demonstrate how nesting layouts works. The code is pretty lengthy, but it should be pretty easy to read and understand:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="Slugrace.TestPage"
             Title="TestPage">
    <FlexLayout 
        BackgroundColor="Beige"
        Direction="Column">

        <HorizontalStackLayout
            BackgroundColor="Black"
            Spacing="50"
            Padding="10">
            <Label 
                Text="File"
                TextColor="White"
                FontSize="20"
                FontAttributes="Bold" />
            <Label 
                Text="Edit"
                TextColor="White"
                FontSize="20"
                FontAttributes="Bold" />
            <Label 
                Text="View"
                TextColor="White"
                FontSize="20"
                FontAttributes="Bold" />
            <Label 
                Text="Help"
                TextColor="White"
                FontSize="20"
                FontAttributes="Bold" />
        </HorizontalStackLayout>

        <Grid
            Margin="40"
            Padding="10"
            BackgroundColor="Bisque"
            RowDefinitions="100, *, 100"
            ColumnDefinitions="100, *, 100"
            RowSpacing="10"
            ColumnSpacing="10">
            <Label
                Text="Start"
                FontSize="30"
                TextColor="Firebrick" />
            
            <AbsoluteLayout
                Grid.Column="1"
                BackgroundColor="Beige">
                <Button
                    Text="1"
                    FontSize="40"
                    AbsoluteLayout.LayoutBounds=".1, .5, .3, .5"
                    AbsoluteLayout.LayoutFlags="PositionProportional, SizeProportional" />
                <Button
                    Text="2"
                    FontSize="40"
                    AbsoluteLayout.LayoutBounds=".9, .5, .3, .5"
                    AbsoluteLayout.LayoutFlags="PositionProportional, SizeProportional" />
            </AbsoluteLayout>

            <BoxView 
                Grid.Column="2"
                Grid.RowSpan="3"
                Color="Firebrick" />

            <BoxView
                Grid.Row="1"
                Grid.Column="0"
                Grid.RowSpan="2"
                Grid.ColumnSpan="2"
                Color="Firebrick" />
        </Grid>           
        
    </FlexLayout>
</ContentPage>

And here’s the result:

In the next part of the series we’ll clarify some property-related terms that you can often come across like property elements, content properties or attached properties.


Spread the love

Leave a Reply