Converting Xamarin.Forms to WindowsUI and Uno: StackLayout v StackPanel

When moving from Xamarin.Forms across to WinUI, one of the simpler changes to make is to switch out StackLayout for StackPanel. Or so you’d think – unfortunately StackLayout has some quirky behaviour that needs some extra handling to get right. In this post we’ll look at why the StackPanel replacement doesn’t always work, and what you can do to fix it in order to get your application to work across all the WinUI and Uno platforms.

Let’s start with a simple XAML example.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="AwesomeApp.MainPage">
    <StackLayout>
        <Label Text="Welcome to Xamarin.Forms!"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
        <Button Text="Click me"
                Clicked="Button_Clicked" />
    </StackLayout>
</ContentPage>

If you’re not familiar with using the StackLayout, you may be thinking that this would display a Label and Button in a stack, located at the top of the screen. In actual fact, as you can see from the following image, it actually has the Label roughly in the center of the screen and the Button at the bottom of the screen.

I said the Label is roughly in the center of the screen because it’s actually centered in the space above the Button (this is what the CenterAndExpand option does), so not technically the center of the screen.

If we attempt to translate this to WinUI by replacing the StackLayout with a StackPanel, we’d end up with a layout that looks like the following image.

Before converting our XAML we’re going to replace the StackLayout with a Grid that gives the same layout. Note that there are two rows: the Button is located in Row 1 which is Auto height; the Label is centered in Row 0 that takes up the remainder of the space.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="AwesomeApp.MainPage">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Label Text="Welcome to Xamarin.Forms!"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
        <Button Grid.Row="1"
                Text="Click me"
                Clicked="Button_Clicked" />
    </Grid>
</ContentPage>

The final converted XAML for WinUI and Uno is

<Page x:Class="AwesomeApp.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:AwesomeApp"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock Text="Welcome to Xamarin.Forms!"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center" />
        <Button Content="Click me"
                Grid.Row="1"
                Click="Button_Clicked"
                HorizontalAlignment="Stretch" />
    </Grid>
</Page>

And here it is running on Android and Windows

WinUI + Uno for Android
WinUI Desktop

And there you have it – if you’re converting XAML from Xamarin.Forms to WinUI+Uno, I would recommend locating use of CenterAndExpand and convert it to use a Grid layout instead.