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.
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.
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”.
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.
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
Click on the Individual components tab and ensure that you have the .NET 8 Runtime (Long Term Support) selected, as shown in Figure 6.
Locate the .NET MAUI SDK for Windows and ensure that it's selected, as shown in Figure 7.
If you wish to run the Android Emulator, select the Google Android Emulator (local install), as shown in Figure 8.
The last item to select is the Android SDK setup, as shown in Figure 9.
.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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.