Multi-platform App User Interface - a cross-platform framework for Windows, Android, iOS, macOS (for iOS and macOS requires Mac) for creating native mobile and desktop apps using C# and XAML(eXtensible Application Markup Language) evolved from Xamarin.Forms.
To enable you to implement as much of your app logic and UI layout in a single code-base.
.NET MAUI unifies Android, iOS, macOS, and Windows APIs into a single API that allows a write-once run-anywhere developer experience, while additionally providing deep access to every aspect of each native platform.
.NET 6 or greater provides a series of platform-specific frameworks for creating apps: .NET for Android, .NET for iOS, .NET for macOS, and Windows UI 3 (WinUI 3) library. These frameworks all have access to the same .NET Base Class Library (BCL). This library 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.
While the BCL enables apps running on different platforms to share common business logic, the various platforms have different ways of defining the user interface for an app, and they provide varying models for specifying how the elements of a user interface communicate and interoperate.
.NET MAUI provides a single framework for building the UIs for mobile and desktop apps. The following diagram shows a high-level view of the architecture of a .NET MAUI app:
In a .NET MAUI app, you write code that primarily interacts with the .NET MAUI API. .NET MAUI then directly consumes the native platform APIs. In addition, app code may directly exercise platform APIs, if required.
.NET MAUI provides a collection of controls that can be used to display data, initiate actions, indicate activity, display collections, pick data, and more. In addition to a collection of controls, .NET MAUI also provides:
- An elaborate layout engine for designing pages.
- Multiple page types for creating rich navigation types, like drawers.
- Support for data-binding, for more elegant and maintainable development patterns.
- The ability to customize handlers to enhance the way in which UI elements are presented.
- Cross-platform APIs for accessing native device features. These APIs enable apps to access device features such as the GPS, the accelerometer, and battery and network states. For more information, see Cross-platform APIs for device features.
- Cross-platform graphics functionality, that provides a drawing canvas that supports drawing and painting shapes and images, compositing operations, and graphical object transforms.
- A single project system that uses multi-targeting to target Android, iOS, macOS, and Windows. For more information, see .NET MAUI Single project.
- .NET hot reload, so that you can modify both your XAML and your managed source code while the app is running, then observe the result of your modifications without rebuilding the app. For more information, see .NET hot reload.
.NET MAUI provides cross-platform APIs for native device features. Examples of functionality provided by .NET MAUI for accessing device features includes:
- Access to sensors, such as the accelerometer, compass, and gyroscope on devices.
- Ability to check the device's network connectivity state, and detect changes.
- Provide information about the device the app is running on.
- Copy and paste text to the system clipboard, between apps.
- Pick single or multiple files from the device.
- Store data securely as key/value pairs.
- Utilize built-in text-to-speech engines to read text from the device.
- Initiate browser-based authentication flows that listen for a callback to a specific app registered URL.
.NET MAUI single project takes the platform-specific development experiences you typically encounter while developing apps and abstracts them into a single shared project that can target Android, iOS, macOS, and Windows.
.NET MAUI single project provides a simplified and consistent cross-platform development experience, regardless of the platforms being targeted. .NET MAUI single project provides the following features:
- A single shared project that can target Android, iOS, macOS, and Windows.
- A simplified debug target selection for running your .NET MAUI apps.
- Shared resource files within the single project.
- A single app manifest that specifies the app title, id, and version.
- Access to platform-specific APIs and tools when required.
- A single cross-platform app entry point.
.NET MAUI single project is enabled using multi-targeting and the use of SDK-style projects. For more information about .NET MAUI single project, see .NET MAUI single project.
- Platforms - platform-native initialization code calls
CreateMauiApp()
inMauiProgram.cs
. - MauiProgram.cs - application entry point, called by the platform's native code, register services in the DI container.
- Resources - contains fonts, images and assets used by all platforms.
- App.xaml - defines the resources that the app is going to use.
- App.xaml.cs - code behind
App.xaml
, defines the App class representing the app at runtime, creates an initial window and assigns it to the MainPage property. - AppShell.xaml - Defines the main structure of the app. The MAUI Shell provides features like: app styling, url navigation and layout options.
- AppShell.xaml.cs - code behind
AppShell.xaml
, define routes. - MainPage.xaml - default main page, XAML based layout definition.
- MainPage.xaml.cs - code behind
MainPage.xaml
contains UI event handlers
The UI is typically defined as a combination of XAML and C#. XAML is used more for rendering visuals (XAML is markup language). C# is usually used for the code behind, e.g., event handling code.
<Button Text="Save" Grid.Row="1" Grid.Column="0" Clicked="OnSaveButtonClicked" Margin="20,0" />
<Button Text="Delete" Grid.Row="1" Grid.Column="1" Clicked="OnDeleteButtonClicked" />
<Button Text="Cancel" Grid.Row="1" Grid.Column="2" Clicked="OnCancelButtonClicked" Margin="20,0" />
async void OnDeleteButtonClicked(object sender, EventArgs e)
{
await _dataService.DeleteToDoAsync(ToDo.Id);
await Shell.Current.GoToAsync("..");
}
The .NET Multi-platform App UI (.NET MAUI) ContentPage displays a single view, which is often a layout such as as Grid or StackLayout, and is the most common page type.
ContentPage defines a Content
property, of type View, which defines the view that represents the page's content. This property is backed by a BindableProperty object, which means that it can be the target of data bindings, and styled. In addition, ContentPage inherits Title
, IconImageSource
, BackgroundImageSource
, IsBusy
, and Padding
bindable properties from the Page class.
Note
The
Content
property is the content property of the ContentPage class, and therefore does not need to be explicitly set from XAML.
.NET MAUI apps typically contain multiple pages that derive from ContentPage, and navigation between these pages can be performed.
The child of a ContentPage is typically a layout, such as Grid or StackLayout, with the layout typically containing multiple views. However, the child of the ContentPage can be a view that displays a collection, such as CollectionView.
Note
The value of the
Title
property will be shown on the navigation bar, when the app performs navigation using a NavigationPage.
What is data binding? Data binding is when we take the value of a property in our C# class and make it the value that drives a property in our MAUI controls.
If we look at the original XAML, the code for the button is pretty basic.
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
In the original version, the click handler updates the button text directly.
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
SemanticScreenReader.Announce(CounterBtn.Text);
}
Now we are going to change this to use data binding instead. First, we will set the binding context to the current UI control.
public MainPage()
{
this.InitializeComponent();
this.BindingContext = this; // Set the binding context to the current UI control.
}
Create a new Count
property to handle the value. We will call NotifyPropertyChanged
when we update this property, telling the UI that the property has been updated.
private int count;
public int Count
{
get => this.count;
set
{
this.count = value;
// Telling the UI that the property has been updated.
this.OnPropertyChanged();
this.OnPropertyChanged(nameof(this.ButtonText));
}
}
We will create a separate property for the actual button text.
public string ButtonText => this.Count switch
{
0 => "Click Here",
1 => "Clicked 1 time",
_ => $"Clicked {this.Count} times"
};
Now we will update the click handler to update the Count property.
private void OnCounterClicked(object sender, EventArgs e) => this.Count++;
Last, we must update the button control. We must bind the button’s text to the newly created ButtonText property.
<Button
x:Name="CounterBtn"
Text="{Binding ButtonText}"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
Set up MVVM Architecture with the following features:
- Have Dependency Injection (DI) working
- Ability to bind ViewModels to Views
- Simple navigation from the ViewModels (only ‘linear’ navigation, forward, back, no tabs or drawer navigation)
- Ability to pass parameters when navigating
- Trigger events or call functions when navigating