CHAPTER 3
At its core, .NET MAUI is a managed layer that allows you to create native user interfaces from a single C# codebase by sharing code. This chapter provides the foundations for building the user interface in a .NET MAUI solution. Then, in the next three chapters, you will learn about layouts, controls, pages, and navigation in more detail. If you have previous experience with Xamarin.Forms, you will definitely be familiar with all the discussed concepts.
One of the benefits of .NET MAUI is that you can define the entire user interface of your application inside a single project. Native apps for iOS, Android, Mac, Tizen, and Windows that you get when you build a solution will render the user interface with the proper native layouts and controls on each platform. This is possible because MAUI maps native controls into C# classes that are then responsible for rendering the appropriate visual element, depending on the platform the app is running on. These classes represent visual elements such as pages, layouts, and views.
Because a project can only contain code that will certainly run on all platforms, .NET MAUI maps only those visual elements common to all platforms. For instance, iOS, Android, and Windows all provide text boxes and labels; thus, .NET MAUI can provide the Entry and Label controls that represent text boxes and labels, respectively. However, each platform renders and manages visual elements differently from one another, with different properties and behavior. This implies that controls in .NET MAUI expose only properties and events that are common to every platform, such as the font size and text color.
In Chapter 9, you will learn how to extend your app capabilities by using native controls, but for now, let’s focus on how .NET MAUI allows the creation of user interfaces with visual elements provided out of the box.
The user interface in iOS, Android, Tizen, Mac, and Windows has a hierarchical structure made of pages that contain layouts that contain controls. Layouts can be considered as containers of controls that allow for dynamically arranging the user interface in different ways. Based on this consideration, .NET MAUI provides numerous page types, layouts, and controls that can be rendered on each platform.
When you create a .NET MAUI solution, the project will contain a root page that you can populate with visual elements. Then you can design a more complex user interface by adding other pages and visual elements. To accomplish this, you can use both C# and Extensible Application Markup Language (XAML). Let’s discuss both methods.
In .NET MAUI, you can create the user interface of an application in C# code. For instance, Code Listing 2 demonstrates how to create a page with a layout that arranges controls in a stack containing a label and a button. For now, don’t worry about element names and their properties (they will be explained in the next chapter). Rather, focus on the hierarchy of visual elements that the code introduces.
var newPage = new ContentPage(); newPage.Title = "New page";
var newLayout = new VerticalStackLayout(); newLayout.Padding = new Thickness(10);
var newLabel = new Label(); newLabel.Text = "Welcome to .NET MAUI!";
var newButton = new Button(); newButton.Text = "Tap here"; newButton.Margin = new Thickness(0, 10, 0, 0);
newLayout.Children.Add(newLabel); newLayout.Children.Add(newButton);
newPage.Content = newLayout; |
Here you have full IntelliSense support. However, as you can imagine, creating a complex user interface entirely in C# can be challenging for at least the following reasons:
· Representing a visual hierarchy made of tons of elements in C# code is extremely difficult.
· You must write the code in a way that allows you to distinguish between user interface definition and other imperative code.
· As a consequence, your C# becomes much more complex and difficult to maintain.
You have a much more versatile way of designing the user interface with XAML, as you’ll learn in the next section. Obviously, there are still situations in which you might need to create visual elements in C#; for example, if you need to add new controls at runtime (although this is the only scenario for which I suggest you code visual elements in C#).
Note: It is also possible to use C# Markup, a new set of so-called fluent APIs that allow for writing the user interface in C# with a simplified approach. C# Markup will not be covered in this book because it is only available through the .NET MAUI Community Toolkit library. However, it is covered in the final chapter of my ebook .NET MAUI Community Toolkit Succinctly.
As its name implies, XAML is a markup language that you can use to write the user interface definition in a declarative fashion. XAML was first introduced more than 15 years ago with Windows Presentation Foundation, and it has always been available in platforms such as Silverlight, Windows Phone, the Universal Windows Platform, and Xamarin.Forms.
XAML derives from XML and, among others, it offers the following benefits:
· XAML makes it easy to represent structures of elements in a hierarchical way, where pages, layouts, and controls are represented with XML elements and properties with XML attributes.
· It provides clean separation between the user interface definition and the C# logic.
· Being a declarative language separated from the logic, it allows professional designers to work on the user interface without interfering with the imperative code.
The way you define the user interface with XAML is unified across platforms, meaning that you design the user interface once and it will run on iOS, Android, Tizen, Mac Catalyst, and Windows.
Note: XAML in .NET MAUI adheres to Microsoft’s XAML 2009 specifications, but its vocabulary is different from XAML in other platforms, such as WPF or UWP. So, if you have experience with these platforms, you will notice many differences in how visual elements and their properties are named. Also, remember that XAML is case-sensitive for object names and their properties and members.
For example, when you create a .NET MAUI solution, you will find a file called MainPage.xaml, whose markup is represented in Code Listing 3.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiApp2.MainPage">
<ScrollView> <VerticalStackLayout Spacing="25" Padding="30,0" VerticalOptions="Center"> <Image Source="dotnet_bot.png" SemanticProperties.Description="Cute dot net bot waving hi to you!" HeightRequest="200" HorizontalOptions="Center" />
<Label Text="Hello, World!" SemanticProperties.HeadingLevel="Level1" FontSize="32" HorizontalOptions="Center" />
<Label Text="Welcome to .NET Multi-platform App UI" SemanticProperties.HeadingLevel="Level2" SemanticProperties.Description="Welcome to dot net Multi platform App U I" FontSize="18" HorizontalOptions="Center" /> <Button x:Name="CounterBtn" Text="Click me" SemanticProperties.Hint="Counts the number of times you click" Clicked="OnCounterClicked" HorizontalOptions="Center" /> </VerticalStackLayout> </ScrollView> </ContentPage> |
A XAML file in .NET MAUI normally contains a page or a custom view. The root element in Code Listing 2 is a ContentPage object, which represents its C# class counterpart and is rendered as an individual page. In XAML, the Content property of a page is implicit, meaning you do not need to write a ContentPage.Content element. The compiler assumes that the visual elements you enclose between the ContentPage tags are assigned to the ContentPage.Content property. The ScrollView allows for scrolling visual elements that require more space than the available screen size. The VerticalStackLayout is a container of views. The Image shows the specified image.
The Label object allows for displaying text. Properties of this class are assigned with XML attributes, such as Text, VerticalOptions, and HorizontalOptions. Properties can be assigned with complex types, which must be done in a hierarchical way, not inline.
You probably already have the immediate perception of better organization and visual representation of the structure of the user interface. If you look at the root element, you can see several attributes whose definitions start with xmlns. These are referred to as XML namespaces and are important because they make it possible to declare visual elements defined inside specific namespaces or XML schemas.
For example, xmlns points to the root XAML namespace defined inside a specific XML schema and allows for adding all the visual elements defined by .NET MAUI to the UI definition; xmlns:x points to an XML schema that exposes built-in types.
Each page or layout can only contain one visual element. In the case of the autogenerated MainPage.xaml page, you cannot add other visual elements to the page unless you organize them into a layout. This is the reason why, in Code Listing 3, you see several visual elements inside a VerticalStackLayout.
Let’s simplify the autogenerated code to have some text and a button. This requires us to have a Button below a Label, and both must be wrapped inside a container such as the VerticalStackLayout, as demonstrated in Code Listing 4.
Tip: IntelliSense will help you add visual elements faster by showing element names and properties as you type. You can then simply press Tab or double-click to quickly insert an element.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App1.MainPage">
<VerticalStackLayout Padding="10"> <Label Text="Welcome to .NET MAUI!" VerticalOptions="Center" HorizontalOptions="Center" />
<Button x:Name="Button1" Text="Tap here!" Margin="0,10,0,0"/> </VerticalStackLayout>
</ContentPage> |
If you did not include both controls inside the layout, Visual Studio will raise an error. You can nest other layouts inside a parent layout and create complex hierarchies of visual elements. Notice the x:Name assignment for the Button. Generally speaking, with x:Name you can assign an identifier to any visual element so that you can interact with it in C# code; for example, if you need to retrieve a property value.
If you have never seen XAML, you might wonder how you can interact with visual elements in C# at this point. In Solution Explorer, if you expand the MainPage.xaml file, you will see a nested file called MainPage.xaml.cs. This is the so-called code-behind file and it contains all the imperative code for the current page.
In this case, the simplest form of a code-behind file, the code contains the definition of the MainPage class, which inherits from ContentPage, and the page constructor, which makes an invocation to the InitializeComponent method of the base class and initializes the page.
You will access the code-behind file often from Solution Explorer, but Visual Studio offers another easy way that is related to a very common requirement: responding to events raised by the user interface.
If you look at Code Listing 3, you can see that an object called SemanticProperties is automatically added by Visual Studio to some views. This object provides support for accessibility, especially for users with low vision.
With the Heading property, you can specify the hierarchical level of the view; with the Description property, you can specify a detailed description of the view; with the Hint property, you can provide a quick suggestion. If the user has enabled the system accessibility options, the device will be able to read the contents you added.
Especially if you have experience with Xamarin.Forms, you will notice that the main page is not the startup object of a new .NET MAUI project. In fact, the startup object is the AppShell object, a managed representation of the Shell. The Shell is an infrastructure that offers common features to most applications, such as navigation, a flyout menu, a search bar, and more. The Shell is defined inside the AppShell.xaml file, and the basic implementation for new .NET MAUI projects looks like the following:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MauiApp1.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiApp2"
Shell.FlyoutBehavior="Disabled">
<ShellContent
Title="Home"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />
</Shell>
What you need to know for now is that the Shell definition points to the MainPage object via another object called ShellContent, with a special syntax defined in the ContentTemplate property. Do not worry at all if this all sounds strange now, because in Chapter 6 you will learn everything about pages and the Shell.
Exactly like in Xamarin.Forms, the startup object is assigned to the MainPage property of the App class, inside the App.xaml.cs file. The following code demonstrates how this happens in a newly created .NET MAUI project:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
}
Everything will be clarified in the coming paragraphs and chapters.
The XAML code editor in Visual Studio 2022 is powered by the same engine that is behind Windows Presentation Foundation (WPF) and Universal Windows Platform (UWP). This is extremely important for several reasons:
· Full, rich IntelliSense support: You have linting and fuzzy matching, described in more detail shortly.
· Quick actions and refactorings: Light bulb suggestions are available to XAML in .NET MAUI, just as they have been for WPF, UWP, and Xamarin.Forms, making it easy to resolve XML namespaces, remove unused namespaces, and organize code with contextualized suggestions.
· Go To Definition and Peek Definition: These popular features from C# are also available to .NET MAUI’s XAML.
· Enhanced support for binding expressions: IntelliSense lists available objects for bindings based on the {Binding} markup extension. It also lists available resources when using the {StaticResource} markup extension. Tips about this feature will be provided in Chapter 7.
I will now describe these productivity features in more detail.
Fuzzy matching is a feature that helps you find appropriate completions based on what you type. For example, if you type Stk and then press Tab, IntelliSense will add a StackLayout tag.
This feature is also capable of providing a list of possible completions as you type a control name. For example, if you type Layout, IntelliSense will offer VerticalStackLayout, HorizontalStackLayout, StackLayout, FlexLayout, AbsoluteLayout, and RelativeLayout as possible completions, as well as closing tags based on the same typing.
Another interesting feature of fuzzy matching is CamelCase matching, which provides shortcuts based on CamelCase types. For instance, if you type SL and then press Tab, the editor inserts a StackLayout tag. With linting, the code editor underlines code issues as you type with red squiggles (critical errors) or green squiggles (warnings).
With this tool, when a code issue is detected, you can click the light bulb icon (or press Ctrl+.) and IntelliSense will show potential fixes for that code issue. In .NET MAUI’s XAML, the light bulb can suggest code fixes to import missing XML namespaces, sort XML namespaces, and remove unused XML namespaces.
Go To Definition and Peek Definition are popular and extremely useful features in the code editor, and they are also available in the XAML IntelliSense in .NET MAUI. Both are available through the context menu when you right-click an object name in the code editor.
With Go To Definition, Visual Studio will open the definition of the selected object, and if it is defined in a different file, the file will be opened in a separate tab and the cursor will be moved to the object definition. In XAML, this is particularly useful when you need to go to the definition of objects such as styles, templates, and other resources that might be defined in a different file.
Peek Definition, instead, opens an interactive pop-up window in the active editor, allowing you to see the definition of an object or make edits without leaving the active window. Additionally, you are not limited to viewing or editing objects defined in a XAML file; you can also peek the definition of an object defined in the C# code-behind file.
Figure 19 shows an example of Peek Definition where a C# event handler for the Clicked event of a Button is displayed within a XAML editor window.

Figure 19: Making edits in the active editor with Peek Definition
Events are fundamental to interactions between the user and the application. Controls in .NET MAUI raise events as they normally do in any platform. Events are handled in the C# code-behind file. Visual Studio 2022 makes it simple to create event handlers with an evolved IntelliSense experience. For instance, suppose you want to perform an action when the user taps the button defined in the previous code.
The Button control exposes an event called Clicked that you assign the name of an event handler as follows:
<Button x:Name="Button1" Text="Tap here!" Margin="0,10,0,0"
Clicked="Button1_Clicked"/>
However, when you type Clicked=", Visual Studio offers a shortcut that allows the generation of an event handler in C# based on the control’s name, as shown in Figure 20.

Figure 20: Generating an Event Handler
If you press Tab, Visual Studio will insert the name of the new event handler and generate the C# event handler in the code-behind. You can quickly go to the event handler by right-clicking its name and then selecting Go To Definition. You will be redirected to the event handler definition in the C# code-behind as shown in Figure 21.

Figure 21: The event handler definition in C#
At this point, you will be able to write the code that performs the action you want to execute, exactly as it happens with other .NET platforms such as Xamarin.Forms, WPF, or UWP. Event handlers’ signatures require two parameters: one of type object representing the control that raised the event, and one object of type EventArgs, containing information about the event.
In many cases, event handlers work with derived versions of EventArgs, but these will be highlighted when appropriate. As you can imagine, .NET MAUI exposes events that are commonly available on all the supported platforms.
If you look at Code Listing 2, you will see that the Padding property of the VerticalStackLayout is of type Thickness, and the Margin property assigned to the Button is also of type Thickness. However, as you can see in Code Listing 4, the same properties are assigned with values passed in the form of strings in XAML.
.NET MAUI (and all the other XAML-based platforms) implement the so-called type converters, which automatically convert a string into the appropriate value for many known types. Summarizing all the available type converters and known target types is neither possible nor necessary at this point; you simply need to remember that, in most cases, strings you assign as property values are automatically converted into the appropriate type on your behalf.
In the past, if you needed to make one or more changes to the XAML code, you had to stop debugging, make your changes, and then restart the application. This was one of the biggest limitations, especially with the Xamarin.Forms development experience.
In Visual Studio 2022, this problem has been solved by a feature called Hot Reload. The way it works is very simple: with the application running in debugging mode, you can make and save changes to your XAML, and the application will redraw the user interface based on your changes.
Hot Reload is available to all the supported development platforms and must be enabled by accessing Tools > Options > Debugging > XAML Hot Reload. Here you will find options to enable the feature for different platforms. Make sure that at least the Android and iOS (.NET MAUI) option is selected (see Figure 22).

Figure 22: Enabling XAML Hot Reload
In the same dialog, you will find additional settings that are not covered here.
Sharing the user interface across platforms is the main goal of .NET MAUI, and this chapter provided a high-level overview of how you define the user interface with XAML, based on a hierarchy of visual elements. You have seen how to add visual elements and how to assign their properties; you have seen how type converters allow for passing string values in XAML, and how the compiler converts them into the appropriate types.
Following this overview, it is time to discuss important UI concepts in more detail. We will start by organizing the user interface with layouts.