Unlock the true potential of cross-platform development with eXtensible Application Markup Language (XAML) and .NET MAUI (Multi-platform App UI). Say goodbye to the frustration of writing code for each platform separately. With .NET MAUI, you can develop powerful business applications that run seamlessly on Windows, Android, Mac, and iPhone devices. In this series of articles, I'll guide you through the process of building a complete business application from scratch, step by step.

Starting with the fundamentals of XAML, you'll learn how to create your first .NET MAUI application. From there, you'll dive into constructing various screens, including a detailed view, and using grid, stack, and flex layouts. Witness first-hand how effortless it is to craft screens that adapt flawlessly to any screen size. Whether you're an aspiring developer or a seasoned pro, this article gets you started on building business applications using XAML and .NET MAUI. If you're a C# Windows Forms developer or a WPF developer who wants to learn to use XAML in .NET MAUI applications, this article is also for you. To get the most out of this article, I'm assuming that you're very familiar with C#, .NET Core, and object-oriented programming.

An Overview of .NET MAUI

.NET MAUI is a technology that helps you build native applications on Windows, macOS, iOS, and Android, all using a single C# codebase. To design the UI for your applications, use the XAML markup. The XAML you write is translated into C# for the .NET runtime designed to run on the target OS. XAML has been around for many years and was originally created for use in WPF applications to create robust, resolution-independent screens. Like HTML, XAML uses a flow-layout instead of absolute positioning to ensure that screens can adapt to devices of any size.

The goal of .NET MAUI is to allow you to create multi-platform applications using a single project and codebase. You're also allowed to add platform-specific source code and resources if needed. Starting with .NET 6, .NET MAUI targets platform-specific frameworks (Figure 1) for creating apps on .NET Android, .NET iOS, .NET macOS, and Windows UI 3 (WinUI 3). These frameworks all use the same .NET Base Class Library (BCL). The BCL abstracts the details of the underlying platform away from your code. The BCL depends on the .NET runtime to provide the execution environment for your code. For iOS, macOS, and Android, the .NET runtime is implemented by Mono which is an implementation of the runtime designed for those platforms. On Windows, the .NET CoreCLR provides the execution environment.

Figure 1: .NET MAUI is built on top of several technologies to ensure that it works across all platforms.
Figure 1: .NET MAUI is built on top of several technologies to ensure that it works across all platforms.

Instead of having to learn how each platform defines their UI and how they express business logic in their different programming languages, .NET MAUI provides a single framework for building the UIs and business logic. .NET MAUI can create applications for either mobile or desktop. You may use a Windows computer to create applications that target either Windows or Android; however, to build applications for iOS or macOS does require a Mac computer.

The Basics of XAML

XAML is a declarative markup language like HTML. Unlike HTML, however, XAML is mapped directly to C# types defined within the .NET API used by WPF or .NET MAUI. It's interesting to note that you can write a WPF or .NET MAUI application solely in C# without any XAML at all. This is because each XAML control maps directly to a C# class in the .NET library.

Of course, it's much simpler to use XAML because it's more visual and can express controls with less code than writing in C#. In addition, using XAML allows a designer to create the UI for an application, and a C# programmer focuses on the business logic. This saves time and is a nice division of labor. XAML files are text files with a .xaml extension, thus making it easy to share these files between the programmer and the designer.

XAML is simply XML, and thus, each element starts with an opening tag such as <Button> or <Label>, as shown in Figure 2. Each element ends with a closing tag such as </Button> or </Label>. Both the start and end tags together make up an XAML control or element. You'll find that most developers use “tag” and “element” interchangeably, but technically, they're different.

Figure 2: An element consists of a start and end tag with usually some content in between them.
Figure 2: An element consists of a start and end tag with usually some content in between them.

One thing to note about the <Label> element shown in Figure 2 is that you're setting the Text property of the <Label> by including it between the start and ending tags. Not all XAML elements allow you to set values in this manner. Most use the attribute syntax, as described in the next section.

Set Properties Using Attribute Syntax

Tags such as <Button> or <Label> map directly to Button and Label types in C#. To set properties of the class, you use attributes. An attribute is represented by a name (the property name in C#), followed by an equal sign, and then the value you wish to set contained within quotes. Figure 3 creates an instance of a Label class and sets the Text property to the value “First Name”.

Figure 3: An attribute sets properties of an element.
Figure 3: An attribute sets properties of an element.

Because all properties are set within the starting tag, you don't need the closing tag at all. Instead, you may express the <Label> element shown in Figure 3 with the following code snippet.

<Label Text="First Name"/>

Set Properties Using Element Syntax

Instead of using attributes to set properties of a class, you may also use element syntax. Sometimes the value you must set is unable to be set within quotation marks, in this case, between the opening and closing tags, you may use the format <TypeName.PropertyName>Value</TypeName.PropertyName>, as shown in the code snippet below. As you can see, using the attribute syntax is often more compact, but either method is acceptable.

<Label>
    <Label.Text>
        First Name
    </Label.Text>
</Label>

Create Elements Using C#

You're allowed to create controls using C# in the code-behind for a page. For example, to create the Label shown in the last few examples, you write the C# code like this:

Label label = new () {
    Text = "First Name"
};

Only One Root Element Is Allowed

In XAML and HTML, only one root element is allowed per file. For example, in an HTML document, you must have one and only one <html> tag within which all your other HTML is contained. In a XAML file you must also only have one tag, such as a <ContentPage>, <ContentView>, or <ResourceDictionary>. All other XAML must be contained within the opening and closing tags of these. An example of this is shown in Listing 1.

Listing 1: Each XAML file must have only one defined container into which all other XAML is contained.

<ContentPage
    xmlns="http://schemas.microsoft .com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml";     
    x:Class="AdventureWorks.MAUI.MainPage">

    <Label Text="First Name" />

</ContentPage>

XML Namespaces

An XML namespace is like a C# namespace in that it contains a set of classes that may be used without any prefixing. For example, in C#, you may reference a File class directly if you've declared a Using statement to the System.IO namespace at the top of the C# file. In the <ContentPage> example you just looked at, the xmlns="http://schemas.microsoft.com/dotnet/2021/maui"; attribute is like a Using statement to specify which set of element names (<Button>, <Label>, etc.) may be used without any prefix.

The second XML namespace xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"; is also a Using statement but has aliased any language construct within this namespace so it must use an x followed by a colon in front of any name of a language construct used from this namespace. Constructs you'll use frequently within this namespace include x:Class, x:Key, x:Name, and x:Type. You're also allowed to bring in your own XML namespaces and set your own alias. Both of these techniques are illustrated a little later in this article.

XAML and Code-Behind Are Compiled Together

The <ContentPage> element in the MainPage.xaml file has an x:Class="AdventureWorks.MAUI.MainPage" attribute (Listing 1). The XAML in the MainPage.xaml file is parsed into a partial class using the namespace and class name specified in this x:Class attribute. This partial class and the partial class located in the MainPage.xaml.cs file (Listing 2) are compiled together to create the final page to be displayed to the user.

Listing 2: The class name in the code-behind file is marked as partial so it can be compiled with the parsed XAML file.

namespace AdventureWorks.MAUI;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

Events in XAML

Some controls can raise an event to which you can respond in the code-behind of your XAML page. One example is a Button control that raises a Clicked event when the button is pressed upon by the user. In XAML, define the name of the event procedure to call by adding a Clicked attribute to the Button start tag. The value assigned to the Clicked attribute is the name of an event procedure located in the C# code-behind file.

<Button Text="Save" Clicked="SaveButton_Clicked"/>

The C# code you write in the code-behind looks like the following code snippet:

private void SaveButton_Clicked(object sender, EventArgs e)
{
    // TODO: Respond to the event here
}

The first parameter received into this event procedure is a reference to the button that initiated the event. The second parameter is a generic EventArgs object. There are no special event arguments passed to the Clicked event on a button. Other objects may pass some event arguments for different events that are raised.

Reference Controls from the Code-Behind

To change the property of a control on your XAML with C# code, name your control uniquely on the page. For example, if you have an Entry control that holds the First Name, assign it a name using the construct x:Name="uniqueId", as shown in the code snippet below.

<Entry x:Name="firstName" />

In the code behind, use C# to retrieve the value from this Entry control's Text property and display it on the console using the following code.

Console.WriteLine(firstName.Text);

XAML Is Case-Sensitive

All element and attribute names are case-sensitive when using XAML. This is because C# is case-sensitive and, to compile XAML into C#, the names must match exactly. The values contained within the properties may or may not be case-sensitive and will depend on what the property will accept. If you're setting a Text property, for example, you may use any case you wish. If you're setting a Boolean property, you may use either true or True, because the XAML parser knows to always convert True to lower-case for C#. However, if the property you are trying to set is a C# enumeration, you must spell it exactly or the XAML parser will give you an error message.

Installing .NET MAUI

You need to ensure that .NET MAUI has been installed on your machine. Detailed directions are in this link: https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?tabs=vswin. I've included the following simplified instructions to get you going quickly.

Bring Up the Visual Studio Installer Tool

In the search bar on your windows computer (Figure 4), type in Visual Studio Installer, and when you locate the Visual Studio Installer icon, click on it to launch the VS Installer utility.

Figure 4: Locate the Visual Studio Installer utility.
Figure 4: Locate the Visual Studio Installer utility.

When the installer utility has launched, on the Workloads tab (Figure 5), ensure that you have the following checked.

  • ASP.NET and web development
  • .NET Multi-platform App UI development (MAUI)
  • .NET desktop development
Figure 5: Install appropriate workloads for this class.
Figure 5: Install appropriate workloads for this class.

Click on the Individual components tab and ensure that you have the .NET 8 Runtime (Long Term Support) selected, as shown in Figure 6.

Figure 6: .NET 8.0 Runtime (Long Term Support) is required.
Figure 6: .NET 8.0 Runtime (Long Term Support) is required.

Locate the .NET MAUI SDK for Windows and ensure that it's selected, as shown in Figure 7.

Figure 7: .NET MAUI SDK for Windows is required.
Figure 7: .NET MAUI SDK for Windows is required.

If you wish to run the Android Emulator, select the Google Android Emulator (local install), as shown in Figure 8.

Figure 8: Google Android Emulator (local install) is required.
Figure 8: Google Android Emulator (local install) is required.

The last item to select is the Android SDK setup, as shown in Figure 9.

Figure 9: Android SDK setup is required.
Figure 9: Android SDK setup is required.

.NET 8

.NET 8 (or later) is the best version to use when developing .NET MAUI applications, so be sure to download and install it if you haven't already done so. To see whether you have .NET 8 installed, open a command prompt and type in the following command:

dotnet --version

The results from running this command should look something like the following output:

8.0.xxx

If you don't have .NET 8, you can download and install it from https://dotnet.microsoft.com/en-us/download/dotnet/8.0.

Activate Hyper-V on Your Computer

In your Windows Search bar, type in Turn Windows features on or off to display the widget shown in Figure 10.

Figure 10: Search for Turn Windows features on or off.
Figure 10: Search for Turn Windows features on or off.

Once you click on this widget, you should see the Windows Features dialog appear (Figure 11). Scroll down and locate the Hyper-V component and ensure that it's checked.

Figure 11: Turn on Hyper-V on your Windows computer.
Figure 11: Turn on Hyper-V on your Windows computer.

Scroll down a little further and locate the Windows Hypervisor Platform (Figure 12) and ensure that it has been checked as well. These components help you have the best performance while developing your .NET MAUI applications.

Figure 12: Turn on Windows Hypervisor Platform on your Windows computer.
Figure 12: Turn on Windows Hypervisor Platform on your Windows computer.

Build Your First .NET MAUI Application

Now that you've learned a little about XAML and have installed the necessary components for building a .NET MAUI application, let's dive right in and start building a .NET MAUI application. Open Visual Studio 2022 and click on Create a new project, as shown in Figure 13, to advance to the next page.

Figure 13: Select Create a new project from the Visual Studio screen.
Figure 13: Select Create a new project from the Visual Studio screen.

On the Create a new project page (Figure 14), type .NET MAUI into the search box (1). Select the C# language (2) because this is the only language you may use, currently, to develop .NET MAUI applications. From the list of templates, click on the .NET MAUI App (3) templates, and then click the Next button to advance to the next page.

Figure 14: Select a .NET MAUI App from the list of templates.
Figure 14: Select a .NET MAUI App from the list of templates.

On the Configure your new project page (Figure 15), set the Project name (marked as 1 on the image) of the new project to AdventureWorks.MAUI. Set the Location (marked as 2) to a folder on your hard drive where you'd like to place your C# projects. Uncheck the Place solution and project in the same directory (marked as 3) as you're going to be adding more projects to this solution later and will want to place each of them into a separate folder. Click the Next button to advance to the next page.

Figure 15: Set the project name and location.
Figure 15: Set the project name and location.

On the Additional information page (Figure 16), choose .NET 8 (Long-Term Support), or a later version if you have one installed on your computer. Click on the Create button and Visual Studio creates your new .NET MAUI application.

Figure 16: Choose .NET 8 or later when creating a .NET MAUI application.
Figure 16: Choose .NET 8 or later when creating a .NET MAUI application.

Try It Out

Once the application has been created, click on the Windows Machine button (Figure 17) on the Visual Studio toolbar to run this application on your windows computer. The first time you try to run a .NET MAUI application, the build process takes some time, as there are lots of dependencies to load, and it must install the application into your Windows operating system.

Figure 17: Run the .NET MAUI application on Windows.
Figure 17: Run the .NET MAUI application on Windows.

Once the application launches, you'll see a screen that looks like Figure 18. Click on the button Click me and the text within the button changes to reflect how many times you've clicked the button. This .NET MAUI application is just like a normal Windows application in that you can resize the window, minimize, maximize, or close the window.

Figure 18: The .NET MAUI template application running on Windows.
Figure 18: The .NET MAUI template application running on Windows.

After you run the application using Visual Studio, go to Add or remove programs in Control Panel and find AdventureWorks.MAUI as one of the installed applications.

Try It Out on an Android Emulator

Close the Window or stop the application using Visual Studio. Click the arrow next to the Windows Machine and select Android Emulators > Pixel 5 – API 34 (Android 14.0 – API 34), as shown in Figure 19.

Figure 19: Choose the Android Emulators > Pixel 5 – API 34.
Figure 19: Choose the Android Emulators > Pixel 5 – API 34.

After selecting the Android emulator, it appears as the configuration item (Figure 20). Click on this button to run the Android emulator. Again, it may take a few minutes to run this emulator, so be patient. Even if the emulator comes up, it may still take a few minutes for VS to compile the application and deploy it to the emulator. Keep an eye on the status bar at the bottom left of the Visual Studio window, as it should show you the progress and provide you with messages as to what's happening.

Figure 20: Run the application in the Android emulator.
Figure 20: Run the application in the Android emulator.

Once the application is deployed, you should see a screen that looks like Figure 21. This is the Android emulator and shows you how this application would look on an Android phone. Click on the button to see the button text change to tell you how many times you have clicked on the button.

Figure 21: The .NET MAUI template application running on the Android emulator.
Figure 21: The .NET MAUI template application running on the Android emulator.

Modify the Content Page

When you create a new .NET MAUI application, Visual Studio provides you with a MainPage.xaml that has some controls on it. For just learning XAML and .NET MAUI, this XAML might seem a little overwhelming, so let's delete it and build up the sample slowly. Open the MainPage.xaml file and remove lines 7 through 34. This is the <ScrollView> tag and everything down to (and including) the closing </ScrollView> tag. Open the MainPage.xaml.cs file and remove the code behind, except for the constructor, as shown in the following code snippet.

namespace AdventureWorks.MAUI;
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

Add a Label

Add the following <Label> element where the <ScrollView> element was located.

<Label Text="Adventure Works"
       FontSize="Large"
       VerticalOptions="Center"
       HorizontalOptions="Center" />

A Label control is used to display textual information to your user. Set the Text property to the value you wish to display. In the code above, set the FontSize property to the value Large to display the text in a larger font than the default size. The VerticalOptions and HorizontalOptions properties allow you to center the Label control within the page by setting their values to Center.

Set the Title Property

Bring up the Properties window (Figure 22) by selecting View > Properties Window from the VS menu. Click on line 2, so you're focused on the <ContentPage> element and you should see the Properties window refresh with the list of properties you can set for the ContentPage element.

Figure 22: The Properties window allows you to set properties on whatever control has focus.
Figure 22: The Properties window allows you to set properties on whatever control has focus.

Set the Title property of the ContentPage to Adventure Works and press the Enter key. You should see that the XAML has been written for you in the editor. Close the Properties window, click just after the Title="Adventure Works" attribute, and hit the spacebar. You should see an IntelliSense window open in your editor that has the same list of properties you saw in the Properties window. Using IntelliSense, set the BackgroundColor attribute to Gray.

Try It Out

Run the application on Windows to see the new Title attribute on the screen. Notice the gray background, as shown in Figure 23.

Figure 23: Setting properties on the  affects the appearance of the view.
Figure 23: Setting properties on the affects the appearance of the view.

Leave the .NET MAUI application running, go back to Visual Studio and remove the BackgroundColor property from the ContentPage element. If you look at the running application, you should see that the background color has returned to the original color. You can also run this on the Android emulator and see the same thing happen on that screen as well.

Change the Shell Properties

When the application is running on Windows, notice that the window title reads “AdventureWorks.MAUI”. This is the name of the project, but it's probably not what you want on the Window title. Open the AppShell.xaml file and set the attributes on the <Shell> element.

<Shell x:Class="XamlBasicsMAUI.AppShell"
    xmlns="http://schemas.microsoft..."
    xmlns:x="http://schemas.microsoft..."
    xmlns:local="clr-namespace:XamlBasicsMAUI"
    Shell.TitleColor="Black"
    Shell.BackgroundColor="Gray"
    Title="Adventure Works"
    Shell.FlyoutBehavior="Disabled">

Try It Out

Run the application on the Windows Machine and see that the background of the title area is gray and the title color is black (Figure 24). Also notice there is now a title on the Windows title bar area. The title bar shows up on Windows, but not on an Android emulator or iOS phone emulator because cell phones don't have a title bar area.

Figure 24: Changes to the application shell are controlled by the  element.
Figure 24: Changes to the application shell are controlled by the element.

Remove the TitleColor and BackgroundColor attributes you just added to the <Shell> element, as it's better to not set colors for the shell. This allows your application to pick up any theme colors set either in your OS, or from global styles in your application, as will be explained later.

The Shell Class

The first line in the AppShell.xaml file is a <Shell> element that provides the top-level element in which all other elements are contained. The <Shell> element is like the <html> element on an HTML page. This <Shell> element is where you describe the navigation to allow you to move from one page to another. The Shell class uses URI-based navigation that can be defined through XAML or C# code. The navigation structure can be defined as tabs (menus), flyouts, and tab bars. Each tab or flyout maps to a content page which, when clicked upon, displays that content page within the shell of the application. You'll see examples of navigation later in this article series.

Make the Window Maximized

When you run the application on Windows, notice that the page isn't being displayed as a full-screen window. If you want your page to be full screen, open the MauiProgram.cs file and add a few Using statements at the top of the file. These Using statements must be wrapped within a #if WINDOWS conditional compile, as making a screen maximized is only applicable on a Windows operating system.

#if WINDOWS
using Microsoft.Maui.LifecycleEvents;
using WinRT.Interop;
using Microsoft.UI;
using Microsoft.UI.Windowing;
#endif

The Using statements may look disabled; this is okay, as they're applied when you run the .NET MAUI application using the Windows Machine start button within Visual Studio. Next, add a new method within the MauiProgram class named SetWindowOptions(), as shown in Listing 3.

Listing 3: Add a method to change the window attributes.

#if WINDOWS
public static void SetWindowOptions(MauiAppBuilder builder)
{
  builder.ConfigureLifecycleEvents(events =>
  {
    events.AddWindows(wndBuilder =>
      {
        wndBuilder.OnWindowCreated(window =>
          {
             IntPtr hndl = WindowNative.GetWindowHandle(window);
             WindowId winId = Win32Interop.GetWindowIdFromWindow(hndl);
             AppWindow appWnd = AppWindow.GetFromWindowId(winId);
             if (appWnd.Presenter is OverlappedPresenter p) {
                  p.Maximize();

                  //p.HasBorder = false;
                  //p.HasTitleBar = false;
                  //p.IsAlwaysOnTop = false;
                  //p.IsMaximizable = false;
                  //p.IsMinimizable = false;
                  //p.IsModal = false;
                  //p.IsResizable = false;
             }
          });
      });
  });
}
#endif

It's not important that you understand every line of code in Listing 3. The important part is that you get access to the current window's Presenter object and invoke the Maximize() method to set the page to full screen. There are several commented out properties that you may set as well. I included them in the code so you can see the various properties you may manipulate on the current window.

The code in Listing 3 also looks disabled but is activated when run on a Windows machine. You now need to call the SetWindowsOptions() method by calling it within the CreateMauiApp() method. Just before the #if DEBUG statement, call this new method, as shown in the following code snippet:

#if WINDOWS
  SetWindowOptions(builder);
#endif

Try It Out

Run the application on the Windows Machine and see that the window is now full screen. If you ran this code on the Android emulator, the code is ignored because it's wrapped in the conditional compile statement #if WINDOWS.

Using the .NET MAUI Grid Control for Page Layout

The ContentPage element has a single Content property that's the area between the opening <ContentPage> tag and the closing </ContentPage> tag. This content area is a View class and is used to display a single control. If you want to have more than one control on a page, those controls must be wrapped within a Container control. A Container control can have one or more controls within it. How that container displays the controls depends on what kind of container it is. For example, you have a few different kinds of stack container controls that display controls either horizontally or vertically. You also have a Grid that allows you to place controls into rows and columns.

A Grid control is not for displaying rows and columns of data from a database table. Instead, it's for laying out labels, text boxes, radio buttons, check boxes, and buttons into rows and columns. Look at Figure 25 and you can see that there are eight rows and two columns defined in a Grid control. In rows one and two are Label controls that display information about the page. Row three contains a BoxView control that makes the horizontal line to separate the header information from the data entry portion of the page. In rows four through seven, there's one Label control to tell the user what should be entered into the one Entry control located in the second column. In the eighth row and second column is a HorizontalStackLayout control into which two Button controls are placed so they appear side-by-side. Don't worry, you're going to learn about each of these controls as you work your way through this article.

Figure 25: A standard data entry page typically has several rows and two columns.
Figure 25: A standard data entry page typically has several rows and two columns.

Defining Rows and Columns

Within a <Grid> element, you may define rows and columns in two different ways. The first is to use the element-based approach as shown below.

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

The second method to define rows and columns is to use attribute-based XAML. Each row's height and each column's width is separated by a comma, as shown in the following code snippet.

<Grid RowDefinitions="Auto,Auto,...,Auto"
    ColumnDefinitions="Auto,*">
    ...
</Grid>

Auto and Star (*) Spacing

The Width property of a ColumnDefinition element or the Height of a RowDefinition element within a grid can be defined in one of three ways. If you use a single number, this is called absolute spacing and is a device-independent unit of measure. I highly recommend that you never use this spacing, as it makes it hard for the controls to adjust to different screen sizes. The second method is to use a setting of Auto. This setting means the height or the width is based on the largest-sized control within that row or column.

The third method is to set the Height or Width properties to a Star (*). This tells the page layout engine to use the rest of the available space within that row or column. This method is called proportional spacing. You may also place a number before the star, such as 2*, in which case, that row or column will receive two times the height/width as the rest of the rows/columns within the grid. This number can be any number you want, based on how large you want the row/column in relation to the others. Consider the following <Grid> definition:

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

In this grid, you have three rows: Auto, star (*), and the absolute value of 100. The grid first allocates 100 device-independent units to the third row. Next, it looks at all controls within the first row and determines the largest height value of those and uses that number to set the Height property of the first row. The Height property of the second row is set to whatever value is left over after calculating the heights of the other rows.

There are three columns in this grid, of which the first one has an absolute value of 200 device-independent units. The grid allocates that column first, then subtracts that amount away from the width of the page on which this grid is contained. Within the third column, it determines the width of the largest control and assigns that value to the third column's Width property. The remaining width is then allocated to the second column.

As you can imagine, there could be several columns that both use a star (*) for the width. If that's the case, after all other widths are calculated, the remaining width is split equally among those columns.

Assigning Rows and Columns to Controls

After you've created your <Grid> and set up the appropriate number of rows and columns, an “attached property” allows a child element of a Grid to attach itself to the grid in a certain row and column. For example, to place the Label and the Entry control into the fifth row and the first and second column, respectively, as shown in Figure 25, use the following XAML:

<Label Grid.Row="4"
     Grid.Column="0"
     Text="First Name" />
<Entry Grid.Row="4"
     Grid.Column="1"
     Text="" />

In the code above, you're telling the Grid to place the Label control in the fourth row and the first column. The row and column numbering starts with zero, like all collections in .NET. The Grid.Column="0" attribute doesn't need to be set because zero is the default for any row or column.

ColumnSpan and RowSpan

Looking at Figure 25, you can see that the first two labels seem to span across the two columns. This is accomplished by using the attached property Grid.ColumSpan. The Grid.ColumnSpan attached property indicates how many columns the control it's attached to should span within a Grid control. Because the Grid in the example has two columns defined, set the ColumnSpan attribute to “2” to have the Label take up two columns, as shown in the XAML below.

<Label Grid.Row="0"
     Grid.Column="0"
     Grid.ColumnSpan="2"
     Text="User Information"
     FontSize="Title" />

There is also a Grid.RowSpan attached property that indicates to the grid how many rows the control it is attached to should span.

Create the ContentPage

Let's now start creating the page shown in Figure 25. Open the MainPage.xaml file and replace the Label control you added earlier with the XAML shown in the following code:

<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,*">
    <Label Grid.Row="0"
           Grid.ColumnSpan="2"
           Text="User Information"
           FontSize="Title" />
    <Label Grid.Row="1"
           Grid.ColumnSpan="2"
           Text="Use this Screen to Modify User Information."
           FontSize="Body" />
    <BoxView Grid.Row="2"
             Grid.ColumnSpan="2"
             HeightRequest="1"
             Color="Black" />
</Grid>

The BoxView Control

A BoxView control draws a rectangle with a specified height and width. If you set the HeightRequest property to a one (1), this control draws a line. By default, the line takes up the complete width of the container within which it's drawn. Set the Color property to whatever color you wish. In this case you are going to use the value Black.

Try It Out

Run the application to see the two labels and the line appears on your main page.

Spacing, Margins, and Alignment

When you run the application, notice that there's no spacing between the labels and the box view, and between the labels and box view and the top and sides of the page. Also note that the Label controls are lined up at the top of the Entry controls to the right of them. All of these issues can be solved quickly by setting just a few properties, as discussed in the next few sections.

Use Row and Column Spacing for Separation of Controls

The Grid control allows to you set RowSpacing and ColumnSpacing properties to determine how much space appears between each row and column respectively. Set each of these properties on the Grid start tag and set them to a value of ten (10) for each, as shown in the following code snippet:

<Grid RowDefinitions="Auto,Auto,..."
      ColumnDefinitions="Auto,*"
      ColumnSpacing="10"
      RowSpacing="10">

Try It Out

Run the application to see that the labels and the box view controls now have a little room between them.

Apply Margin and Padding

Although you now have room between the labels and the box view, they're still right against the top and left edge of the page. The reason they're flush with the page is because the Grid control itself doesn't have any separation from the ContentPage. The RowSpacing and ColumnSpacing properties affect the rows and columns within the Grid. To put some distance between the Grid and the ContentPage, you need to use Margin property. As you can see in Figure 26, the Margin property affects the area around the control to which it is applied.

Figure 26: Use Margin and Padding to achieve separation between controls.
Figure 26: Use Margin and Padding to achieve separation between controls.

The Margin property is a Thickness structure. This structure has properties such as Top, Right, Left, and Bottom. The Thickness can be expressed as a single number, such as Margin="10", in which case, it moves the left side, the top, the right side, and the bottom and away from the container that surrounds the control by ten device-independent pixels. Margin may also be expressed with two numbers, Margin="5,10", with each number separated by a comma in which case it applies the first number to the left and right sides, and the second number to the top and bottom. Finally, Margin may be expressed with all four values, Margin="2,4,6,8", in which case the values are applied to the Left, Top, Right, and Bottom in that order.

The Padding property is also of the type Thickness and may be set the same as the Margin property. The Padding property places the spacing between the border of the control and the contents inside the control. Add a Margin and a Padding property to the Grid control, as shown in the following code snippet. To really see the difference between the two properties, set the BackgroundColor="Blue" on the Grid control and try different Margin values.

<Grid RowDefinitions="Auto,Auto,Auto, ..."
      ColumnDefinitions="Auto,*"
      ColumnSpacing="10"
      RowSpacing="10"
      Margin="10"
      Padding="10">

Try It Out

Run the application and see that the controls are now spaced away from the page border. Because you're using the RowSpacing and ColumnSpacing properties, you probably don't need to set the Padding property on the Grid.

Add the Data Entry Fields

Let's now add the Label and Entry controls to the Grid to make the page look more like Figure 25. Add four more “Auto” heights to the RowDefinitions property on the starting Grid tag. Add the XAML shown in Listing 4 below the BoxView you added earlier.

Listing 4: Add data entry fields to each row and column to the grid.

<Label Grid.Row="3"
       VerticalOptions="Center"
       Text="Login ID" />
<Entry Grid.Row="3"
       Grid.Column="1"
       Text="" />
<Label Grid.Row="4"
       Text="First Name" />
<Entry Grid.Row="4"
       Grid.Column="1"
       Text="" />
<Label Grid.Row="5"
       Text="Last Name" />
<Entry Grid.Row="5"
       Grid.Column="1"
       Text="" />
<Label Grid.Row="6"
       Text="Email Address" />
<Entry Grid.Row="6"
       Grid.Column="1"
       Keyboard="Email"
       Text="" />

The Entry Control

The Entry control is used to gather small, one-line pieces of data from a user, such as a login identifier, first name, last name, email address, etc. Some of the most common properties you might set on this control are Text, Keyboard, IsReadOnly, and IsPassword. The Text property is the value that is placed into the entry area on the control. The Keyboard property can be set to values such as Email, Numeric, Plain, Telephone, Text, or URL. These values don't do anything on Windows, but when they're set on a mobile device, they'll be used to display the corresponding keyboard pop-up to allow for easy entry by the user for the type of input you're expecting. The IsReadOnly is set to either a true or false value to not allow any entry of data into this control. If you want the user to enter a password, set the IsPassword property to true, and the values entered won't be displayed on the page.

Try It Out

Run the application and you should now see the additional label and entry controls displayed below the box view control.

Set the Alignment on the Labels

When you run the application, notice that the Labels appear aligned to the top of the Entry control. You might like these Labels to be more centered, or even toward the bottom of the Entry control. The VerticalOptions property on the Label can be set to a value of Start, Center, End, or Fill. Add VerticalOptions="Center" to each of the Label controls (Login ID, First Name, Last Name, and Email Address) you just added.

Try It Out

Run the application again and you should now see that each of the Label controls line up in the center of each of the Entry controls they're identifying.

Stack and Flex Layouts

Sometimes you don't need to place controls within a strict row/column structure, such as a Grid. In this case, you can use a Horizontal, Vertical, or a Flex layout in which controls are placed adjacent to one another defined by the type of stack they are placed within.

Stack Items Horizontally

To stack items horizontally in relation to one another, use a HorizontalStackLayout control. The HorizontalStackLayout control has a Spacing property of the type Double to separate each item within the stack a specified amount of device-independent pixels from one another. Looking at Figure 25, you can use the HorizontalStackLayout control to encapsulate the two button controls: Save and Cancel. Open the MainPage.xaml file and add one "Auto" height to the RowDefinitions property on the Grid. Add the following XAML immediately before the closing </Grid> tag.

<HorizontalStackLayout Grid.Row="7"
                       Grid.Column="1"
                       Spacing="5">
    <Button Text="Save" />
    <Button Text="Cancel" />
</HorizontalStackLayout>

Try It Out

Run the application and notice how the two buttons are placed next to one another in the last row and the second column.

Stack Items Vertically

The VerticalStackLayout control works similarly to the HorizontalStackLayout, but stacks items within it vertically. As an example of this, locate the Entry control just below the <Label Text="Login Id" /> and replace that Entry with the XAML in the next snippet. Don't remove the <Label Text="Login Id" />.

<VerticalStackLayout Grid.Row="3"
                     Grid.Column="1">
    <Entry Text="" />
    <Label FontSize="Micro"
           Text="Please use a combination of letters and numbers." />
</VerticalStackLayout>

Try It Out

Run the application and see how the Label is placed below the Entry control but still within the third row and the second column. Also notice that the third row is now a little larger than the other rows because it contains both an Entry and a Label within that second column.

Flex Layout

The FlexLayout control is a container that can arrange its children horizontally and vertically in a stack. FlexLayout is based on the flex box model in CSS. FlexLayout can wrap its children if there are too many to fit in a single row or column. FlexLayout can control orientation, alignment, and adapt to different screen sizes. Use FlexLayout when you have a set of controls in a container that you want to keep together but want to wrap if the container shrinks, like a set of buttons, check boxes, or radio buttons.

Let's add a set of check boxes to the page and wrap them within a FlexLayout. A CheckBox control doesn't have a Text property, so you need to use a Label control next to each CheckBox to provide textual data for the user to know what checking that check box means. You should wrap each Label and CheckBox combination within a HorizontalStackLayout control, and the set of all HorizontalStackLayout controls wrapped within a FlexLayout.

Open the MainPage.xaml file and add one more "Auto" to the Grid. Change the HorizontalStackLayout control that contains the Save and Cancel buttons to be in Grid.Row="8". Just before the last HorizontalStackLayout that contains the Save and Cancel buttons, add the XAML shown in Listing 5.

Listing 5: Use a FlexLayout to allow a set of controls to wrap if not enough space is available on a page.

<Label Grid.Row="7"
       Text="Is Enrolled?" />
<FlexLayout Grid.Row="7"
            Grid.Column="1"
            Wrap="Wrap"
            Direction="Row">
    <HorizontalStackLayout Spacing="5">
         <Label Text="401k?" />
         <CheckBox IsChecked="True"/>
    </HorizontalStackLayout>
    <HorizontalStackLayout Spacing="5">
         <Label Text="Flex Time?" />
         <CheckBox />
    </HorizontalStackLayout>
    <HorizontalStackLayout Spacing="5">
        <Label Text="Health Care?" />
        <CheckBox IsChecked="True" />
    </HorizontalStackLayout>
    <HorizontalStackLayout Spacing="5">
        <Label Text="Health Savings Account?" />
        <CheckBox IsChecked="True" />
    </HorizontalStackLayout>
</FlexLayout>

Set the Wrap property on the FlexLayout control to "Wrap" or the controls won't wrap to the next line. The Direction property has a default of "Row", so technically you didn't need to set it but, I like to add it for clarity. Each HorizontalStackLayout control is considered the child elements of the FlexLayout, thus each one of those are what is wrapped when you shrink the screen.

Try It Out

Run the application and shrink the screen to see the labels and their corresponding check boxes wrapped within the second column of the Grid control.

Nested Grids

One of the most powerful features of .NET MAUI is the ability to nest one control within one another. Nesting Grid controls can help you layout a side navigation area on the left of the screen, a status bar area at the bottom, and your input page within the rest of the page, as shown in Figure 27.

Figure 27: Use a nested grid to add controls around other controls on a page.
Figure 27: Use a nested grid to add controls around other controls on a page.

Open the MainPage.xaml file and just before the Grid control you already have there, add a starting <Grid> tag. Add a closing </Grid> tag below the existing </Grid> closing tag. Within the new Grid you just added, add a VerticalStackLayout control to display in the first row and column of this new Grid. Within this VerticalStackLayout add a set of Button controls. Add two attached properties to the original Grid control to set the Grid.Row="0" and the Grid.Column="1". Just below the original closing </Grid> tag and before the new closing </Grid> tag, add a Label control, as shown in Listing 6.

Listing 6: Use nested grid controls to create a navigation and status bar area.

<Grid RowDefinitions="*,Auto"
      ColumnDefinitions="Auto,*">
  
    <!-- Side Navigation Area -->
    <VerticalStackLayout Grid.Row="0"
                         Grid.Column="0"
                         Grid.RowSpan="2"
                         BackgroundColor="DarkGray"
                         Spacing="10"
                         Padding="10">
        <Button Text="Nav 1" />
        <Button Text="Nav 2" />
        <Button Text="Nav 3" />
        <Button Text="Nav 4" />
        <Button Text="Nav 5" />
        <Button Text="Nav 6" />
    </VerticalStackLayout>

    <!-- Page Area -->
    <Grid Grid.Row="0"
          Grid.Column="1"
          RowDefinitions="Auto,Auto,...,Auto"
          ColumnDefinitions="Auto,*"
          ColumnSpacing="10"
          RowSpacing="10"     
          Margin="10"
          Padding="10">

     // REST OF THE XAML HERE
 
    </Grid>
  
    <!-- Status Bar Area -->
    <Label Grid.Row="1"
           Grid.Column="0"
           Grid.ColumnSpan="2"
           BackgroundColor="LightGray"
           Padding="10"
           Text="This is an area for you to display status text" />
</Grid>

Try It Out

Run the application and your screen should like Figure 27.

Remove the Outer Grid

Creating this navigation and status bar area was for illustration purposes only. You aren't going to use the outer Grid control, the side navigation buttons, or the Label at the bottom, so go ahead and remove these items now.

Summary

.NET MAUI and XAML are great for creating cross-platform applications. In this article, you learned the basics of XAML, XML namespaces, attributes, and elements. After installing .NET MAUI from the Visual Studio Installer utility, you created your first .NET MAUI application and used label and entry controls to build a data-entry page. The Grid control is ideal for laying out how a page should look to the user. With the flexibility of Auto and Star (*) spacing, you can create pages that can easily respond to changes in screen size. Stack layouts help you build a set of controls that can be displayed either horizontally or vertically. In the next article in this series, you'll create styles to keep your look consistent, navigate from one page to another, create reusable UIs, and use more .NET MAUI controls to build even richer UIs for your users that can run on Windows, Android, or iOS operating systems.