0% found this document useful (0 votes)
11K views1,940 pages

Dotnet Maui Net Maui 7.0

.NET MAUI is a cross-platform framework that allows developers to create native mobile and desktop apps using C# and XAML. It supports building apps that can run on Android, iOS, macOS, and Windows from a single shared codebase. .NET MAUI provides controls, layouts, data binding, and cross-platform APIs to access device features. It uses a single project system with multi-targeting to build apps for the supported platforms.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11K views1,940 pages

Dotnet Maui Net Maui 7.0

.NET MAUI is a cross-platform framework that allows developers to create native mobile and desktop apps using C# and XAML. It supports building apps that can run on Android, iOS, macOS, and Windows from a single shared codebase. .NET MAUI provides controls, layouts, data binding, and cross-platform APIs to access device features. It uses a single project system with multi-targeting to build apps for the supported platforms.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1940

Tell us about your PDF experience.

.NET Multi-platform App UI


documentation
.NET Multi-platform App UI (.NET MAUI) lets you build native apps using a .NET cross-
platform UI toolkit that targets the mobile and desktop form factors on Android, iOS,
macOS, Windows, and Tizen.

Overview

e OVERVIEW

What is .NET Multi-platform App UI?

Supported platforms

Troubleshooting

h WHAT'S NEW

What's new in .NET MAUI docs?

Get started

c HOW-TO GUIDE

Installation

Build your first app

Debug on an Android emulator

Setup an Android device for debugging

iOS device provisioning

Build an iOS app with .NET CLI

Build a Mac Catalyst app with .NET CLI

Pair to Mac for iOS development

Remote iOS Simulator for Windows

Setup Windows for debugging

b GET STARTED
b GET STARTED

Learning resources

Migrate from Xamarin

p CONCEPT

Upgrade Xamarin native projects

Xamarin.Android project migration

Xamarin Apple project migration

Automatically upgrade Xamarin.Forms projects

Manually upgrade Xamarin.Forms projects

Layout behavior changes

Custom renderers

Effects

XAML

p CONCEPT

Overview

Fundamentals

Compilation

Field modifiers

Generics

Markup extensions

Namespaces

Pass arguments

XAML Hot Reload

Fundamentals


p CONCEPT

Accessibility

App lifecycle

Behaviors

Data binding

Resource dictionaries

Shell

Single project

Triggers

User interface

p CONCEPT

Animation

Brushes

Controls

Graphics

Handlers

Layouts

Pages

Styles

Theming

Platform integration

p CONCEPT

App information

Clipboard

Device information

Device sensors
File picker

Network connectivity

Permissions

Photo picker

Preferences

Web authenticator

Data & cloud services

p CONCEPT

Azure mobile apps

Consume a REST-based web service

Connect to local web services

Local databases

Deployment

p CONCEPT

Hot restart

Project configuration

Publish Android apps

Publish iOS apps

Publish Mac Catalyst apps

Publish Windows apps


What is .NET MAUI?
Article • 01/30/2023 • 5 minutes to read

.NET Multi-platform App UI (.NET MAUI) is a cross-platform framework for creating


native mobile and desktop apps with C# and XAML.

Using .NET MAUI, you can develop apps that can run on Android, iOS, macOS, and
Windows from a single shared code-base.

.NET MAUI is open-source and is the evolution of Xamarin.Forms, extended from mobile
to desktop scenarios, with UI controls rebuilt from the ground up for performance and
extensibility. If you've previously used Xamarin.Forms to build cross-platform user
interfaces, you'll notice many similarities with .NET MAUI. However, there are also some
differences. Using .NET MAUI, you can create multi-platform apps using a single project,
but you can add platform-specific source code and resources if necessary. One of the
key aims of .NET MAUI is to enable you to implement as much of your app logic and UI
layout as possible in a single code-base.

Who .NET MAUI is for


.NET MAUI is for developers who want to:

Write cross-platform apps in XAML and C#, from a single shared code-base in
Visual Studio.
Share UI layout and design across platforms.
Share code, tests, and business logic across platforms.

How .NET MAUI works


.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. For Android,
iOS, and macOS, the environment is implemented by Mono, an implementation of the
.NET runtime. On Windows, .NET CoreCLR provides the execution environment.

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. You can craft the UI for each platform separately using
the appropriate platform-specific framework (.NET for Android, .NET for iOS, .NET for
macOS, or WinUI 3), but this approach then requires you to maintain a code-base for
each individual family of devices.

.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 (1).
.NET MAUI then directly consumes the native platform APIs (3). In addition, app code
may directly exercise platform APIs (2), if required.

.NET MAUI apps can be written on PC or Mac, and compile into native app packages:

Android apps built using .NET MAUI compile from C# into intermediate language
(IL) which is then just-in-time (JIT) compiled to a native assembly when the app
launches.
iOS apps built using .NET MAUI are fully ahead-of-time (AOT) compiled from C#
into native ARM assembly code.
macOS apps built using .NET MAUI use Mac Catalyst, a solution from Apple that
brings your iOS app built with UIKit to the desktop, and augments it with
additional AppKit and platform APIs as required.
Windows apps built using .NET MAUI use Windows UI 3 (WinUI 3) library to create
native apps that target the Windows desktop. For more information about WinUI
3, see Windows UI Library.

7 Note

Building apps for iOS and macOS requires a Mac.

What .NET MAUI provides


.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.

Cross-platform APIs for device features


.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.

Single project
.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.

Hot reload
.NET MAUI includes support for .NET hot reload, which enables you to modify your
managed source code while the app is running, without the need to manually pause or
hit a breakpoint. Then, your code edits can be applied to your running app without
recompilation.

.NET MAUI also includes support for XAML hot reload, which enables you to save your
XAML files and see the changes reflected in your running app without recompilation. In
addition, your navigation state and data will be maintained, enabling you to quickly
iterate on your UI without losing your place in the app.
Supported platforms for .NET MAUI
apps
Article • 10/24/2022 • 2 minutes to read

.NET Multi-platform App UI (.NET MAUI) apps can be written for the following platforms:

Android 5.0 (API 21) or higher.


iOS 11 or higher, using the latest release of Xcode.
macOS 10.15 or higher, using Mac Catalyst.
Windows 11 and Windows 10 version 1809 or higher, using Windows UI Library
(WinUI) 3.

.NET MAUI Blazor apps have the following additional platform requirements:

Android 7.0 (API 24) or higher is required


iOS 14 or higher is required.
macOS 11 or higher, using Mac Catalyst.

.NET MAUI Blazor apps also require an updated platform specific WebView control. For
more information, see Blazor supported platforms.

.NET MAUI apps for Android, iOS, and Windows can be built in Visual Studio. However, a
networked Mac is required for iOS development.

Additional platform support


.NET MAUI also includes Tizen support, which is provided by Samsung.
Installation
Article • 12/23/2022 • 2 minutes to read

Developing native, cross-platform .NET Multi-platform App UI (.NET MAUI) apps


requires Visual Studio 2022 17.3 or greater, or Visual Studio 2022 for Mac 17.4 or
greater.

Visual Studio

To start developing native, cross-platform .NET MAUI apps on Windows, install


Visual Studio 2022 17.3 or greater by following the installation steps.

Prerequisites
Visual Studio 2022 17.3 or greater. For information about supported operating
systems, hardware, supported languages, and additional requirements and
guidance, see Visual Studio 2022 System Requirements.

To build, sign, and deploy .NET MAUI apps for iOS, you'll also need:

A Mac that is compatible with the latest version of Xcode. For more
information, see Apple's minimum requirements documentation
The latest version of Xcode .
An Apple ID and paid Apple Developer Program enrollment. An Apple ID
is required to deploy apps to devices, and to submit apps to the Apple Store.

Alternatively, to deploy debug builds of your app directly from Windows to your
iOS device with hot restart, you'll need:

An Apple Developer account and paid Apple Developer Program


enrollment.

Installation
1. To create .NET MAUI apps, you'll need the latest version of Visual Studio 2022:

Download Visual Studio 2022 Community

Download Visual Studio 2022 Professional

Download Visual Studio 2022 Enterprise


2. Either install Visual Studio, or modify your existing installation, and install the
.NET Multi-platform App UI development workload with its default optional
installation options:

Next steps
To learn how to create and run your first .NET MAUI app in Visual Studio 2022 on
Windows, or Visual Studio 2022 for Mac, click the button below.

Build your first app


Build your first app
Article • 04/11/2023

In this tutorial, you'll learn how to create and run your first .NET Multi-platform App UI
(.NET MAUI) app in Visual Studio 2022 on Windows or Visual Studio 2022 for Mac. This
will help to ensure that your development environment is correctly set up.

Visual Studio

Prerequisites
Visual Studio 2022 17.3 or greater, with the .NET Multi-platform App UI
workload installed. For more information, see Installation.

Create an app
In this tutorial, you'll create your first .NET MAUI app in Visual Studio 2022 and run
it on Windows:

1. Launch Visual Studio 2022. In the start window, click Create a new project to
create a new project:
2. In the Create a new project window, select MAUI in the All project types
drop-down, select the .NET MAUI App template, and click the Next button:

3. In the Configure your new project window, name your project, choose a
suitable location for it, and click the Next button:

4. In the Additional information window, choose the version of .NET that you'd
like to target and click the Create button:
5. Wait for the project to be created and its dependencies to be restored:

6. In the Visual Studio toolbar, use the Debug Target drop-down to select
Framework and then the net6.0-windows entry:
7. In the Visual Studio toolbar, press the Windows Machine button to build and
run the app:

If you've not enabled Developer Mode, Visual Studio will prompt you to
enable it. In the Enable Developer Mode for Windows dialog, click settings
for developers to open the Settings app:

In the Settings app, turn on Developer Mode and accept the disclaimer:

Close the Settings app and then close the Enable Developer Mode for
Windows dialog.

8. In the running app, press the Click me button several times and observe that
the count of the number of button clicks is incremented:
Troubleshooting
If your app fails to compile, review Troubleshooting known issues, which may have a
solution to your problem.

Next steps
In this tutorial, you've learned how to create and run your first .NET Multi-platform App
UI (.NET MAUI) app.
To learn the fundamentals of building an app with .NET MAUI, see Create a .NET MAUI
app. Alternatively, for a full .NET MAUI training course, see Build mobile and desktop
apps with .NET MAUI.
Resources for learning .NET MAUI
Article • 09/21/2022 • 2 minutes to read

There are many different resources available for you to use to learn .NET Multi-platform
App UI (.NET MAUI). There are Microsoft Learn modules, workshops, videos, and
podcasts. Each varies in its depth and the topics it covers.

Build mobile and desktop apps with .NET MAUI

Learn how to use .NET MAUI to build apps that run on mobile devices and the
desktop using C# and Visual Studio. You'll learn the fundamentals of building an
app with .NET MAUI and more advanced topics such as local data storage and
invoking REST-based web services.

.NET MAUI for beginners

Follow a short video series that teaches you how to get started with .NET MAUI
and Visual Studio, to build your very first cross-platform desktop and mobile app.

.NET MAUI workshop

Learn how to build a .NET MAUI app that displays a list of monkeys from around
the world. You'll start by building the business logic backend that retrieves JSON-
encoded data from a REST-based endpoint. You then use .NET MAUI to find the
closest monkey to you, and show the monkey on a map. You'll also examine how
to display data using different approaches, and then finally fully theme the app.

Enterprise application patterns using .NET MAUI

This book provides real world solutions for addressing challenges faced when
building an enterprise app using .NET MAUI. The book covers topics such as:
Model-View-ViewModel (MVVM) pattern
Dependency injection
Navigation
Configuration
Loose-coupling of components
Additional enterprise concerns

The content of this book is helpful for anyone looking to build a new app or
looking to solve the problems of apps that evolve over time.

.NET MAUI samples

Download and explore the code of different example .NET MAUI apps.
.NET MAUI podcast

Keep up with the latest news in the world of mobile and desktop development
with the official .NET MAUI podcast.
How to enable hardware acceleration
with Android emulators (Hyper-V &
HAXM)
Article • 06/24/2022 • 4 minutes to read

This article explains how to use your computer's hardware acceleration features to
maximize Android emulator performance.

With Visual Studio, you can easily test and debug your .NET MAUI app for Android in
situations where an Android device isn't available. However, if hardware acceleration
isn't available or enabled, the emulator will run too slow. You can drastically improve the
performance of the emulator by enabling hardware acceleration and using x86-64 or
x86 virtual device images.

Scenario HAXM WHPX Hypervisor.Framework

You have an Intel Processor X X X

You have an AMD Processor X

You want to support Hyper-V X

You want to support nested Virtualization Limited

You want to use technologies like Docker (with WSL2) X X

Accelerating Android emulators on Windows


The following virtualization technologies are available for accelerating the Android
emulator:

1. Microsoft's Hyper-V and the Windows Hypervisor Platform (WHPX).

Hyper-V is a virtualization feature of Windows that makes it possible to run


virtualized computer systems on a physical host computer.

2. Intel's Hardware Accelerated Execution Manager (HAXM).

HAXM is a virtualization engine for computers running Intel CPUs.

For the best experience on Windows, it's recommended you use WHPX to accelerate the
Android emulator. If WHPX isn't available on your computer, then HAXM can be used.
The Android emulator automatically uses hardware acceleration if the following criteria
are met:

Hardware acceleration is available and enabled on your development computer.

The emulator is running a system image created for an x86-64 or x86-based virtual
device.

) Important

You can't run a VM-accelerated emulator inside another VM, such as a VM hosted
by VirtualBox, VMware, or Docker (unless using WSL2). You must run the Android
emulator directly on your system hardware .

For information about launching and debugging with the Android emulator, see
Debugging on the Android Emulator.

Accelerating with Hyper-V


Before enabling Hyper-V, read the following section to verify that your computer
supports Hyper-V.

Verifying support for Hyper-V


Hyper-V runs on the Windows Hypervisor Platform. To use the Android emulator with
Hyper-V, your computer must meet the following criteria to support the Windows
Hypervisor Platform:

Your computer hardware must meet the following requirements:


A 64-bit Intel or AMD Ryzen CPU with Second Level Address Translation (SLAT).
CPU support for VM Monitor Mode Extension (VT-c on Intel CPUs).
Minimum of 4-GB memory.

In your computer's BIOS, the following items must be enabled:


Virtualization Technology (may have a different label depending on
motherboard manufacturer).
Hardware Enforced Data Execution Prevention.

Your computer must be running Windows 11 or Windows 10 Version 1909 or later.

To verify that your computer hardware and software is compatible with Hyper-V, open a
command prompt and type the following command:
cmd

systeminfo

If all listed Hyper-V requirements have a value of Yes, then your computer can support
Hyper-V. For example:

If the Hyper-V result indicates that a hypervisor is currently running, Hyper-V is already
enabled.

Enabling Hyper-V acceleration in Windows and the


emulator
If your computer meets the above criteria, use the following steps to accelerate the
Android emulator with Hyper-V:

1. Enter windows features in the Windows search box and select Turn Windows
features on or off in the search results. In the Windows Features dialog, enable
both Hyper-V and Windows Hypervisor Platform:
After making these changes, reboot your computer.

) Important

On Windows 10 October 2018 Update (RS5) and higher, you only need to enable
Hyper-V, as it will use Windows Hypervisor Platform (WHPX) automatically.

1. Make sure that the virtual device you created in the Android Device Manager is an
x86-64 or x86-based system image. If you use an Arm-based system image, the
virtual device won't be accelerated and will run slowly.

After Hyper-V is enabled, you'll be able to run your accelerated Android emulator.

Accelerating with HAXM

) Important

HAXM is only supported on Intel CPUs.

If your computer doesn't support Hyper-V, you may use HAXM to accelerate the
Android emulator. To use HAXM, disable Device Guard.
Verifying HAXM support
To determine if your hardware supports HAXM, follow the steps in Does My Processor
Support Intel Virtualization Technology? . If your hardware supports HAXM, you can
check to see if HAXM is already installed by using the following steps:

1. Open a command prompt window and enter the following command:

cmd

sc query intelhaxm

2. Examine the output to see if the HAXM process is running. If it is, you should see
output listing the intelhaxm state as RUNNING . For example:

If STATE isn't set to RUNNING , then HAXM isn't installed.

If your computer can support HAXM but HAXM isn't installed, use the steps in the next
section to install HAXM.

Installing HAXM
HAXM install packages for Windows are available from the Intel Hardware Accelerated
Execution Manager GitHub releases page. Use the following steps to download and
install HAXM:

1. From the Intel website, download the latest HAXM virtualization engine installer
for Windows. The advantage of downloading the HAXM installer directly from the
Intel website is that you can be assured of using the latest version.

2. Run intelhaxm-android.exe to start the HAXM installer. Accept the default values
in the installer dialogs.
When you create a virtual device, be sure to select an x86_64 or x86-based system
image. If you use an Arm-based system image, the virtual device will not be accelerated
and will run slowly.

Troubleshooting
For help with troubleshooting hardware acceleration issues, see the Android emulator
Troubleshooting guide.

Related Links
Run Apps on the Android Emulator
Managing virtual devices with the
Android Device Manager
Article • 12/12/2022 • 6 minutes to read

This article explains how to use the Android Device Manager to create and configure
Android Virtual Devices (AVDs) that emulate physical Android devices. You can use these
virtual devices to run and test your app without having to rely on a physical device.

) Important

Enable hardware acceleration for the Android devices. For more information, see
Hardware Acceleration for Emulator Performance.

Android Device Manager on Windows


You use the Android Device Manager to create and configure an Android Virtual Devices
(AVD) that run in the Android Emulator. Each AVD is an emulator configuration that
simulates a physical Android device. This makes it possible to run and test your app in a
variety of configurations that simulate different physical Android devices.

Requirements
To use the Android Device Manager, you'll need the following items:

Visual Studio 2022: Community, Professional, and Enterprise editions are


supported.

The Android SDK API Level 30 or later. Be sure to install the Android SDK at its
default location if it isn't already installed: C:\Program Files (x86)\Android\android-
sdk.

The following packages must be installed:


Android SDK Tools 5.0 or later
Android SDK Platform-Tools 31.0.3 or later
Android SDK Build-Tools 30.0.2 or later
Android Emulator 30.8.4 or later

These packages should be displayed with Installed status as seen in the following
screenshot:

When you install the .NET Multi-Platform App UI development workload in Visual
Studio, everything is installed for you. For more information on setting up .NET MAUI
with Visual Studio, see Build your first app.

Open the device manager


Open the Android Device Manager in Visual Studio from the Tools menu by pressing
Tools > Android > Android Device Manager:
Main screen
When you run the Android Device Manager, it presents a screen that displays all
currently configured virtual devices. For each virtual device, the Name, OS (Android
Version), Processor, Memory size, and screen Resolution are displayed:

When you select a device in the list, the Start button appears on the right. Press the
Start button to launch the emulator with this virtual device. If the emulator is running
with the selected virtual device, the Start button changes to a Stop button that you can
use to halt the emulator.

Create a new device


To create a new device, press the New button:

The New Device window is displayed. To configure the device, follow these steps:

1. Give the device a new name. In the following example, the new device is named
Pixel 3a - API 31.

2. Select a physical device to emulate by selecting a device in the Base Device box.

3. Select a processor type for this virtual device with the Processor box.

It's recommended that you choose x86_64 and enable hardware acceleration.

4. Select the Android version (API level) with the OS box.

If you select an Android API level that has not yet been installed, the Device
Manager will display A new device will be downloaded message at the bottom of
the screen – it will download and install the necessary files as it creates the new
virtual device.

5. If you want to include Google Play Services APIs in your virtual device, select the
Google APIs option. To include the Google Play Store app on the virtual device,
select the Google Play Store option

7 Note

Google Play Store images are available only for some base device types such
as Pixel, Pixel 2, Pixel 3, and Nexus 5. This is indicated by the text (+ Store) in
the image name.

6. Use the property list to change some of the most commonly modified properties.
To make changes to properties, see Editing Android Virtual Device Properties.

7. Add any additional properties that you need to explicitly set with the Add Property
box at the bottom of the window:
You can also define a custom property by selecting Custom....

8. Press the Create button to create the new device:

You might get a License Acceptance screen when you create the device. Select
Accept if you agree to the license terms.

9. The Android Device Manager adds the new device to the list of installed virtual
devices while displaying a Creating progress indicator during device creation:
10. When the creation process is complete, the new device is shown in the list of
installed virtual devices with a Start button, ready to launch

Edit device
To edit an existing virtual device, select the device and then press the Edit button:

Pressing Edit displays the Device Editor window for the selected virtual device.

The Device Editor window lists the properties of the virtual device under the Property
column, with the corresponding values of each property in the Value column. When you
select a property, a detailed description of that property is displayed on the right.

To change a property, edit its value in the Value column. For example, in the following
screenshot the hw.lcd.density property is being changed to 240:

After you've made the necessary configuration changes, press the Save button. For
more information about changing virtual device properties, see Editing Android Virtual
Device Properties.

Additional options
Additional options for working with devices are available from the Additional Options
(…) pull-down menu:

The additional options menu contains the following items:

Duplicate and Edit – Duplicates the currently selected device and opens it in the
New Device screen with a new name that's similar to the existing device. For
example, selecting Pixel 3a - API 31 and pressing Duplicate and Edit appends a
counter to the name: Pixel 3a - API 31 (1).

Start with Factory Defaults – Starts the device with a cold boot.

Start with Kernel Logs – Starts the emulator and opens up kernel logs directory.
Download System Image – Downloads the Android OS system image for the
device, if it's not already downloaded.

Reveal in Explorer – Opens Windows Explorer and navigates to the folder that
holds the files for the virtual device.

Repair – Initiates a repair on the device.

Factory Reset – Resets the selected device to its default settings, erasing any user
changes made to the internal state of the device while it was running. This action
also erases the current Fast Boot snapshot if it exists. This change doesn't alter
modifications that you make to the virtual device during creation and editing. A
dialog box will appear with the reminder that this reset cannot be undone – press
Factory Reset to confirm the reset.

Delete – Permanently deletes the selected virtual device. A dialog box will appear
with the reminder that deleting a device cannot be undone. Press Delete if you are
certain that you want to delete the device.

Troubleshooting
The following sections explain how to diagnose and work around problems that may
occur when using the Android Device Manager to configure virtual devices.

Wrong version of Android SDK Tools


If you have the wrong Android SDK tools installed, installed, you may see this error
dialog on launch:

If you see that error dialog, press Open SDK Manager to open the Android SDK
Manager. In the Android SDK Manager, go to the Tools tab and install the following
packages:

Android SDK Command-line Tools 5.0 or later


Android SDK Platform-Tools 31.0.3 or later
Android SDK Build-Tools 30.0.3 or later

Snapshot disables Wi-Fi on Android Oreo


If you've an AVD configured for Android Oreo with simulated Wi-Fi access, restarting the
AVD after a snapshot may cause Wi-Fi access to become disabled.

To work around this problem,

1. Open the Android Device Manager.

2. Select the AVD in the Android Device Manager.

3. From the Additional Options (…) menu, select Reveal in Explorer.

4. Navigate to the snapshots > default_boot folder.

5. Delete the snapshot.pb file:

6. Restart the AVD.

After these changes are made, the AVD will restart in a state that allows Wi-Fi to work
again.
Editing Android virtual device
properties
Article • 08/09/2022 • 14 minutes to read

This article explains how to use the Android Device Manager (AVD) to edit the profile
properties of an Android virtual device.

Android Device Manager on Windows


The Android Device Manager supports the editing of individual Android virtual device
profile properties. The New Device and Device Edit screens list the properties of the
virtual device in the first column, with the corresponding values of each property in the
second column (as seen in this example):

When you select a property, a detailed description of that property is displayed on the
right. You can modify hardware profile properties and AVD properties. Hardware profile
properties (such as hw.ramSize and hw.accelerometer ) describe the physical
characteristics of the emulated device. These characteristics include screen size, the
amount of available RAM, whether or not an accelerometer is present. AVD properties
specify the operation of the AVD when it runs. For example, AVD properties can be
configured to specify how the AVD uses your development computer's graphics card for
rendering.
You can change properties by using the following guidelines:

To change a boolean property, click the check mark to the right of the boolean
property:

To change an enum (enumerated) property, click the down-arrow to the right of


the property and choose a new value.

To change a string or integer property, double-click the current string or integer


setting in the value column and enter a new value.

The following table provides a detailed explanation of the properties listed in the New
Device and Device Editor screens:

Property Description Options

abi.type ABI type – Specifies the ABI (application binary x86, x86_64,
interface) type of the emulated device. The x86 armeabi-
option is for the instruction set commonly v7a, arm64-
referred to as "x86" or "IA-32." The x86_64 v8a
option is for the 64-bit x86 instruction set. The
armeabi-v7a option is for the ARM instruction
set with v7-a ARM extensions. The arm64-v8a
option is for the ARM instruction set that
supports AArch64.
Property Description Options

disk.cachePartition Cache partition – Determines whether the yes, no


emulated device will use a /cache partition on
the device. The /cache partition (which is initially
empty) is the location where Android stores
frequently accessed data and app components. If
set to no, the emulator will not use a /cache
partition and the other disk.cache settings will
be ignored.

disk.cachePartition.path Cache partition path – Specifies a cache partition


image file on your development computer. The
emulator will use this file for the /cache partition.
Enter an absolute path or a path relative to the
emulator's data directory. If not set, the emulator
creates an empty temporary file called cache.img
on your development computer. If the file does
not exist, it is created as an empty file. This
option is ignored if disk.cachePartition is set to
no.

disk.cachePartition.size Cache partition size – The size of the cache


partition file (in bytes). Normally you do not need
to set this option unless the app will be
downloading very large files that are larger than
the default cache size of 66 megabytes. This
option is ignored if disk.cachePartition is set to
no. If this value is an integer, it specifies the size
in bytes. You can also specify the size in
kilobytes, megabytes, and gigabytes by
appending K, M, or G to the value. The minimum
size is 9M and the maximum size is 1023G.

disk.dataPartition.initPath Initial path to the data partition – Specifies the


initial contents of the data partition. After wiping
user data, the emulator copies the contents of
the specified file to user data (by default,
userdata-qemu.img) instead of using
userdata.img as the initial version.
Property Description Options

disk.dataPartition.path Path to the data partition – Specifies the user


data partition file. To configure a persistent user
data file, enter a filename and a path on your
development computer. If the file doesn't exist,
the emulator creates an image from the default
file userdata.img, stores it in the filename
specified by disk.dataPartition.path , and
persists user data to it when the emulator shuts
down. If you don't specify a path, the default file
is named userdata-qemu.img. The special value
<temp> causes the emulator to create and use a
temporary file. If disk.dataPartition.initPath is
set, its content will be copied to the
disk.dataPartition.path file at boot-time. Note
that this option cannot be left blank.

disk.dataPartition.size Data partition size – Specifies the size of the


user data partition in bytes. If this value is an
integer, it specifies the size in bytes. You can also
specify the size in kilobytes, megabytes, and
gigabytes by appending K, M, or G to the value.
The minimum size is 9M and the maximum size
is 1023G.

disk.ramdisk.path Ramdisk path – Path to the boot partition


(ramdisk) image. The ramdisk image is a subset
of the system image that is loaded by the kernel
before the system image is mounted. The
ramdisk image typically contains boot-time
binaries and initialization scripts. If this option is
not specified, the default is ramdisk.img in the
emulator system directory.

disk.snapStorage.path Snapshot storage path – Path to the snapshot


storage file where all snapshots are stored. All
snapshots made during execution will be saved
to this file. Only snapshots that are saved to this
file can be restored during the emulator run. If
this option is not specified, the default is
snapshots.img in the emulator data directory.

disk.systemPartition.initPath System partition init path – Path to the read-


only copy of the system image file; specifically,
the partition containing the system libraries and
data corresponding to the API level and any
variant. If this path is not specified, the default is
system.img in the emulator system directory.
Property Description Options

disk.systemPartition.path System partition path – Path to the read/write


system partition image. If this path is not set, a
temporary file will be created and initialized from
the contents of the file specified by
disk.systemPartition.initPath .

disk.systemPartition.size System partition size – The ideal size of the


system partition (in bytes). The size is ignored if
the actual system partition image is larger than
this setting; otherwise, it specifies the maximum
size that the system partition file can grow to. If
this value is an integer, it specifies the size in
bytes. You can also specify the size in kilobytes,
megabytes, and gigabytes by appending K, M, or
G to the value. The minimum size is 9M and the
maximum size is 1023G.

hw.accelerometer Accelerometer – Determines whether the yes, no


emulated device contains an accelerometer
sensor. The accelerometer helps the device
determine orientation (used for auto-rotation).
The accelerometer reports the acceleration of the
device along three sensor axes.

hw.audioInput Audio recording support – Determines whether yes, no


the emulated device can record audio.

hw.audioOutput Audio playback support – Determines whether yes, no


the emulated device can play audio.

hw.battery Battery support – Determines whether the yes, no


emulated device can run on a battery.

hw.camera Camera support – Determines whether the yes, no


emulated device has a camera.

hw.camera.back Back-facing camera – Configures the back-facing emulated,


camera (the lens faces away from the user). If you none,
are using a webcam on your development webcam0
computer to simulate the back-facing camera on
the emulated device, this value must be set to
webcamn, where n selects the webcam (if you
have only one webcam, choose webcam0). If set
to emulated, the emulator simulates the camera
in software. To disable the back-facing camera,
set this value to none. If you enable the back-
facing camera, be sure to also enable hw.camera .
Property Description Options

hw.camera.front Front-facing camera – Configures the front- emulated,


facing camera (the lens faces towards the user). If none,
you are using a webcam on your development webcam0
computer to simulate the front-facing camera on
the emulated device, this value must be set to
webcamn, where n selects the webcam (if you
have only one webcam, choose webcam0). If set
to emulated, the emulator simulates a camera in
software. To disable the front-facing camera, set
this value to none. If you enable the front-facing
camera, be sure to also enable hw.camera .

hw.camera.maxHorizontalPixels Maximum horizontal camera pixels – Configures


the maximum horizontal resolution of the
emulated device's camera (in pixels).

hw.camera.maxVerticalPixels Maximum vertical camera pixels – Configures


the maximum vertical resolution of the emulated
device's camera (in pixels).

hw.cpu.arch CPU architecture – The CPU architecture to be x86, x86_64,


emulated by the virtual device. If you are using arm, arm64
Intel HAXM for hardware acceleration, select x86
for a 32-bit CPU. Select x86_64 for a 64-bit
HAXM-accelerated device. (Be sure to install the
corresponding Intel x86 system image in the SDK
Manager: for example, Intel x86 Atom or Intel
x86 Atom_64.) To simulate an ARM CPU, select
arm for 32-bit or select arm64 for a 64-bit ARM
CPU. Keep in mind that ARM-based virtual
devices will run much slower than those that are
x86-based because hardware acceleration is not
available for ARM.

hw.cpu.model CPU model – This value is normally left unset (it


will be set to a value that is derived from
hw.cpu.arch if it is not explicitly set). However, it
can be set to an emulator-specific string for
experimental use.

hw.dPad DPad keys – Determines whether the emulated yes, no


device supports directional pad (DPad) keys. A
DPad typically has four keys to indicate
directional control.

hw.gps GPS support – Determines whether the emulated yes, no


device has a GPS (Global Positioning System)
receiver.
Property Description Options

hw.gpu.enabled GPU emulation – Determines whether the yes, no


emulated device supports GPU emulation. When
enabled, GPU emulation uses Open GL for
Embedded Systems (OpenGL ES) for rendering
both 2D and 3D graphics on the screen, and the
associated GPU Emulation Mode setting
determines how the GPU emulation is
implemented.

hw.gpu.mode GPU emulation mode – Determines how GPU auto, host,


emulation is implemented by the emulator. If you mesa,
select auto, the emulator will choose hardware angle,
and software acceleration based on your swiftshader,
development computer setup. If you select host, off
the emulator will use your development
computer's graphics processor to perform GPU
emulation for faster rendering. If your GPU is not
compatible with the emulator and you are on
Windows, you can try angle instead of host. The
angle mode uses DirectX to provide performance
similar to host. If you select mesa, the emulator
will use the Mesa 3D software library to render
graphics. Select mesa if you have problems
rendering via your development computer's
graphics processor. The swiftshader mode can be
used to render graphics in software with slightly
less performance than using your computer's
GPU. The off option (disable graphics hardware
emulation) is a deprecated option that can cause
improper rendering for some items and is
therefore not recommended.

hw.gsmModem GSM modem support – Determines whether the yes, no


emulated device includes a modem that supports
the GSM (Global System for Mobile
Communications) telephony radio system.

hw.initialOrientation Initial screen orientation – Configures the initial portrait,


orientation of the screen on the emulated device landscape
(portrait or landscape mode). In portrait mode,
the screen is taller than it is wide. In landscape
mode, the screen is wider than it is tall. When
running the emulated device, you can change
the orientation if both portrait and landscape are
supported in the device profile.

hw.keyboard Keyboard support – Determines whether the yes, no


emulated device supports a QWERTY keyboard.
Property Description Options

hw.keyboard.charmap Keyboard charmap name – The name of the


hardware charmap for this device. NOTE: This
should always be the default qwerty2 unless you
have modified the system image accordingly.
This name is sent to the kernel at boot time.
Using an incorrect name will result in an
unusable virtual device.

hw.keyboard.lid Keyboard lid support – If keyboard support is yes, no


enabled, this setting determines whether the
QWERTY keyboard can be closed/hidden or
opened/visible. This setting will be ignored if
hw.keyboard is set to false. NOTE: the default
value is false if the emulated device targets API
level 12 or higher.

hw.lcd.backlight LCD backlight – Determines whether an LCD yes, no


backlight is simulated by the emulated device.

hw.lcd.density LCD density – The density of the emulated LCD 120, 160,
display, measured in density-independent pixels, 240, 213,
or dp (dp is a virtual pixel unit). When the setting 320
is 160 dp, each dp corresponds to one physical
pixel. At runtime, Android uses this value to
select and scale the appropriate resources/assets
for correct display rendering.

hw.lcd.depth LCD color depth – The color bit-depth of the 16, 32


emulated framebuffer that holds the bitmap for
driving the LCD display. This value can be 16 bits
(65,536 possible colors) or 32 bits (16,777,216
colors plus transparency). The 32-bit setting can
make the emulator run slightly slower but with
better color accuracy.

hw.lcd.height LCD pixel height – The number of pixels that


make up the vertical dimension of the emulated
LCD display.

hw.lcd.width LCD pixel width – The number of pixels that


make up the horizontal dimension of the
emulated LCD display.
Property Description Options

hw.mainKeys Hardware Back/Home keys – Determines yes, no


whether the emulated device supports hardware
Back and Home navigation buttons. You can set
this value to yes if the buttons are implemented
only in software. If hw.mainKeys is set to yes, the
emulator will not display navigation buttons on
the screen, but you can use the emulator side
panel to "press" these buttons.

hw.ramSize Device RAM Size – The amount of physical RAM


on the emulated device, in megabytes. The
default value will be computed from the screen
size or the skin version. Increasing the size can
provide faster emulator operation, but at the
expense of demanding more resources from
your development computer.

hw.screen Touch screen type – Defines the type of screen touch,


on the emulated device. A multi-touch screen multi-
can track two or more fingers on the touch touch, no-
interface. A touch screen can detect only single- touch
finger touch events. A no-touch screen does not
detect touch events.

hw.sdCard SDCard support – Determines whether the yes, no


emulated device supports insertion and removal
of virtual SD (Secure Digital) cards. The emulator
uses mountable disk images stored on your
development computer to simulate the partitions
of actual SD card devices (see hw.sdCard.path).

sdcard.size SDCard size – Specifies the size of the virtual SD


card file at the location specified by
hw.sdCard.path . available on the device (in
bytes). If this value is an integer, it specifies the
size in bytes. You can also specify the size in
kilobytes, megabytes, and gigabytes by
appending K, M, or G to the value. The minimum
size is 9M and the maximum size is 1023G.

hw.sdCard.path SDCard Image Path – Specifies the filename and


path to an SD card partition image file on your
development computer. For example, this path
could be set to C:\sd\sdcard.img on Windows.
Property Description Options

hw.sensors.magnetic_field Magnetic Field Sensor – Determines whether the yes, no


emulated device supports a magnetic field
sensor. The magnetic field sensor (also known as
magnetometer) reports the ambient
geomagnetic field as measured along three
sensor axes. Enable this setting for apps that
need access to a compass reading. For example,
a navigation app might use this sensor to detect
which direction the user faces.

hw.sensors.orientation Orientation Sensor – Determines whether the yes, no


emulated device provides orientation sensor
values. The orientation sensor measures degrees
of rotation that a device makes around all three
physical axes (x, y, z). Note that the orientation
sensor was deprecated as of Android 2.2 (API
level 8).

hw.sensors.proximity Proximity Sensor – Determines whether the yes, no


emulated device supports a proximity sensor.
This sensor measures the proximity of an object
relative to the view screen of a device. This
sensor is typically used to determine whether a
handset is being held up to a person's ear.

hw.sensors.temperature Temperature Sensor – Determines whether the yes, no


emulated device supports a temperature sensor.
This sensor measures the temperature of the
device in degrees Celsius (°C).

hw.touchScreen Touch-screen support – Determines whether the yes, no


emulated device supports a touch screen. The
touch screen is used for direct manipulation of
objects on the screen.

hw.trackBall Trackball support – Determines whether the yes, no


emulated device supports a trackball.

hw.useext4 EXT4 file system support – Determines whether no


the emulated device uses the Linux EXT4 file
system for partitions. Because the file system
type is now auto-detected, this option is
deprecated and ignored.
Property Description Options

kernel.newDeviceNaming Kernel new device naming – Used to specify autodetect,


whether the kernel requires a new device naming yes, no
scheme. This is typically used with Linux 3.10
kernels and later. If set to autodetect, the
emulator will automatically detect whether the
kernel requires a new device naming scheme.

kernel.parameters Kernel parameters – Specifies the string of Linux


kernel boot parameters. By default, this setting is
left blank.

kernel.path Kernel path – Specifies the path to the Linux


kernel. If this path is not specified, the emulator
looks in the emulator system directory for kernel-
ranchu.

kernel.supportsYaffs2 YAFFS2 partition support – Determines whether autodetect,


the kernel supports YAFFS2 (Yet Another Flash yes, no
File System 2) partitions. Typically, this applies
only to kernels before Linux 3.10. If set to
autodetect the emulator will automatically
detect whether the kernel can mount YAFFS2 file
systems.

skin.name Skin name – The name for an Android emulator


skin. A skin is a collection of files that defines the
visual and control elements of an emulator
display; it describes what the window of the AVD
will look like on your development computer. A
skin describes screen size, buttons, and the
overall design, but it does not affect the
operation of your app.

skin.path Skin path – Path to the directory that contains


the emulator skin files specified in skin.name This
directory contains hardware.ini layout files, and
image files for the display elements of the skin.

skin.dynamic Skin dynamic – Whether or not the skin is no


dynamic. The emulator skin is a dynamic skin if
the emulator is to construct a skin of a given size
based on a specified width and height.

For more information about these properties, see Hardware Profile Properties .
Debug on the Android Emulator
Article • 08/18/2022 • 2 minutes to read

The Android Emulator, installed as part of the .NET Multi-Platform App UI


development workload, can be run in various configurations to simulate different
Android devices. Each one of these configurations is created as a virtual device. In this
article, you'll learn how to launch the emulator from Visual Studio and run your app in a
virtual device. For more information about how to create and configure a virtual device,
see Managing virtual devices with the Android Device Manager.

Launching the Emulator


Near the top of Visual Studio, there's the Solution Configurations drop-down menu
that can be used to select Debug or Release mode. Choosing Debug causes the
debugger to attach to the application process running inside the emulator after the app
starts. Choosing Release mode disables the debugger. When in release mode, you'll
need to rely on app logging for debugging.

After you've chosen a virtual device from the Debug Target device drop-down menu,
select either Debug or Release mode, then select the Play button to run the application:

After the emulator starts, Visual Studio deploys the app to the virtual device. An
example screenshot of the Android Emulator is displayed below. In this example, the
emulator is running the .NET MAUI template app.
When you're finished debugging and running your app, you can leave the emulator
running. The first time a .NET MAUI app is run in the emulator, the .NET MAUI shared
runtime for the targeted API level is installed, followed by the app. The runtime
installation may take a few moments to install. If you leave the emulator running, later
debugging sessions start faster as the runtime is already present on the device. If the
device is restarted, the runtime will be redeployed to the device.

Fast boot
The Android Emulator includes a feature named Fast Boot which is enabled by default.
This feature is configured by each device's emulator settings. With this feature enabled,
a snapshot of the virtual device is saved when the emulator is closed. The snapshot is
quickly restored the next time the device is started.

The first time a virtual device is started, a cold boot of the virtual device takes place
without a speed improvement because a snapshot hasn't yet been created:

When you exit out of the emulator, Fast Boot saves the state of the emulator in a
snapshot:
The next time the virtual device starts, it loads much faster because the emulator simply
restores the state at which you closed the emulator.

Troubleshooting
For tips and workarounds for common emulator problems, see Android Emulator
Troubleshooting.

For more information about using the Android Emulator, see the following Android
Developer articles:

Navigating on the Screen


Performing Basic Tasks in the Emulator
Working with Extended Controls, Settings, and Help
Run the emulator with Quick Boot
Android emulator troubleshooting
Article • 01/09/2023 • 10 minutes to read

This article describes the most common warning messages and issues that occur while
configuring and running the Android Emulator. Also, it describes solutions for resolving
these errors and various troubleshooting tips to help you diagnose emulator problems.

Deployment issues on Windows


Some error messages may be displayed by the emulator when you deploy your app. The
most common errors and solutions are explained here.

Deployment errors
If you see an error about a failure to install the APK on the emulator or a failure to run
the Android Debug Bridge (adb), verify that the Android SDK can connect to your
emulator. To verify emulator connectivity, use the following steps:

1. Launch the emulator from the Android Device Manager (select your virtual device
and select Start).

2. Open a command prompt and go to the folder where adb is installed. If the
Android SDK is installed at its default location, adb is located at C:\Program Files
(x86)\Android\android-sdk\platform-tools\adb.exe; if not, modify this path for the
location of the Android SDK on your computer.

3. Type the following command:

shell

adb devices

4. If the emulator is accessible from the Android SDK, the emulator should appear in
the list of attached devices. For example:

shell

List of devices attached

emulator-5554 device

5. If the emulator doesn't appear in this list, start the Android SDK Manager, apply all
updates, then try launching the emulator again.

MMIO access error


If the message An MMIO access error has occurred is displayed, restart the emulator.

Missing Google Play Services


If the emulated Android device doesn't have Google Play Services or Google Play Store
installed, you probably created a virtual device that excluded these packages. When you
create a virtual device, be sure to select one or both of the following options:

Google APIs—includes Google Play Services in the virtual device.


Google Play Store–includes Google Play Store in the virtual device.

For example, this virtual device will include Google Play Services and Google Play Store:

7 Note

Google Play Store images are available only for some base device types such as
Pixel, Pixel 2, Nexus 5, and Nexus 5X.
Performance issues
Performance issues are typically caused by one of the following problems:

The emulator is running without hardware acceleration.


The virtual device running in the emulator using an Arm-based image.

The following sections cover these scenarios in more detail.

Hardware acceleration isn't enabled


When you start a virtual device, and you don't have hardware acceleration enabled, the
Device Manager displays an error dialog similar to the following image:

To fix this error, follow the troubleshooting steps in the Hardware acceleration issues
section.

Hardware acceleration issues


When using hardware acceleration, you may run into configuration problems or conflicts
with other software on your computer. The first step in troubleshooting is verifying that
hardware acceleration is enabled. You can use the Android's SDK to check this setting.
Open a command prompt and entering the following command:

cmd

"C:\Program Files (x86)\Android\android-sdk\emulator\emulator-check.exe"


accel

This command assumes that the Android SDK is installed at the default location of
C:\Program Files (x86)\Android\android-sdk. If the Android SDK is installed elsewhere,
modify the preceding command to the correct location.

 Tip

Make sure the Android Emulator is up to date. From Visual Studio, press Tools >
Android > Android SDK Manager. Select the Tools tab and see if the Android
Emulator entry has an update available.

Hardware acceleration not available


If Hyper-V is available, a message like the following example will be returned from the
emulator-check.exe accel command:

cmd

HAXM isn't installed, but Windows Hypervisor Platform is available.

If HAXM is available, a message like the following example will be returned:

cmd

HAXM version 6.2.1 (4) is installed and usable.

If hardware acceleration isn't available, a message like the following example will be
displayed (the emulator looks for HAXM if it's unable to find Hyper-V):

cmd

HAXM isn't installed on this machine

If hardware acceleration isn't available, see Enabling Hyper-V acceleration to learn how
to enable hardware acceleration on your computer.

Incorrect BIOS settings


If the BIOS hasn't been configured properly to support hardware acceleration, a
message similar to the following example will be displayed when you run the emulator-
check.exe accel command:
cmd

VT feature disabled in BIOS/UEFI

To correct this problem, reboot into your computer's BIOS and enable the following
options:

Virtualization Technology (may have a different label depending on motherboard


manufacturer).
Hardware Enforced Data Execution Prevention.

If problems still occur because of issues related to Hyper-V and HAXM, see the following
section.

Hyper-V issues
In some cases, enabling both Hyper-V and Windows Hypervisor Platform in the Turn
Windows features on or off dialog may not properly enable Hyper-V. To verify that
Hyper-V is enabled, use the following steps:

1. Enter PowerShell in the Windows search box.

2. Right-click Windows PowerShell in the search results and select Run as


administrator.

3. In the PowerShell console, enter the following command:

PowerShell

Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-All -Online

If Hyper-V isn't enabled, a message similar to the following example will be


displayed to indicate that the state of Hyper-V is Disabled:

cmd

FeatureName : Microsoft-Hyper-V-All

DisplayName : Hyper-V

Description : Provides services and management tools for creating


and running virtual machines and their resources.

RestartRequired : Possible

State : Disabled

CustomProperties :

4. In the PowerShell console, enter the following command:


PowerShell

Get-WindowsOptionalFeature -FeatureName HypervisorPlatform -Online

If the Hypervisor isn't enabled, a message similar to the following example will be
displayed to indicate that the state of HypervisorPlatform is Disabled:

cmd

FeatureName : HypervisorPlatform

DisplayName : Windows Hypervisor Platform

Description : Enables virtualization software to run on the


Windows hypervisor

RestartRequired : Possible

State : Disabled

CustomProperties :

If Hyper-V or HypervisorPlatform aren't enabled, use the following PowerShell


commands to enable them:

PowerShell

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

Enable-WindowsOptionalFeature -Online -FeatureName HypervisorPlatform -All

After these commands complete, reboot.

For more information about enabling Hyper-V (including techniques for enabling
Hyper-V using the Deployment Image Servicing and Management tool), see Install
Hyper-V.

HAXM issues
HAXM issues are often the result of conflicts with other virtualization technologies,
incorrect settings, or an out-of-date HAXM driver.

HAXM process isn't running


If HAXM is installed, you can verify that the HAXM process is running by opening a
command prompt and entering the following command:

cmd

sc query intelhaxm

If the HAXM process is running, you should see output similar to the following result:

cmd

SERVICE_NAME: intelhaxm

TYPE : 1 KERNEL_DRIVER

STATE : 4 RUNNING

(STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)

WIN32_EXIT_CODE : 0 (0x0)

SERVICE_EXIT_CODE : 0 (0x0)

CHECKPOINT : 0x0

WAIT_HINT : 0x0

For information about troubleshooting HAXM, see Troubleshooting in the HAXM wiki.

HAXM virtualization conflicts

HAXM can conflict with other technologies that use virtualization, such as Hyper-V,
Windows Device Guard, and some antivirus software:

Hyper-V—If you're using a version of Windows before the Windows 10 April 2018
update (build 1803) and Hyper-V is enabled, follow the steps in Disabling Hyper-V
so that HAXM can be enabled.

Device Guard—Device Guard and Credential Guard can prevent Hyper-V from
being disabled on Windows machines. To disable Device Guard and Credential
Guard, see Disabling Device Guard.

Antivirus Software—If you're running antivirus software that uses hardware-


assisted virtualization (such as Avast), disable or uninstall this software, reboot, and
retry the Android emulator.

Incorrect BIOS settings for HAXM

On Windows, HAXM won't work unless virtualization technology (Intel VT-x) is enabled
in the BIOS. If VT-x is disabled, you'll get an error similar to the following when you
attempt to start the Android Emulator:

This computer meets the requirements for HAXM, but Intel Virtualization
Technology (VT-x) isn't turned on.

To correct this error, boot the computer into the BIOS, enable both VT-x and SLAT
(Second-Level Address Translation) and restart the computer.
Disabling Hyper-V
If you're using a version of Windows before the Windows 10 April 2018 Update (build
1803) and Hyper-V is enabled, you must disable Hyper-V and reboot your computer to
install and use HAXM. If you're using Windows 10 April 2018 Update (build 1803) or
later, Android Emulator version 27.2.7 or later can use Hyper-V (instead of HAXM) for
hardware acceleration, so it isn't necessary to disable Hyper-V.

You can disable Hyper-V from the Control Panel by following these steps:

1. Enter windows features in the Windows search box and select Turn Windows
features on or off in the search results.

2. Uncheck Hyper-V:

3. Restart the computer.

Alternately, you can use the following PowerShell command to disable the Hyper-V
Hypervisor:

PowerShell

Disable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-


Hypervisor

Intel HAXM and Microsoft Hyper-V can't both be active at the same time. Unfortunately,
there's no way to switch between Hyper-V and HAXM without restarting your computer.
It's possible that the preceding steps won't succeed in disabling Hyper-V if Device Guard
and Credential Guard are enabled. If you're unable to disable Hyper-V, or it seems to be
disabled but HAXM installation still fails, use the steps in the next section to disable
Device Guard and Credential Guard.

Disabling Device Guard

Device Guard and Credential Guard can prevent Hyper-V from being disabled on
Windows machines. This situation is often a problem for domain-joined machines that
are configured and controlled by an owning organization. On Windows 10, use the
following steps to see if Device Guard is running:

1. Enter System info in the Windows search box and select System Information in
the search results.

2. In the System Summary, look to see if Device Guard Virtualization based security
is present and is in the Running state:

If Device Guard is enabled, use the following steps to disable it:

1. Ensure that Hyper-V is disabled (under Turn Windows Features on or off) as


described in the previous section.

2. In the Windows Search Box, enter gpedit.msc and select the Edit group policy
search result. These steps launch the Local Group Policy Editor.

3. In the Local Group Policy Editor, navigate to Computer Configuration >


Administrative Templates > System > Device Guard:

4. Change Turn On Virtualization Based Security to Disabled (as shown above) and
exit the Local Group Policy Editor.

5. In the Windows Search Box, enter cmd. When Command Prompt appears in the
search results, right-click Command Prompt and select Run as Administrator.

6. Copy and paste the following commands into the command prompt window (if
drive Z: is in use, pick an unused drive letter to use instead):

cmd

mountvol Z: /s

copy %WINDIR%\System32\SecConfig.efi
Z:\EFI\Microsoft\Boot\SecConfig.efi /Y

bcdedit /create {0cb3b571-2f2e-4343-a879-d86a476d7215} /d "DebugTool"


/application osloader

bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} path


"\EFI\Microsoft\Boot\SecConfig.efi"

bcdedit /set {bootmgr} bootsequence {0cb3b571-2f2e-4343-a879-


d86a476d7215}

bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} loadoptions


DISABLE-LSA-ISO,DISABLE-VBS

bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} device partition=Z:

mountvol Z: /d

7. Restart your computer. On the boot screen, you should see a prompt similar to the
following message:

Do you want to disable Credential Guard?

Press the indicated key to disable Credential Guard as prompted.


8. After the computer reboots, check again to ensure that Hyper-V is disabled (as
described in the previous steps).

If Hyper-V is still not disabled, the policies of your domain-joined computer may prevent
you from disabling Device Guard or Credential Guard. In this case, you can request an
exemption from your domain administrator to allow you to opt out of Credential Guard.
Alternately, you can use a computer that isn't domain-joined if you must use HAXM.

More troubleshooting tips


The following suggestions are often helpful in diagnosing Android emulator issues.

Starting the emulator from the command line


If the emulator isn't already running, you can start it from the command line (rather than
from within Visual Studio) to view its output. Typically, Android emulator AVD images
are stored at the following location: %userprofile%\.android\avd.

You can launch the emulator with an AVD image from this location by passing in the
folder name of the AVD. For example, this command launches an AVD named
Pixel_API_27:

cmd

"C:\Program Files (x86)\Android\android-sdk\emulator\emulator.exe" -


partition-size 2000 -no-boot-anim -verbose -feature
WindowsHypervisorPlatform -avd pixel_5_-_api_30 -prop
monodroid.avdname=pixel_5_-_api_30

This command assumes that the Android SDK is installed at the default location of
C:\Program Files (x86)\Android\android-sdk. If the Android SDK is installed elsewhere,
modify the preceding command to the correct location.

When you run this command, it produces many lines of output while the emulator starts
up. Specifically, lines such as the following example are printed if hardware acceleration
is enabled and working properly. In this example, HAXM is used for hardware
acceleration:

cmd

emulator: CPU Acceleration: working

emulator: CPU Acceleration status: HAXM version 6.2.1 (4) is installed and
usable.

Viewing Device Manager logs


Often you can diagnose emulator problems by viewing the Device Manager logs. These
logs are written to the following location:
%userprofile%\AppData\Local\Xamarin\Logs\16.0.

You can view each DeviceManager.log file by using a text editor such as Notepad. The
following example log entry indicates that HAXM wasn't found on the computer:

cmd

Component Intel x86 Emulator Accelerator (HAXM installer) r6.2.1 [Extra:


(Intel Corporation)] not present on the system

Set up Android device for debugging


Article • 04/04/2023 • 3 minutes to read

While the Android emulator is a great way to rapidly develop and test your app, you'll
want to test your apps on a real Android device. To run on a device, you'll need to
enable developer mode on the device and connect it to your computer.

) Important

The steps in this article are written generically, to work on as many devices as
possible. If you can't find these settings on your device, consult your device
manufacturer's documentation.

Enable developer mode on the device


A device must enable Developer mode in order to deploy and test an Android app.
Developer mode is enabled by following these steps:

1. Go to the Settings screen.


2. Select About phone.
3. Tap Build Number seven times until You are now a developer! is visible.

Depending on the UI your device is running, the About phone option may be in a
different location. Consult your device documentation if you can't find About phone.

Enable USB debugging


After enabling developer mode on your device, enable USB debugging by following
these steps:

1. Go to the Settings screen.


2. Select Developer options.
3. Turn on the USB debugging option.

Depending on the UI your device is running, the USB debugging option may be in a
different location. Consult your device documentation if you can't find USB debugging.

Connect the device to the computer


The final step is to connect the device to the computer. The easiest and most reliable
way is to do so over USB.

You'll receive a prompt to trust the computer on your device if you haven't used it for
debugging before. You can also check Always allow from this computer to prevent
requiring this prompt each time you connect the device.

If your computer isn't recognizing the device when it's plugged in, try installing a driver
for the device. Consult your device manufacturer's support documentation. You can also
try installing the Google USB Driver through the Android SDK Manager:
Enable WiFi debugging
It's possible to debug an android device over WiFi, without keeping the device physically
connected to the computer. This technique requires more effort, but could be useful
when the device is too far from the computer to remain constantly plugged-in via a
cable.

Connecting over WiFi


By default, the Android Debug Bridge (adb) is configured to communicate with an
Android device via USB. It's possible to reconfigure it to use TCP/IP instead of USB. To
do this, both the device and the computer must be on the same WiFi network.

First, enable Wireless debugging on your Android device:

1. Follow the steps in the Enable developer mode on the device section.
2. Follow the steps in the Enable USB debugging section.
3. Go to the Settings screen.
4. Select Developer options.
5. Turn on the Wireless debugging option.

Depending on the UI your device is running, the Wireless debugging option may be in
a different location. Consult your device documentation if you can't find Wireless
debugging.
Next, use adb to connect to your device, first through a USB connection:

1. Determine the IP address of your Android device. One way to find out the IP
address is to look under Settings > Network & internet > Wi-Fi, then tap on the
WiFi network that the device is connected to, and then tap on Advanced. This will
open a drop-down showing information about the network connection, similar to
what is seen in the screenshot below:

On some versions of Android the IP address won't be listed there but can be found
instead under Settings > About phone > Status.

2. In Visual Studio, open the adb command prompt by selecting the menu option:
Tools > Android > Android Adb Command Prompt....

3. In the command prompt, use the adb tcpip command to tell the device to listen
to TCP/IP connections on port 5555.

command

adb tcpip 5555

4. Disconnect the USB cable from your device.

5. Connect to the device's IP address with port 5555:

command

adb connect 192.168.1.28:5555

When this command finishes, the Android device is connected to the computer via
WiFi.
When you're finished debugging via WiFi, you can reset ADB back to USB mode
with the following command:

command

adb usb

To see the devices connected to the computer, use the adb devices command:

command

adb devices

Build an iOS app with .NET CLI


Article • 03/01/2023 • 3 minutes to read

In this tutorial, you'll learn how to create and run a .NET Multi-platform App UI (.NET
MAUI) app on iOS using .NET Command Line Interface (CLI) on macOS:

1. To create .NET MAUI apps, you'll need to download and run the installer for the
latest .NET runtime. You'll also need to download and install the latest version of
Xcode, which is also available from the App Store app on your Mac.

2. On your Mac, open Terminal and check that you have the latest .NET runtime
installed:

zsh

dotnet --version

3. In Terminal, install the latest public build of .NET MAUI:

zsh

sudo dotnet workload install maui --source


https://api.nuget.org/v3/index.json

This command will install the latest released version of .NET MAUI, including the
required platform SDKs.

4. In Terminal, create a new .NET MAUI app using .NET CLI:

zsh

dotnet new maui -n "MyMauiApp"

5. In Terminal, change directory to MyMauiApp, and build and run the app:

zsh

cd MyMauiApp

dotnet build -t:Run -f net7.0-ios

The dotnet build command will restore the project the dependencies, build the
app, and launch it in the default simulator.
6. In the default simulator, press the Click me button several times and observe that
the count of the number of button clicks is incremented.

Launch the app on a specific simulator


A .NET MAUI iOS app can be launched on a specific iOS simulator from a Mac by
providing its unique device id (UDID):
1. On your Mac, open Xcode, select the Windows > Devices and Simulators menu
item, and then the Simulators tab.

2. Right-click on your chosen simulator, and select Copy Identifier to copy the UDID
to the clipboard.

Alternatively, you can retrieve a list of UDID values by executing the simctl list
command:

zsh

/Applications/Xcode.app/Contents/Developer/usr/bin/simctl list

3. In Terminal, build the app and run it on your chosen simulator by specifying the
_DeviceName MSBuild property using the -p MSBuild option:

zsh
dotnet build -t:Run -f net7.0-ios -
p:_DeviceName=:v2:udid=insert_UDID_here

For example, use the following command to build the app and run it on the iPhone
13 Pro simulator:

zsh

dotnet build -t:Run -f net7.0-ios -p:_DeviceName=:v2:udid=E25BBE37-


69BA-4720-B6FD-D54C97791E79

4. In your chosen simulator, press the Click me button several times and observe that
the count of the number of button clicks is incremented.
Launch the app on a device
A device must be provisioned before you can deploy an iOS app to it. For more
information, see Device provisioning for iOS. Once a device has been provisioned, a
.NET MAUI iOS app can be launched on the device from a Mac by providing its unique
device id (UDID):

1. Connect your device to your local Mac with a USB cable.

2. Open Xcode, and navigate to Window > Devices and Simulators.


3. In Xcode, select the Devices tab, and select the device from the list of connected
devices.

4. In Xcode, copy the Identifier value to the clipboard:

Alternatively, right-click on your device and select Copy Identifier to copy the
UDID to the clipboard.

5. In Terminal, build the app and run it on your chosen simulator by specifying the
_DeviceName MSBuild property using the -p MSBuild option:

zsh

dotnet build -t:Run -f net7.0-ios -p:RuntimeIdentifier=ios-arm64 -


p:_DeviceName=insert_UDID_here

Pair to Mac for iOS development


Article • 03/22/2023 • 6 minutes to read

Building native iOS applications using .NET Multi-platform App UI (.NET MAUI) requires
access to Apple's build tools, which only run on a Mac. Because of this, Visual Studio
2022 must connect to a network-accessible Mac to build .NET MAUI iOS apps.

Visual Studio 2022's Pair to Mac feature discovers, connects to, authenticates with, and
remembers Mac build hosts so that you can work productively on Windows.

Pair to Mac enables the following software development workflow:

You can write .NET MAUI iOS code in Visual Studio 2022.
Visual Studio 2022 opens a network connection to a Mac build host and uses the
build tools on that machine to compile and sign the iOS app.
There's no need to run a separate application on the Mac – Visual Studio 2022
invokes Mac builds securely over SSH.
Visual Studio 2022 is notified of changes as soon as they happen. For example,
when an iOS device is plugged into the Mac or becomes available on the network,
the iOS Toolbar updates instantly.
Multiple instances of Visual Studio 2022 can connect to the Mac simultaneously.
It's possible to use the Windows command-line to build iOS apps.

7 Note

Before following the instructions in this article, on a Mac, install Xcode . Then
manually open Xcode, after installation, so that it can add additional components.
In addition, you should also install either the latest Visual Studio 2022 for Mac or
Mono . In addition, if you have a Mac computer with Apple silicon please ensure
that Rosetta is installed.

If you would prefer not to install Visual Studio 2022 for Mac, Visual Studio 2022 can
automatically configure the Mac build host. However, you must still install and run
Xcode, and install Mono.

Enable remote login on the Mac


To set up the Mac build host, first enable remote login:

1. On the Mac, open System Preferences and go to the Sharing pane.


2. Check Remote Login in the Service list.

Make sure that it's configured to allow access for All users, or that your Mac
username or group is included in the list of allowed users.

3. If prompted, configure the macOS firewall. If you have set the macOS firewall to
block incoming connections, you may need to allow mono-sgen to receive
incoming connections. An alert appears to prompt you if so.

4. If it's on the same network as the Windows machine, the Mac should now be
discoverable by Visual Studio 2022. If the Mac is still not discoverable, try manually
adding a Mac.

Connect to the Mac from Visual Studio 2022


After enabling remote login on the Mac, connect Visual Studio 2022 to the Mac:

1. In Visual Studio 2022, open an existing .NET MAUI project or create a new one.

2. Open the Pair to Mac dialog with the Pair to Mac button iOS toolbar:
Alternatively, select Tools > iOS > Pair to Mac.

The Pair to Mac dialog displays a list of all previously connected and currently
available Mac build hosts:

3. Select a Mac in the list and select Connect.

4. Enter your username and password. The first time you connect to any particular
Mac, you're prompted to enter your username and password for that machine:
 Tip

When logging in, use your system username.

Pair to Mac uses these credentials to create a new SSH connection to the Mac. If it
succeeds, a key is added to the authorized_keys file on the Mac. Subsequent
connections to the same Mac will log in automatically.

5. Pair to Mac automatically configures the Mac. Visual Studio 2022 installs or
updates pre-requisites on a connected Mac build host as needed. However, Xcode
must still be installed manually.

6. Examine the connection status icon. When Visual Studio 2022 is connected to a
Mac, that Mac's item in the Pair to Mac dialog displays an icon indicating that it's
currently connected:

There can be only one connected Mac at a time.

 Tip
Right-clicking any Mac in the Pair to Mac list brings up a context menu that
allows you to Connect..., Forget this Mac, or Disconnect:

If you choose Forget this Mac, your credentials for the selected Mac will be
forgotten. To reconnect to that Mac, you will need to re-enter your username
and password.

If you've successfully paired to a Mac build host, you're ready to build .NET MAUI iOS
apps in Visual Studio 2022. For more information, see Build your first app.

If you haven't been able to pair a Mac, try manually adding a Mac.

Manually add a Mac


If you don't see a specific Mac listed in the Pair to Mac dialog, add it manually:

1. Open System Preferences > Sharing > Remote Login on your Mac to locate your
Mac’s IP address:

Alternatively, use the command line. In Terminal, issue the following command:

zsh

ipconfig getifaddr en0

Depending on your network configuration, you may need to use an interface name
other than en0 , for example, en1 or en2 .

2. In Visual Studio 2022's Pair to Mac dialog, select Add Mac...:

3. Enter the Mac's IP address and select Add:


4. Enter your username and password for the Mac:

 Tip

When logging in, use your system username.

5. Select Login to connect Visual Studio 2022 to the Mac over SSH and add it to the
list of known machines.

Enable automatic connection to known Macs


By default, a connection to previously paired Macs won't be established when Visual
Studio starts. However, automatic connection to known Macs can be enabled in Visual
Studio by navigating to Tools > Options > Xamarin > iOS Settings and ensuring that
Enable auto connection to known Macs is checked:
After restarting Visual Studio, it will automatically connect to known Macs on each
launch.

Automatic Mac provisioning


Pair to Mac automatically provisions a Mac with the software necessary for building .NET
MAUI iOS apps. This includes .NET and various Xcode-related tools (but not Xcode
itself).

) Important

Pair to Mac can't install Xcode. You must manually install it on the Mac build
host. It's required for .NET MAUI iOS development.
Automatic Mac provisioning requires that remote login is enabled on the Mac,
and the Mac must be network-accessible to the Windows machine.
Automatic Mac provisioning requires sufficient free space on the Mac to
install .NET.

In addition, Pair to Mac performs required software installations and updates to the
Mac, when Visual Studio 2022 connects to it.

Xcode tools and license


Pair to Mac will also check to determine whether Xcode has been installed and its
license accepted. While Pair to Mac doesn't install Xcode, it does prompt for license
acceptance.

In addition, Pair to Mac will install or update various packages distributed with Xcode.
The installation of these packages happens quickly and without a prompt.

Troubleshooting automatic Mac provisioning


If you encounter any trouble using automatic Mac provisioning, take a look at the Visual
Studio 2022 IDE logs, stored in %LOCALAPPDATA%\Xamarin\Logs\17.0. These logs may
contain error messages to help you better diagnose the failure or get support.

Build iOS apps from the Windows command-


line
Pair to Mac supports building .NET MAUI apps from the command line. Navigate to the
folder that holds the source of your .NET MAUI iOS app and execute the following
command:

dotnet

dotnet build -f:net7.0-ios /p:ServerAddress={macOS build host IP address}


/p:ServerUser={macOS username} /p:ServerPassword={macOS password}
/p:TcpPort=58181 /p:_DotNetRootRemoteDirectory=/Users/{macOS
username}/Library/Caches/Xamarin/XMA/SDKs/dotnet/

The parameters passed to dotnet in the above example are:

ServerAddress – the IP address of the Mac build host.

ServerUser – the username to use when logging in to the Mac build host. Use your
system username rather than your full name.
ServerPassword – the password to use when logging in to the Mac build host.
_DotNetRootRemoteDirectory - the folder on the Mac build host that contains the

.NET SDK.

The first time Pair to Mac logs in to a Mac build host from either Visual Studio 2022 or
the command-line, it sets up SSH keys. With these keys, future logins won't require a
username or password. Newly created keys are stored in
%LOCALAPPDATA%\Xamarin\MonoTouch.
If the ServerPassword parameter is omitted from a command-line build invocation, Pair
to Mac attempts to log in to the Mac build host using the saved SSH keys.
Remote iOS Simulator for Windows
Article • 12/02/2022 • 2 minutes to read

The remote iOS Simulator for Windows allows you to test your apps on an iOS simulator
displayed in Windows alongside Visual Studio 2022.

Get started
The remote iOS Simulator for Windows is installed automatically as part of the .NET
Multi-platform App UI development workload in Visual Studio 2022. To use it, follow
these steps:

1. Launch Visual Studio 2022 and create or load a .NET MAUI app project.

2. In Visual Studio 2022, pair the IDE to a Mac Build host if you haven't previously. For
more information, see Pair to Mac for iOS development.

3. In the Visual Studio toolbar, use the Debug Target drop-down to select iOS
Simulators and then a specific iOS simulator:
4. In the Visual Studio toolbar, press the green Start button for your chosen iOS
simulator:

Visual Studio will build the app, start the remote iOS simulator for Windows, and
deploy the app to the simulator:
Enable the remote iOS simulator for Windows
The remote iOS simulator for Windows is enabled by default. However, if it's been
previously disabled it can be enabled in Visual Studio by navigating to Tools > Options
> Xamarin > iOS Settings and ensuring that Remote Simulator to Windows is checked:
7 Note

When the remote simulator is disabled in Visual Studio, debugging a .NET MAUI
iOS app will open the iOS Simulator on the connected Mac build host.

Simulator window toolbar


The toolbar at the top of the simulator's window displays five buttons:

The buttons are as follows:

Home – simulates the home button on an iOS device.


Lock – locks the simulator (swipe to unlock).
Take Screenshot – saves a screenshot of the simulator to \Users\
{User}\Pictures\Xamarin\iOS Simulator.
Settings – displays keyboard and other settings.
Other options – displays various simulator options such as rotation, and shake
gesture.

Clicking the toolbar's Settings button (the gear icon) opens the Settings window:
These settings allow you to enable the hardware keyboard and reset the content and
settings for the simulator.

Clicking the toolbar's Other options button (the ellipsis icon) reveals additional buttons
such as rotation, shake gestures, and rebooting:

7 Note

Right-clicking anywhere in the remote iOS simulator window will display all the
toolbar buttons as a context menu.

Touchscreen support
Many Windows computers have touch screens. Since the remote iOS Simulator for
Windows supports touch interactions, you can test your app with the same pinch, swipe,
and multi-finger touch gestures that you use with physical iOS devices.

Similarly, the remote iOS Simulator for Windows treats Windows Stylus input as Apple
Pencil input.

Sound handling
Sounds played by the simulator will come from the host Mac's speakers. iOS sounds are
not heard on Windows.

Troubleshooting
In some circumstances, an Xcode configuration problem can result in the remote iOS
Simulator for Windows getting stuck in a Connecting to Mac...Checking
Server...Connected... loop. When this occurs, you need to remove and reset the
Simulators on your Mac build host:

Ensure that Xamarin Mac Agent (XMA) and Xcode aren't running.
Delete your ~/Library/Developer/CoreSimulator/Devices folder.
Run killall -9 com.apple.CoreSimulator.CoreSimulatorService .
Run xcrun simctl list devices .

Logs
If you experience issues with the remote iOS Simulator, you can view the logs in the
following locations:

Mac – ~/Library/Logs/Xamarin/Simulator.Server
Windows – %LOCALAPPDATA%\Xamarin\Logs\Xamarin.Simulator
Build a Mac Catalyst app with .NET CLI
Article • 03/23/2023 • 2 minutes to read

In this tutorial, you'll learn how to create and run a .NET Multi-platform App UI (.NET
MAUI) app on Mac Catalyst using .NET Command Line Interface (CLI) on macOS:

1. To create .NET MAUI apps, you'll need to download and run the installer for the
latest .NET runtime. You'll also need to download and install the latest version of
Xcode, which is also available from the App Store app on your Mac.

2. On your Mac, open Terminal and check that you have the latest .NET runtime
installed:

zsh

dotnet --version

3. In Terminal, install the latest public build of .NET MAUI:

zsh

sudo dotnet workload install maui --source


https://api.nuget.org/v3/index.json

This command will install the latest released version of .NET MAUI, including the
required platform SDKs.

4. In Terminal, create a new .NET MAUI app using .NET CLI:

zsh

dotnet new maui -n "MyMauiApp"

5. In Terminal, change directory to MyMauiApp, and build and run the app:

zsh

cd MyMauiApp

dotnet build -t:Run -f net7.0-maccatalyst

The dotnet build command will restore the project dependencies, build the app,
and launch it.
If you see a build error and a warning that the Xcode app bundle could not be
found, you may need to run the following command:

zsh

xcode-select --reset

6. In the running app, press the Click me button several times and observe that the
count of the number of button clicks is incremented.
Deploy and debug your .NET MAUI app
on Windows
Article • 11/08/2022 • 2 minutes to read

You can use your local Windows development computer to deploy and debug a .NET
Multi-platform App UI (.NET MAUI) app. This article describes how to configure
Windows to debug a .NET MAUI app.

Configure Windows
You must enable Developer Mode in Windows. Both Windows 10 and Windows 11 are
supported.

Windows 11
Developer Mode is enabled in Settings app, under Privacy & security > For developers.
To enable Developer Mode in Windows 11:

1. Open the Start Menu.


2. Type Developer settings in the search box and select it.
3. Turn on Developer Mode.
4. If you receive a warning message about Developer Mode, read it, and select Yes if
you understand the warning.

Windows 10
Developer Mode is enabled in Settings app, under Update & Security > For developers.
To enable Developer Mode in Windows 10:

1. Open the Start Menu.


2. Search for Developer settings, select it.
3. Turn on Developer Mode.
4. If you receive a warning message about Developer Mode, read it, and select Yes if
you understand the warning.
Target Windows
In Visual Studio, set the Debug Target to Framework (...) > net7.0-windows. There is a
version number in the item entry, which may or may not match the following
screenshot:
Upgrade from Xamarin to .NET
Article • 02/15/2023 • 2 minutes to read

Xamarin projects can run on .NET, starting with .NET 6, after completing an upgrade
process. This series of articles describe the process for migrating your Xamarin projects
to .NET.

) Important

To migrate an app from Xamarin to .NET:

All projects do need to become SDK-style.


Projects don't need to be rewritten.
Multi-project solutions don't need to become a multi-targeted single project.

To upgrade your Xamarin native projects to .NET, you'll first have to update the projects
to be SDK-style projects and then update your dependencies to .NET 6+. For more
information, see Upgrade Xamarin.Android, Xamarin.iOS, and Xamarin.Mac apps to .NET.

The .NET Upgrade Assistant is a command-line tool that can help you upgrade
Xamarin.Forms projects to .NET Multi-platform App UI (.NET MAUI). After running the
tool, in most cases the app will require additional effort to complete the migration. For
more information, see Upgrade a Xamarin.Forms app to .NET MAUI with the .NET
Upgrade Assistant.

Alternatively, you can manually upgrade a Xamarin.Forms project to .NET MAUI with a
two-step process:

1. Upgrade your Xamarin native projects, in your Xamarin.Forms solution, to .NET. For
more information, see Upgrade Xamarin.Android, Xamarin.iOS, and Xamarin.Mac
apps to .NET.
2. Upgrade your Xamarin.Forms library project to .NET Multi-platform App UI (.NET
MAUI). For more information, see Manually upgrade a Xamarin.Forms app to .NET
MAUI.
Upgrade Xamarin.Android, Xamarin.iOS,
and Xamarin.Mac projects to .NET
Article • 03/10/2023 • 3 minutes to read

To upgrade your Xamarin native projects to .NET, you must:

" Update your project file to be SDK-style.


" Update or replace incompatible dependencies with .NET 6+ versions.
" Compile and test your app.

For most apps, you won't need to change namespaces or undertake other rewrites.

To simplify the upgrade process, we recommend creating a new .NET project of the
same type and name as your Xamarin native project, and then copying in your code.
This is the approach outlined below.

Create a new project


In Visual Studio, create a new .NET project of the same type and name as your Xamarin
native project. For example, to upgrade from Xamarin.Android to .NET for Android select
the Android Application project template:
The new project should be given the same project and package name as your existing
project, and should be placed in a new folder. Opening the project file will confirm that
you have a .NET SDK-style project:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFramework>net6.0-android</TargetFramework>

<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>

<OutputType>Exe</OutputType>

<Nullable>enable</Nullable>

<ImplicitUsings>enable</ImplicitUsings>

<ApplicationId>com.companyname.AndroidApp2</ApplicationId>

<ApplicationVersion>1</ApplicationVersion>

<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>

</PropertyGroup>

</Project>

) Important

The target framework moniker (TFM) is what denotes the project as using .NET, in
this case .NET 6. Valid TFMs for equivalent Xamarin native projects are net6.0-
android, net6.0-ios, net6.0-macos, net6.0-tvos, and their .NET 7 equivalents.

Launch the app to confirm that your development environment can build the app.

Merge files
Copy your code and resource files from the folders of your Xamarin native app to
identical folders within your new app. You should overwrite any files of the same name.

If you have other library projects, you should add them to your new solution and add
project references to them from your new .NET project.

You'll also need to copy some project properties from your Xamarin native project to
your new .NET project, for settings like conditional compilation arguments and code
signing. Opening the projects side-by-side in separate Visual Studio instances will
enable you to compare the project properties. Alternatively, you can migrate the
settings by editing the new project file directly. For more information, see
Xamarin.Android project migration and Xamarin Apple project migration.

Update dependencies
Xamarin native NuGet packages are not compatible with .NET 6+ unless they have been
recompiled using .NET TFMs. You can confirm a package is .NET 6+ compatible by
looking at the Frameworks tab on NuGet for the package you're using, and checking
that it lists one of the compatible frameworks shown in the following table:

Compatible frameworks Incompatible frameworks

net6.0-android, net7.0-android monoandroid, monoandroid10.0

net6.0-ios, net7.0-ios monotouch, xamarinios, xamarinios10

net6.0-macos, net6.0-macos monomac, xamarinmac, xamarinmac20

net6.0-tvos, net7.0-tvos xamarintvos

xamarinwatchos

7 Note

.NET Standard libraries that have no dependencies on the incompatible frameworks


listed below are still compatible with .NET 6+.

If a package on NuGet indicates compatibility with any of the net6 or newer


frameworks above, regardless of also including incompatible frameworks, then the
package is compatible. Compatible NuGet packages can be added to your .NET native
project using the NuGet package manager in Visual Studio.

If you can't find a .NET 6+ compatible version of a NuGet package you should:

Recompile the package with .NET TFMs, if you own the code.
Look for a preview release of a .NET 6+ version of the package.
Replace the dependency with a .NET 6+ compatible alternative.

For information about migrating Xamarin.Essentials code in a .NET for Android or .NET
for iOS app, see Migrate Xamarin.Essentials code in .NET for Android and .NET for iOS
apps.

Compile and troubleshoot


Once your dependencies are resolved and your code and resource files are added to
your .NET native project, you should build your project. Any errors will guide you
towards next steps.
 Tip

Delete all bin and obj folders from all projects before opening and building
projects in Visual Studio, particularly when changing .NET versions.
Delete the Resource.designer.cs generated file from the Android project.
Xamarin.Android project migration
Article • 02/15/2023 • 5 minutes to read

A .NET 7 project for a .NET for Android app is similar to the following example:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFramework>net7.0-android</TargetFramework>

<OutputType>Exe</OutputType>

</PropertyGroup>

</Project>

For a library project, omit the $(OutputType) property completely or specify Library as
the property value.

.NET configuration files


There's no support for configuration files such as Foo.dll.config or Foo.exe.config in
.NET for Android projects. <dllmap> configuration elements are not supported in
.NET Core at all, and other element types for compatibility packages like
System.Configuration.ConfigurationManager have never been supported in Android
projects.

Changes to MSBuild properties


The $(AndroidSupportedAbis) property shouldn't be used:

XML

<PropertyGroup>

<!-- Used in Xamarin.Android projects -->

<AndroidSupportedAbis>armeabi-v7a;arm64-
v8a;x86;x86_64</AndroidSupportedAbis>

</PropertyGroup>

Instead, the $(AndroidSupportedAbis) property should be replaced with .NET runtime


identifiers:

XML
<PropertyGroup>

<!-- Used in .NET for Android projects -->

<RuntimeIdentifiers>android-arm;android-arm64;android-x86;android-
x64</RuntimeIdentifiers>

</PropertyGroup>

For more information about runtime identifiers, see .NET RID Catalog.

The following table shows other MSBuild properties that have changed in .NET for
Android:

Property Comments

$(AndroidUseIntermediateDesignerFile) True by default.

$(AndroidBoundExceptionType) System by default. This will alter the types of


exceptions thrown from various methods to better
align with existing .NET 6+ semantics, at the cost of
compatibility with Xamarin.Android. For more
information, see Some of the new wrapped Java
exceptions use BCL exceptions that differ from the
related BCL types .

$(AndroidClassParser) class-parse by default. jar2xml isn't supported.

$(AndroidDexTool) d8 by default. dx isn't supported.

$(AndroidCodegenTarget) XAJavaInterop1 by default. XamarinAndroid isn't


supported.

$(AndroidManifest) Defaults to AndroidManifest.xml in the root of projects


because Properties\AssemblyInfo.cs is no longer used
in SDK-style projects. Properties\AndroidManifest.xml
will also be detected and used if it exists, to ease
migration.

$(DebugType) portable by default. full and pdbonly aren't


supported.

$(MonoSymbolArchive) False , since mono-symbolicate isn't supported.

In addition, if Java binding is enabled with @(InputJar) , @(EmbeddedJar) , or


@(LibraryProjectZip) , then the $(AllowUnsafeBlocks) property will default to True .

7 Note

Referencing an Android Wear project from an Android app isn't supported.


Default file inclusion
Default .NET for Android related file globbing behavior is defined in AutoImport.props .
This behavior can be disabled for Android items by setting
$(EnableDefaultAndroidItems) to false , or all default item inclusion behavior can be
disabled by setting $(EnableDefaultItems) to false . For more information, see
Workload props files .

Runtime behavior
There are behavioral changes to the String.IndexOf() method in .NET 5+ on different
platforms. For more information, see .NET globalization and ICU.

Linker
.NET 5+ has new settings for the linker:

<PublishTrimmed>true</PublishTrimmed>

<TrimMode>link</TrimMode> , which enables member-level trimming.

For more information, see Trimming options.

In .NET for Android projects by default, Debug builds will not use the linker and Release
builds will set PublishTrimmed=true and TrimMode=link . TrimMode=copyused is the default
for the .NET SDK but isn't appropriate for mobile apps. However, you can still opt into
TrimMode=copyused if required.

If the legacy AndroidLinkMode setting is used, both SdkOnly and Full will default to
equivalent linker settings:

<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>

With AndroidLinkMode=SdkOnly only BCL and SDK assemblies marked with %(Trimmable)
will be linked at the member level. AndroidLinkMode=Full will set %(TrimMode)=link on
all .NET assemblies.

 Tip

You should migrate to the new linker settings, because the AndroidLinkMode setting
will eventually be deprecated.
Ahead-of-Time compilation
$(RunAOTCompilation) is the new MSBuild property for enabling Ahead-of-Time (AoT)
compilation. This is the same property used for Blazor WASM. The $(AotAssemblies)
property also enables AOT, in order to help with migration from Xamarin.Android
projects to .NET for Android projects.

 Tip

You should migrate to the new $(RunAOTCompilation) property, because


$(AotAssemblies) is deprecated in .NET 7.

Release builds will default to the following AOT property values:

XML

<PropertyGroup Condition="'$(Configuration)' == 'Release'">

<RunAOTCompilation>true</RunAOTCompilation>

<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>

</PropertyGroup>

This is the behavior when the $(RunAOTCompilation) and $(AndroidEnableProfiledAot)


properties are unset, and chooses the optimal settings for startup time and app size.

To disable AOT, you need to explicitly set the $(RunAOTCompilation) and


$(AndroidEnableProfiledAot) properties to false :

XML

<PropertyGroup Condition="'$(Configuration)' == 'Release'">

<RunAOTCompilation>false</RunAOTCompilation>

<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>

</PropertyGroup>

Supported encodings
If your Xamarin.Android app uses certain international codesets, they have to be
specified explicitly in your project file using the Mandroidl18n MSBuild property, so that
the linker can include supporting resources. For more information about this build
property, see MAndroidl18n.
However, the Mandroidl18n MSBuild property isn't supported in .NET for Android apps.
Instead, support is provided by the System.TextEncoding.CodePages NuGet package.
For more information, see CodePagesEncodingProvider.

.NET CLI
.NET for Android supports using .NET command-line interface (.NET CLI) to create, build,
publish, and run Android apps.

dotnet new
dotnet new can be used to create new .NET for Android projects and items using project

templates and item templates that are named following the patterns and naming of
existing .NET templates:

Template Short Name Language Tags

Android Activity template android-activity C# Android

Android Java Library Binding android-bindinglib C# Android

Android Layout template android-layout C# Android

Android Class library androidlib C# Android

Android Application android C# Android

The following examples show using dotnet new to create different types of .NET for
Android projects:

.NET CLI

dotnet new android --output MyAndroidApp --packageName


com.mycompany.myandroidapp

dotnet new androidlib --output MyAndroidLibrary

dotnet new android-bindinglib --output MyJavaBinding

Once .NET for Android projects have been created, item templates can be used to add
items to the projects:

.NET CLI

dotnet new android-activity --name LoginActivity --namespace MyAndroidApp

dotnet new android-layout --name MyLayout --output Resources/layout

dotnet build & publish


For .NET for Android, dotnet build produces a runnable app. This means creating an
.apk or .aab file during the build process, and reordering MSBuild tasks from the .NET

SDK so that they run during the build. Therefore, .NET for Android does the following
during a build:

Run aapt to generate Resource.designer.cs and potentially emit build errors for
issues in @(AndroidResource) files.
Compile C# code.
Run the ILLink MSBuild target for linking.
Generate java stubs, and AndroidManifest.xml .
Compile java code via javac .
Convert java code to .dex via d8/r8.
Create an .apk or .aab and sign it.

dotnet publish is reserved for publishing an app for Google Play and other distribution

mechanisms such as ad-hoc. It also signs the .apk or .aab with different keys.

7 Note

Behavior inside IDEs will differ. The Build target will not produce an .apk file if
$(BuildingInsideVisualStudio) is true . IDEs will call the Install target for

deployment, which will produce the .apk file. This behavior matches
Xamarin.Android.

dotnet run
dotnet run can be used to launch apps on a device or emulator via the --project

argument:

.NET CLI

dotnet run --project HelloAndroid.csproj

Alternatively, you could use the Run MSBuild target:

.NET CLI

dotnet build HelloAndroid.csproj -t:Run

See also
Binding projects
Xamarin Apple project migration
Article • 02/15/2023 • 2 minutes to read

A .NET 7 project for a .NET for iOS app is similar to the following example:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFramework>net7.0-ios</TargetFramework>

<OutputType>Exe</OutputType>

<Nullable>enable</Nullable>

<ImplicitUsings>true</ImplicitUsings>

<SupportedOSPlatformVersion>13.0</SupportedOSPlatformVersion>

</PropertyGroup>

</Project>

For a library project, omit the $(OutputType) property completely or specify Library as
the property value.

Changes to MSBuild properties


The following table shows how to map properties in Xamarin Apple projects to .NET
projects:

Property Description .NET Project conversion

MtouchExtraArgs Additional arguments to Some arguments are Copy


mtouch. still applicable, some
arguments are not.

MtouchArch App architecture(s). N/A Convert to


RuntimeIdentifier . For
more information, see
Convert to
RuntimeIdentifier.

XamMacArch App architecture(s). N/A Convert to


RuntimeIdentifier . For
more information, see
Convert to
RuntimeIdentifier.
Property Description .NET Project conversion

HttpClientHandler The default UseNativeHttpHandler Convert to


HttpClientHandler . UseNativeHttpHandler .
For more information,
see Convert to
UseNativeHttpHandler.

MtouchHttpClientHandler The default UseNativeHttpHandler Convert to


MtouchHttpClientHandler. UseNativeHttpHandler .
For more information,
see Convert to
UseNativeHttpHandler.

EnableCodeSigning If code signing is enabled. Copy

CodeSigningKey Specifies the code signing Rename to


key. CodesignKey

CodesignKey Specifies the code signing Copy


key.

CodesignProvision Specifies the provisioning Copy


profile.

CodesignEntitlements The path to the Copy


entitlements file.

CodesignExtraArgs Extra code signing Copy


arguments.

PackageSigningKey Specifies the code signing Copy


key to sign the package.

PackagingExtraArgs Specifies the extra Copy


arguments to the
packaging tool.

ProductDefinition The path to the product Copy


definition file to use when
packaging.

MtouchEnableSGenConc Rename to
EnableSGenConc .

EnableSGenConc Copy

Convert to RuntimeIdentifier
The following table shows how to convert the MtouchArch and XamMacArch properties to
the RuntimeIdentifier property when migrating a Xamarin.iOS project to .NET for iOS:

Value RuntimeIdentifier RuntimeIdentifiers

ARMv7 ios-arm

ARMv7s ios-arm

ARMv7+ARMv7s ios-arm

ARM64 ios-arm64

ARMv7+ARM64 ios-arm,ios-arm64

ARMv7+ARMv7s+ARM64 ios-arm,ios-arm64

x86_64 iossimulator-x64

i386 iossimulator-x86

x86_64+i386 iossimulator-x86,iossimulator-x64

The following table shows how to convert the MtouchArch and XamMacArch properties to
the RuntimeIdentifier property when migrating a Xamarin.Mac project to .NET for
macOS+:

Property RuntimeIdentifier

x86_64 osx-x64

The following table shows how to convert the MtouchArch and XamMacArch properties to
the RuntimeIdentifier property when migrating a Xamarin.tvOS project to .NET for
tvOS:

Property RuntimeIdentifier

ARM64 tvos-arm64

x86_64 tvossimulator-x64

For more information about the RuntimeIdentifier property, see RuntimeIdentifier. For
more information about runtime identifiers, see .NET RID Catalog.

Convert to UseNativeHttpHandler
The following table shows how to convert the HttpClientHandler and
MtouchHttpClientHandler properties to the UseNativeHttpHandler property when
migrating a Xamarin Apple project to .NET 6+:

Value UseNativeHttpHandler

HttpClientHandler false

NSUrlSessionHandler don't set

CFNetworkHandler don't set

Changes to other items


The following table shows how to map other items in Xamarin Apple projects to .NET
projects:

Item Description .NET Project conversion

LinkDescription Additional XML files to the managed linker. Identical Copy

Changes to Info.plist
Some values have moved from Info.plist to the project file.

MinimumOSVersion and LSMinimumSystemVersion


The MinimumOSVersion and LSMinimumSystemVersion properties should be converted to
the SupportedOSPlatformVersion project in .NET 6+ projects. For more information, see
Ensure MinimumOSVersion is consistent with SupportedOSPlatformVersion .

See also
Project file properties
Migrate Xamarin.Essentials code in .NET
for Android and .NET for iOS apps
Article • 03/10/2023 • 4 minutes to read

Xamarin.Essentials is a fundamental library for nearly every Xamarin app, and its
functionality is now part of .NET Multi-platform App UI (.NET MAUI).

The process to use .NET MAUIs native device functionality, that was formerly known as
Xamarin.Essentials, in a .NET for Android or .NET for iOS app, is:

1. Remove the Xamarin.Essentials NuGet package from your .NET for Android or .NET
for iOS app.
2. Set the $(UseMauiEssentials) build property to true in your project file. For more
information, see Modify your project file.
3. Initialize the "essentials" functionality by calling the Platform.Init method. For
more information, see Initialize the platform.
4. Perform additional setup, if required. For more information, see Perform additional
setup.
5. Add using directives for the required functionality. For more information, see Add
using directives.

Modify your project file


To use .NET MAUIs native device functionality in a .NET for Android or .NET for iOS app,
modify your project file and set the $(UseMauiEssentials) build property to true .

Android

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFramework>net7.0-android</TargetFramework>

...

<UseMauiEssentials>true</UseMauiEssentials>

</PropertyGroup>

</Project>

Initialize the platform


Android

In any Activity that's launched you must call the Platform.Init method, which is in
the Microsoft.Maui.ApplicationModel namespace, from the OnCreate method:

C#

using Android.Content.PM;

using Android.Runtime;

using Microsoft.Maui.ApplicationModel;

namespace MyAndroidApp;

[Activity(Label = "@string/app_name", MainLauncher = true)]

public class MainActivity : Activity

protected override async void OnCreate(Bundle? savedInstanceState)

base.OnCreate(savedInstanceState);

Platform.Init(this, savedInstanceState);

// ...

The Platform.Init method requires an Application argument, or a Activity


argument and a Bundle argument.

Perform additional setup


The static Platform class contains platform-specific helpers.

Android

Member Purpose

ActivityStateChanged An event that's raised when any Activity's state changes.

AppContext A property that gets the Context object that represents the
current app context.

CurrentActivity A property that gets the current Activity object that


represents the current activity.

Intent A static class that contains the ActionAppAction string, which


is the identifier for the Intent used by app actions.
Member Purpose

OnNewIntent Pass an Intent from an activity's overridden method, when


invoking an app action.

OnResume Pass an Activity from an activity's overridden method, when


an Activity is resumed as part of invoking an app action.

OnRequestPermissionsResult Pass permission request results from an activity's overridden


method, for handling internal permission requests.

WaitForActivityAsync Wait for an Activity to be created or become active.

To access the current Context or Activity for the running app:

C#

var context = Platform.AppContext;

// Current Activity or null if not initialized or not started.

var activity = Platform.CurrentActivity;

If there's a situation where the Activity is needed, but the app hasn't fully started,
call the WaitForActivityAsync method:

C#

var activity = await Platform.WaitForActivityAsync();

To handle runtime permission requests, override the OnRequestPermissionsResult


method in every Activity and call the Platform.OnRequestPermissionsResult method
from it:

C#

public override void OnRequestPermissionsResult(int requestCode,


string[] permissions, Permission[] grantResults)

Platform.OnRequestPermissionsResult(requestCode, permissions,
grantResults);

base.OnRequestPermissionsResult(requestCode, permissions,
grantResults);

In addition to getting the current Activity, you can also register for lifecycle events:

C#
protected override void OnCreate(Bundle bundle)

base.OnCreate(bundle);

Platform.Init(this, bundle);

Platform.ActivityStateChanged += Platform_ActivityStateChanged;

protected override void OnDestroy()

base.OnDestroy();

Platform.ActivityStateChanged -= Platform_ActivityStateChanged;

void Platform_ActivityStateChanged(object sender,


ActivityStateChangedEventArgs e) =>

Toast.MakeText(this, e.State.ToString(), ToastLength.Short).Show();

Activity states are:

Created
Resumed
Paused
Destroyed
SaveInstanceState
Started
Stopped

Add using directives


The implicit global using directives for .NET for iOS and .NET for Android don't include
the namespaces for .NET MAUIs native device functionality. Therefore, using directives
for the Xamarin.Essentials namespace should be replaced with using directives for the
namespace that contains the required functionality:

Namespace Purpose

Microsoft.Maui.ApplicationModel Application model functionality, including


app actions, permissions, and version
tracking.

Microsoft.Maui.ApplicationModel.Communication Communication functionality, including


contacts, email, and networking.
Namespace Purpose

Microsoft.Maui.Devices Device functionality, including battery,


sensors, flashlight, and haptic feedback.

Microsoft.Maui.Media Media functionality, including media picking,


and text-to-speech.

Microsoft.Maui.ApplicationModel.DataTransfer Sharing functionality, including the clipboard,


and file sharing.

Microsoft.Maui.Storage Storage functionality, including file picking,


and secure storage.

For more information about the functionality in each namespace, see Platform
integration.
Upgrade a Xamarin.Forms app to .NET
MAUI with the .NET Upgrade Assistant
Article • 04/12/2023

The .NET Upgrade Assistant is a command-line tool that will help you upgrade
Xamarin.Forms projects to .NET Multi-platform App UI (.NET MAUI) by converting the
solution's project file and by performing common code updates. Specifically, the tool
will:

Convert the Xamarin.Forms class library project, Xamarin.iOS project, and


Xamarin.Android project to SDK-style projects.
Update the target framework in project files to net7.0-android and net7.0-ios, as
required.
Set <UseMaui>true</UseMaui> in project files.
Add additional project properties, and remove project properties that aren't
required.
Add and remove specific NuGet packages:
Remove the Xamarin.Forms and Xamarin.Essentials NuGet packages.
Replace the Xamarin.CommunityToolkit NuGet package with the .NET MAUI
Community Toolkit NuGet package.
Replace Xamarin.Forms compatible versions of the SkiaSharp NuGet packages
with .NET MAUI compatible versions, if used.
Remove references to the Xamarin.Essentials namespace, and replace the
Xamarin.Forms namespace with the Microsoft.Maui and Microsoft.Maui.Controls
namespaces.

After running the tool, additional effort will be required to complete the migration.

7 Note

The .NET Upgrade Assistant for .NET MAUI doesn't support upgrading UWP
projects, iOS extension projects, or binding projects.

For more information about .NET Upgrade Assistant, including the other app types it
can convert, see Overview of the .NET Upgrade Assistant.

Get started
.NET Upgrade Assistant is currently only available for Windows, and only works with
Xamarin.Forms projects. To use it, your Xamarin.Forms project must use Xamarin.Forms
4.8 or higher. However, for best success we recommend that your Xamarin.Forms
project uses Xamarin.Forms 5.0, and .NET Standard 2.0 or higher.

) Important

The .NET Upgrade Assistant for .NET MAUI is still under development. Please file
feedback so we can continue to improve this tool.

.NET Upgrade Assistant will make a backup of your solution, but we recommend using
source control. When using source control you may add the --skip-backup parameter
to bypass the backup and speed up the upgrade process.

Installation
Install the .NET Upgrade Assistant globally with the following command:

.NET CLI

dotnet tool install -g upgrade-assistant

Similarly, because the .NET Upgrade Assistant is installed as a .NET tool, it can be easily
updated by running:

.NET CLI

dotnet tool update -g upgrade-assistant

) Important

Installing this tool may fail if you've configued additional NuGet feed source. Use
the --ignore-failed-sources parameter to treat those failures as warnings instead
of errors:

.NET CLI

dotnet tool install -g --ignore-failed-sources upgrade-assistant

Run upgrade-assistant
Open a terminal and navigate to the folder where the target project or solution is
located. Run the upgrade-assistant upgrade command, passing in the name of the
project or solution you're upgrading:

.NET CLI

upgrade-assistant upgrade <sln or csproj> --non-interactive

This command runs the tool in non-interactive mode. It will update all eligible projects
in the solution and dependent projects.

Next steps
Manual migration
Manually upgrade a Xamarin.Forms app to
.NET MAUI
Article • 04/03/2023

Upgrading a Xamarin.Forms app to a .NET Multi-platform App UI (.NET MAUI) app follows the same
steps as a Xamarin.Android and Xamarin.iOS project, with additional steps to take advantage of
changes in .NET MAUI.

This article describes how to manually migrate a Xamarin.Forms library project to a .NET MAUI library
project. Before you do this, you must update your Xamarin.Forms platform projects to be SDK-style
projects. SDK-style projects are the same project format used by all .NET workloads, and compared to
many Xamarin projects are much less verbose. For information about updating your app projects, see
Upgrade Xamarin.Android, Xamarin.iOS, and Xamarin.Mac apps to .NET, Xamarin.Android project
migration and Xamarin Apple project migration.

To migrate a Xamarin.Forms library project to a .NET MAUI library project, you must:

" Update your project file to be SDK-style.


" Update namespaces.
" Address any API changes.
" Configure .NET MAUI.
" Upgrade or replace incompatible dependencies with .NET 6+ versions.
" Compile and test your app.

To simplify the upgrade process, we recommend creating a new .NET MAUI library project of the same
name as your Xamarin.Forms library project, and then copying in your code. This is the approach
outlined below.

Create a new project


In Visual Studio, create a new .NET MAUI class library project of the same name as your Xamarin.Forms
library project. This project will host the code from your Xamarin.Forms library project. Opening the
project file will confirm that you have a .NET SDK-style project:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFrameworks>net7.0;net7.0-android;net7.0-ios;net7.0-
maccatalyst</TargetFrameworks>

<TargetFrameworks
Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net7.0-
windows10.0.19041.0</TargetFrameworks>

<!-- Uncomment to also build the tizen app. You will need to install tizen by
following this: https://github.com/Samsung/Tizen.NET -->

<!-- <TargetFrameworks>$(TargetFrameworks);net7.0-tizen</TargetFrameworks> -->

<UseMaui>true</UseMaui>

<SingleProject>true</SingleProject>

<ImplicitUsings>enable</ImplicitUsings>

<SupportedOSPlatformVersion
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) ==
'ios'">11.0</SupportedOSPlatformVersion>

<SupportedOSPlatformVersion
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) ==
'maccatalyst'">13.1</SupportedOSPlatformVersion>

<SupportedOSPlatformVersion
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) ==
'android'">21.0</SupportedOSPlatformVersion>

<SupportedOSPlatformVersion
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) ==
'windows'">10.0.17763.0</SupportedOSPlatformVersion>

<TargetPlatformMinVersion
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) ==
'windows'">10.0.17763.0</TargetPlatformMinVersion>

<SupportedOSPlatformVersion
Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) ==
'tizen'">6.5</SupportedOSPlatformVersion>

</PropertyGroup>

</Project>

In your platform projects, add a reference to this new library project. Then copy your Xamarin.Forms
library files into the .NET MAUI library project.

Namespace changes
Namespaces have changed in the move from Xamarin.Forms to .NET MAUI, and Xamarin.Essentials
features are now part of .NET MAUI. To make namespace updates, perform a find and replace for the
following namespaces:

Xamarin.Forms namespace .NET MAUI namespace(s)

Xamarin.Forms Microsoft.Maui and Microsoft.Maui.Controls

Xamarin.Forms.DualScreen Microsoft.Maui.Controls.Foldable

Xamarin.Forms.Maps Microsoft.Maui.Controls.Maps and Microsoft.Maui.Maps

Xamarin.Forms.PlatformConfiguration Microsoft.Maui.Controls.PlatformConfiguration

Xamarin.Forms.PlatformConfiguration.AndroidSpeci Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpe
fic cific

Xamarin.Forms.PlatformConfiguration.AndroidSpeci Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpe
fic.AppCompat cific.AppCompat

Xamarin.Forms.PlatformConfiguration.TizenSpecific Microsoft.Maui.Controls.PlatformConfiguration.TizenSpecifi
c

Xamarin.Forms.PlatformConfiguration.WindowsSpec Microsoft.Maui.Controls.PlatformConfiguration.WindowsSp
ific ecific

Xamarin.Forms.PlatformConfiguration.iOSSpecific Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific

Xamarin.Forms.Shapes Microsoft.Maui.Controls.Shapes
Xamarin.Forms namespace .NET MAUI namespace(s)

Xamarin.Forms.StyleSheets Microsoft.Maui.Controls.StyleSheets

Xamarin.Forms.Xaml Microsoft.Maui.Controls.Xaml

.NET MAUI projects make use of implicit global using directives. This enables you to remove using
directives for the Xamarin.Essentials namespace, without having to replace them with the equivalent
.NET MAUI namespaces.

In addition, the default XAML namespace has changed from http://xamarin.com/schemas/2014/forms


in Xamarin.Forms to http://schemas.microsoft.com/dotnet/2021/maui in .NET MAUI. Therefore, you
should replace all occurrences of xmlns="http://xamarin.com/schemas/2014/forms" with
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" .

API changes
Some APIs have changed in the move from Xamarin.Forms to .NET MAUI. This is multiple reasons
including removing duplicate functionality caused by Xamarin.Essentials becoming part of .NET MAUI,
and ensuring that APIs follow .NET naming guidelines. The following sections discuss these changes.

Color changes
In Xamarin.Forms, the Xamarin.Forms.Color struct lets you construct Color objects using double values,
and provides named colors, such as Xamarin.Forms.Color.AliceBlue. In .NET MAUI, this functionality has
been separated into the Microsoft.Maui.Graphics.Color class, and the Microsoft.Maui.Graphics.Colors
class.

The Microsoft.Maui.Graphics.Color class, in the Microsoft.Maui.Graphics namespace, lets you construct


Color objects using float values, byte values, and int values. The Microsoft.Maui.Graphics.Colors
class, which is also in the Microsoft.Maui.Graphics namespace, largely provides the same named
colors.

The following table shows the API changes between the Xamarin.Forms.Color struct and the
Microsoft.Maui.Graphics.Color class:

Xamarin.Forms API .NET MAUI API Comment

Xamarin.Forms.Color.R Microsoft.Maui.Graphics.Color.Red

Xamarin.Forms.Color.G Microsoft.Maui.Graphics.Color.Green

Xamarin.Forms.Color.B Microsoft.Maui.Graphics.Color.Blue

Xamarin.Forms.Color.A Microsoft.Maui.Graphics.Color.Alpha

Xamarin.Forms.Color.Hue Microsoft.Maui.Graphics.Color.GetHue Xamarin.Forms property replaced with


a method in .NET MAUI.
Xamarin.Forms API .NET MAUI API Comment

Xamarin.Forms.Color.Saturation Microsoft.Maui.Graphics.Color.GetSaturation Xamarin.Forms property replaced with


a method in .NET MAUI.

Xamarin.Forms.Color.Luminosity Microsoft.Maui.Graphics.Color.GetLuminosity Xamarin.Forms property replaced with


a method in .NET MAUI.

Xamarin.Forms.Color.Default No .NET MAUI equivalent. Instead,


Microsoft.Maui.Graphics.Color objects
default to null .

Xamarin.Forms.Color.Accent No .NET MAUI equivalent.

Xamarin.Forms.Color.FromHex Microsoft.Maui.Graphics.Color.FromArgb Microsoft.Maui.Graphics.Color.FromHex


is obsolete and will be removed in a
future release.

In addition, all of the numeric values in a Microsoft.Maui.Graphics.Color are float , rather than double
as used in Xamarin.Forms.Color.

7 Note

Unlike Xamarin.Forms, a Microsoft.Maui.Graphics.Color doesn't have an implicit conversion to


System.Drawing.Color.

Layout changes
The following table lists the layout APIs that have been removed in the move from Xamarin.Forms to
.NET MAUI:

Xamarin.Forms .NET MAUI API Comments


API

Xamarin.Forms.Abs The Add overload that accepts 3 arguments isn't present in .NET MAU
oluteLayout.IAbsol I.
uteList<T>.Add

Xamarin.Forms.Gri The Add overload that accepts 5 arguments isn't present in .NET MAU
d.IGridList<T>.Add I.

Xamarin.Forms.Gri No .NET MAUI equivalent.


d.IGridList<T>.Add
Horizontal

Xamarin.Forms.Gri No .NET MAUI equivalent.


d.IGridList<T>.Add
Vertical

Xamarin.Forms.Rela Microsoft.Maui.Con In .NET MAUI, RelativeLayout only exists as a compatibility control fo


tiveLayout trols.Compatibility. r users migrating from Xamarin.Forms. Use Grid instead, or add the x
RelativeLayout mlns for the compatibility namespace.
In addition, adding children to a layout in code in Xamarin.Forms is accomplished by adding the
children to the layout's Children collection:

C#

Grid grid = new Grid();

grid.Children.Add(new new Label { Text = "Hello world" });

In .NET MAUI, the Children collection is for internal use by .NET MAUI and shouldn't be manipulated
directly. Therefore, in code children should be added directly to the layout:

C#

Grid grid = new Grid();

grid.Add(new new Label { Text = "Hello world" });

) Important

Any Add layout extension methods, such as GridExtensions.Add, are invoked on the layout rather
than the layouts Children collection.

You may notice when running your upgraded .NET MAUI app that layout behavior is different. For
more information, see Layout behavior changes from Xamarin.Forms.

Custom layout changes


The process for creating a custom layout in Xamarin.Forms involves creating a class that derives from
Layout<View> , and overriding the OnMeasure and LayoutChildren methods. For more information, see
Create a custom layout in Xamarin.Forms.

The process for creating a custom layout in .NET MAUI involves creating an ILayoutManager
implementation, and overriding the Measure and ArrangeChildren methods:

The Measure override should call Measure on each IView in the layout, and should return the
total size of the layout given the constraints.
The ArrangeChildren override should determine where each IView should be placed within the
given bounds, and should call Arrange on each IView with its appropriate bounds. The return
value should be the actual size of the layout.

For more information, see Custom layout examples .

Device changes
Xamarin.Forms has a Xamarin.Forms.Device class that helps you to interact with the device and
platform the app is running on. The equivalent class in .NET MAUI, Microsoft.Maui.Controls.Device, is
deprecated and its functionality is replaced by multiple types.
The following table shows the .NET MAUI replacements for the functionality in the
Xamarin.Forms.Device class:

Xamarin.Forms API .NET MAUI API Comments

Xamarin.Forms.Device.An Microsoft.Maui.Devices.DevicePlatform.Androi
droid d

Xamarin.Forms.Device.iOS Microsoft.Maui.Devices.DevicePlatform.iOS

Xamarin.Forms.Device.GT No .NET MAUI equivalent.


K

Xamarin.Forms.Device.ma No .NET MAUI equivalent. Instead,


cOS use Microsoft.Maui.Devices.DeviceP
latform.MacCatalyst.

Xamarin.Forms.Device.Tiz Microsoft.Maui.Devices.DevicePlatform.Tizen
en

Xamarin.Forms.Device.U Microsoft.Maui.Devices.DevicePlatform.WinUI
WP

Xamarin.Forms.Device.WP No .NET MAUI equivalent.


F

Xamarin.Forms.Device.Fla No .NET MAUI equivalent.


gs

Xamarin.Forms.Device.Flo Microsoft.Maui.ApplicationModel.AppInfo.Req
wDirection uestedLayoutDirection

Xamarin.Forms.Device.Idi Microsoft.Maui.Devices.DeviceInfo.Idiom
om

Xamarin.Forms.Device.IsIn Microsoft.Maui.Dispatching.Dispatcher.IsDispat
vokeRequired chRequired

Xamarin.Forms.Device.OS Microsoft.Maui.Devices.DeviceInfo.Platform

Xamarin.Forms.Device.Ru Microsoft.Maui.Devices.DeviceInfo.Platform
ntimePlatform

Xamarin.Forms.Device.Be Microsoft.Maui.ApplicationModel.MainThread.
ginInvokeOnMainThread BeginInvokeOnMainThread

Xamarin.Forms.Device.Get Microsoft.Maui.ApplicationModel.MainThread.
MainThreadSynchronizati GetMainThreadSynchronizationContextAsync
onContextAsync

Xamarin.Forms.Device.Get No .NET MAUI equivalent.


NamedColor

Xamarin.Forms.Device.Get No .NET MAUI equivalent.


NamedSize

Xamarin.Forms.Device.Inv Microsoft.Maui.Controls.VisualElement.Invalida
alidate teMeasure
Xamarin.Forms API .NET MAUI API Comments

Xamarin.Forms.Device.Inv Microsoft.Maui.ApplicationModel.MainThread.I
okeOnMainThreadAsync nvokeOnMainThreadAsync

Xamarin.Forms.Device.On Microsoft.Maui.Devices.DeviceInfo.Platform
Platform

Xamarin.Forms.Device.Op Microsoft.Maui.ApplicationModel.Launcher.Op
enUri enAsync

Xamarin.Forms.Device.Set No .NET MAUI equivalent.


Flags

Xamarin.Forms.Device.Set Microsoft.Maui.Controls.Window.FlowDirection
FlowDirection

Xamarin.Forms.Device.Sta Microsoft.Maui.Dispatching.DispatcherExtensio
rtTimer ns.StartTimer or Microsoft.Maui.Dispatching.Di
spatcher.DispatchDelayed

Map changes
In Xamarin.Forms, the Map control and associated types are in the Xamarin.Forms.Maps namespace. In
.NET MAUI, this functionality has moved to the Microsoft.Maui.Controls.Maps and
Microsoft.Maui.Maps namespaces. Some properties have been renamed and some types have been
replaced with equivalent types from Xamarin.Essentials.

The following table shows the .NET MAUI replacements for the functionality in the
Xamarin.Forms.Maps namespace:

Xamarin.Forms API .NET MAUI API Comment

Xamarin.Forms.Maps.Map. Microsoft.Maui.Controls.M
HasScrollEnabled aps.Map.IsScrollEnabled

Xamarin.Forms.Maps.Map. Microsoft.Maui.Controls.M
HasZoomEnabled aps.Map.IsZoomEnabled

Xamarin.Forms.Maps.Map.T Microsoft.Maui.Controls.M
rafficEnabled aps.Map.IsTrafficEnabled

Xamarin.Forms.Maps.Map. No .NET MAUI equivalent.


MoveToLastRegionOnLayo
utChange

Xamarin.Forms.Maps.Pin.Id Microsoft.Maui.Controls.M
aps.Pin.MarkerId

Xamarin.Forms.Maps.Pin.Po Microsoft.Maui.Controls.M
sition aps.Pin.Location

Xamarin.Forms.Maps.MapC Microsoft.Maui.Controls.M
lickedEventArgs.Position aps.MapClickedEventArgs.
Location
Xamarin.Forms API .NET MAUI API Comment

Xamarin.Forms.Maps.Positi Microsoft.Maui.Devices.Se Members of type Xamarin.Forms.Maps.Position have c


on nsors.Location hanged to the Microsoft.Maui.Devices.Sensors.Locatio
n type.

Xamarin.Forms.Maps.Geoc Microsoft.Maui.Devices.Se Members of type Xamarin.Forms.Maps.Geocoder have


oder nsors.Geocoding changed to the Microsoft.Maui.Devices.Sensors.Geoco
ding type.

.NET MAUI has two Map types - Microsoft.Maui.Controls.Maps.Map and


Microsoft.Maui.ApplicationModel.Map. Because the Microsoft.Maui.ApplicationModel namespace is
one of .NET MAUI's global using directives, when using the Microsoft.Maui.Controls.Maps.Map
control from code you'll have to fully qualify your Map usage or use a using alias.

In XAML, an xmlns namespace definition should be added for the Map control. While this isn't
required, it prevents a collision between the Polygon and Polyline types, which exist in both the
Microsoft.Maui.Controls.Maps and Microsoft.Maui.Controls.Shapes namespaces. For more information,
see Display a map.

Other changes
A small number of other APIs have been consolidated in the move from Xamarin.Forms to .NET MAUI.
The following table shows these changes:

Xamarin.Forms .NET MAUI API Comments


API

Xamarin.Forms.A Microsoft.Maui.Stora
pplication.Proper ge.Preferences
ties

Xamarin.Forms.B Microsoft.Maui.Cont
utton.Image rols.Button.ImageSo
urce

Xamarin.Forms.Fr Microsoft.Maui.Cont
ame.OutlineColor rols.Frame.BorderCol
or

Xamarin.Forms.IQ Microsoft.Maui.Cont In Xamarin.Forms, the ApplyQueryAttributes method accepts an IDict


ueryAttributable. rols.IQueryAttributab ionary<string, string> argument. In .NET MAUI, the ApplyQueryAttri
ApplyQueryAttrib le.ApplyQueryAttribu butes method accepts an IDictionary<string, object> argument.
utes tes

Xamarin.Forms.M Microsoft.Maui.Cont Xamarin.Forms.MenuItem.Icon is the base class for Xamarin.Forms.Too


enuItem.Icon rols.MenuItem.IconI lbarItem, and so ToolbarItem.Icon becomes ToolbarItem.IconImageSo
mageSource urce .

Xamarin.Forms.O Microsoft.Maui.Cont In Xamarin.Forms, the OrientationStateTrigger.Orientation property


rientationStateTri rols.OrientationState is of type DeviceOrientation. In .NET MAUI, the OrientationStateTrigg
gger.Orientation Trigger.Orientation er.Orientation property is of type DisplayOrientation.
Xamarin.Forms .NET MAUI API Comments
API

Xamarin.Forms.O Microsoft.Maui.Appli
SAppTheme cationModel.AppThe
me

Xamarin.Forms.S Microsoft.Maui.Cont
pan.ForegroundC rols.Span.TextColor
olor

Xamarin.Forms.To Microsoft.Maui.Cont Microsoft.Maui.Controls.MenuItem.Text is the base class for Microsoft.


olbarItem.Name rols.MenuItem.Text Maui.Controls.ToolbarItem, and so ToolbarItem.Name becomes Toolba
rItem.Text .

Native forms changes


Native forms in Xamarin.Forms has become native embedding in .NET MAUI, and uses a different
initialization approach and different extension methods to convert cross-platform controls to their
native types. For more information, see Native embedding.

Bootstrap your migrated app


When manually updating a Xamarin.Forms to .NET MAUI you will need to update each platform
project's entry point class, and then configure the bootstrapping of the .NET MAUI app.

Android project configuration


In your .NET MAUI Android project, update the MainApplication class to match the code below:

C#

using System;

using Android.App;

using Android.Runtime;

using Microsoft.Maui;

using Microsoft.Maui.Hosting;

namespace YOUR_NAMESPACE_HERE.Droid

[Application]

public class MainApplication : MauiApplication

public MainApplication(IntPtr handle, JniHandleOwnership ownership) :


base(handle, ownership)
{

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();

Also update the MainActivity class to inherit from MauiAppCompatActivity :


C#

using System;

using Microsoft.Maui;

using Android.App;

using Android.Content.PM;

using Android.Runtime;

using Android.OS;

namespace YOUR_NAMESPACE_HERE.Droid

[Activity(Label = "MyTitle", Icon = "@mipmap/icon", Theme = "@style/MainTheme",


MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize |
ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout |
ConfigChanges.SmallestScreenSize)]

public class MainActivity : MauiAppCompatActivity

protected override void OnCreate(Bundle savedInstanceState)

base.OnCreate(savedInstanceState);

Then, update your manifest file to specify that the minSdKVersion is 21, which is the minimum Android
SDK version required by .NET MAUI. This can be achieved by modifying the <uses-sdk /> node, which
is a child of the <manifest> node:

XML

<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="32" />

iOS project configuration


In your .NET MAUI iOS project, update the AppDelegate class to inherit from
MauiUIApplicationDelegate :

C#

using System;

using System.Collections.Generic;
using System.Linq;

using Microsoft.Maui;

using Foundation;

using UIKit;

namespace YOUR_NAMESPACE_HERE.iOS

[Register("AppDelegate")]

public partial class AppDelegate : MauiUIApplicationDelegate

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();

Then, update Info.plist so that MinimumOSVersion is 11.0, which is the minimum iOS version required by
.NET MAUI.

App entry point


.NET MAUI apps have a single cross-platform app entry point. Each platform entry point calls a
CreateMauiApp method on the static MauiProgram class, and returns a MauiApp.

Therefore, add a new class named MauiProgram that contains the following code:

C#

namespace YOUR_NAMESPACE_HERE;

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>();

return builder.Build();

AssemblyInfo changes
Properties that are typically set in an AssemblyInfo.cs file are now available in your SDK-style project.
We recommend migrating them from AssemblyInfo.cs to your project file in every project, and
removing the AssemblyInfo.cs file.

Optionally, you can keep the AssemblyInfo.cs file and set the GenerateAssemblyInfo property in your
project file to false :

XML

<PropertyGroup>

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

</PropertyGroup>

For more information about the GenerateAssemblyInfo property, see GenerateAssemblyInfo.

Update app dependencies


Xamarin.Forms NuGet packages are not compatible with .NET 6+ unless they have been recompiled
using .NET target framework monikers (TFMs). You can confirm a package is .NET 6+ compatible by
looking at the Frameworks tab on NuGet for the package you're using, and checking that it lists one
of the compatible frameworks shown in the following table:
Compatible frameworks Incompatible frameworks

net6.0-android, net7.0-android monoandroid, monoandroid10.0

net6.0-ios, net7.0-ios monotouch, xamarinios, xamarinios10

net6.0-maccatalyst, net7.0-maccatalyst

net6.0-windows, net7.0-windows uap10.0.16299

7 Note

.NET Standard libraries that have no dependencies on the incompatible frameworks listed below
are still compatible with .NET 6+.

If a package on NuGet indicates compatibility with any of the net6 or newer frameworks above,
regardless of also including incompatible frameworks, then the package is compatible. Compatible
NuGet packages can be added to your .NET MAUI library project using the NuGet package manager in
Visual Studio.

If you can't find a .NET 6+ compatible version of a NuGet package you should:

Recompile the package with .NET TFMs, if you own the code.
Look for a preview release of a .NET 6+ version of the package.
Replace the dependency with a .NET 6+ compatible alternative.

Compile and troubleshoot


Once your dependencies are resolved, you should build your project. Any errors will guide you
towards next steps.

 Tip

Delete all bin and obj folders from all projects before opening and building projects in Visual
Studio, particularly when changing .NET versions.
Delete the Resource.designer.cs generated file from the Android project.

The following table provides guidance for overcoming common build or runtime issues:

Issue Tip

Xamarin.* Update the namespace to its .NET MAUI equivalent. For more information, see Namespace
namespace changes.
doesn't exist.

API doesn't Update the API usage to its .NET MAUI equivalent. For more information, see API changes.
exist.
Issue Tip

App won't Ensure that the required platform project is set to deploy in Visual Studio's Configuration
deploy. Manager.

App won't Update each platform project's entry point class, and the app entry point. For more information,
launch. see Boostrap your migrated app.

CollectionView Check the container layout and the measured size of the CollectionView. By default the control
doesn't scroll. will take up as much space as the container allows. A Grid will constrain children at its own size.
However a StackLayout will enable children to take up space beyond its bounds.

BoxView not The default size of a BoxView in Xamarin.Forms is 40x40. The default size of a BoxView in .NET
appearing. MAUI is 0x0. Set WidthRequest and HeightRequest to 40.

Layout is Add default values to your project based on the .NET MAUI style resource. For more information,
missing see Default value changes from Xamarin.Forms.
padding,
margin, or
spacing.

Custom layout Custom layout code needs updating to work in .NET MAUI. For more information, see Custom
doesn't work. layout changes.

Custom Renderer code needs updating to work in .NET MAUI. For more information, see Use custom
renderer renderers in .NET MAUI.
doesn't work.

Effect doesn't Effect code needs updating to work in .NET MAUI. For more information, see Use effects in .NET
work. MAUI.

See also
Porting from .NET Framework to .NET
.NET Upgrade Assistant
Layout behavior changes from
Xamarin.Forms
Article • 02/17/2023 • 5 minutes to read

You may notice when running your upgraded .NET Multi-platform App UI (.NET MAUI)
app that layout behavior is different. Some of this is the result of changes to layout
spacing values. For more information, see Default value changes from Xamarin.Forms.

The following table shows additional behavior changes between layouts in


Xamarin.Forms and .NET MAUI:

Layout Xamarin.Forms .NET MAUI Recommendation

All In certain cases Sizing requests are


sizing requests honored.
aren't honored.

Grid Columns and Columns and rows Add ColumnDefinitions


rows can be must be explicitly and RowDefinitions.
inferred from declared.
XAML.

HorizontalStackLayout *AndExpand has no


effect.

RelativeLayout Requires the Use Grid instead, or add


compatibility the xmlns for the
namespace. compatibility namespace.

StackLayout Children can fill Children are stacked If you need child views to
space in the and will go beyond fill space, change to a Grid.
stacking direction. available space.

VerticalStackLayout *AndExpand has no


effect.

.NET MAUI controls generally honour explicit size requests. If you ask a control to be
200 device-independent units wide, then .NET MAUI will make that control 200 units
wide, even if the control's container is only 100 units wide.

Default layout value changes from


Xamarin.Forms
Xamarin.Forms uses arbitrary default values for some property values, such as padding,
margins, and spacing. .NET MAUI changes these arbitrary property values to zero.

To preserve the Xamarin.Forms default values in projects that don't set explicit values,
add implicit styles to your project. For more information about implicit styles, see
Implicit styles.

7 Note

The .NET MAUI project template includes resource dictionaries that provide default
styles for most controls. It's recommended you take a similar approach in your
apps, by modifying or inheriting from these resource dictionaries .

The following table lists the layout property values that have changed between
Xamarin.Forms and .NET MAUI:

Property Xamarin.Forms value .NET MAUI value

Grid.ColumnSpacing 6 0

Grid.RowSpacing 6 0

StackLayout.Spacing 6 0

Grid
The biggest change in Grid behavior between Xamarin.Forms and .NET MAUI is that
grids don't automatically add missing rows and columns for you. For example, in
Xamarin.Forms you could add controls to a Grid without specifying their row behavior:

XML

<Grid>

<Label Text="Hello"/>

<Label Grid.Row="1" Text="World"/>

</Grid>

In Xamarin.Forms, despite not declaring that the Grid contains two rows, a second row
would be automatically added for you. .NET MAUI doesn't do this. Instead, you have to
explicitly specify how many rows are in the Grid with the RowDefinitions property.

) Important
By default, .NET MAUI creates a Grid with one column and one row. Therefore, it's
not necessary to set the ColumnDefinitions and RowDefinitions properties if this is
your intention.

StackLayout
There are several differences between the stack layouts in .NET MAUI (StackLayout,
VerticalStackLayout, and HorizontalStackLayout) and the StackLayout in Xamarin.Forms.

The main difference is that .NET MAUI stack layouts are very simple. They stack their
child views in a single direction until all of them have been stacked. They will keep going
until the last child has been stacked, even if that takes them beyond the available space
in the stacking direction. Therefore, .NET MAUI stack layouts arrange controls in a
particular direction. They do not subdivide a space. This is completely different to the
Xamarin.Forms StackLayout, which changes its layout behavior based on circumstances
and the presence of any *AndExpand layout options, such as FillAndExpand or
CenterAndExpand . The Xamarin.Forms StackLayout sometimes subdivides the space,
expanding to or stopping at the edge of its container. In other cases, it expands beyond
its container.

The new stack layouts in .NET MAUI, HorizontalStackLayout and VerticalStackLayout, do


not recognize the *AndExpand layout options. If they encounter a child with such layout
options, they simply treat it as if the AndExpand wasn't there. For example,
FillAndExpand becomes Fill . However, for simplicity of migration from Xamarin.Forms,

the .NET MAUI StackLayout does honor the *AndExpand layout options, although they've
been marked as obsolete. To avoid warnings about using obsolete members, you should
convert your layouts that use *AndExpand layout options to the appropriate layout type.
The can be achieved as follows:

1. If your layout is anything other than a StackLayout, remove all uses of AndExpand .
Just as in Xamarin.Forms, in .NET MAUI the AndExpand layout options have no
effect on any layout other than StackLayout.

2. Remove any AndExpand properties which are orthogonal to the stacking direction.
For example, if you have a StackLayout with an Orientation of Vertical , and it has
a child with a HorizontalAligment="CenterAndExpand" - that layout options has no
effect and can be removed.

3. If you have any remaining AndExpand properties on a StackLayout, you should


convert that StackLayout to a Grid. A Grid is designed to subdivide a space, and
will provide the layout that AndExpand provided in Xamarin.Forms. The following
example shows a Xamarin.Forms StackLayout that uses an AndExpand property:

XAML

<StackLayout>

<Label Text="Hello world!"/>

<Image VerticalOptions="FillAndExpand" Source="dotnetbot.png"/>

</StackLayout>

This can be converted to a Grid in .NET MAUI:

XAML

<Grid RowDefinitions="Auto, *">

<Label Text="Hello world!"/>

<Image Grid.Row="1" Source="dotnetbot.png"/>

</Grid>

When performing this conversion, anything that was marked AndExpand in the
StackLayout should go in its own row or column with a size of * in the Grid.

RelativeLayout
Use of RelativeLayout is not recommended in .NET MAUI. Instead, use a Grid wherever
possible.

If you absolutely require a RelativeLayout, it can be found in the


Microsoft.Maui.Controls.Compatibility namespace:

XML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:compat="clr-
namespace:Microsoft.Maui.Controls.Compatibility;assembly=Microsoft.Maui.Cont
rols"

x:Class="MyMauiApp.MyPage"

Title="MyPage">

<compat:RelativeLayout>

<!-- Your code goes here -->

</compat:RelativeLayout>

</ContentPage>

ScrollView
While ScrollView often isn't considered to be a layout, it can be thought of as a layout as
it's used to scroll its child content. In Xamarin.Forms, ScrollView doesn't behave
consistently when stacking. It has some arbitrary limits on minimum size that depend
partially on its content, and it will sometimes compress to enable other items to fit on
the page inside a StackLayout in ways that are inconsistent and sometimes surprising.

In .NET MAUI, the ScrollView expands to whatever size it wants to be unless otherwise
constrained. This means that inside of a VerticalStackLayout, which can expand infinitely,
a ScrollView will expand to its full content height and doesn't scroll. This behavior can
be confusing if you're a Xamarin.Forms user.

) Important

A StackLayout continues in its stacking direction until it runs out of content. It does
not subdivide its container along that axis. If you want to limit your content to a
constrained space in a direction, you should use another layout such as a Grid.
Use custom renderers in .NET MAUI
Article • 02/15/2023 • 2 minutes to read

Browse the sample

While there are many benefits to using .NET Multi-platform App UI (.NET MAUI)
handlers to customize and create controls, it's still possible to use Xamarin.Forms
custom renderers in .NET MAUI apps. For more information about custom renderers, see
Xamarin.Forms custom renderers.

The process for migrating a Xamarin.Forms custom renderer to .NET MAUI is to:

1. Add the custom renderer code into the appropriate location in your .NET MAUI
project(s). For more information, see Add the code.
2. Modify the using directives and remove ExportRenderer attributes. For more
information, see Modify using directives and other code.
3. Register the renderers. For more information, see Register renderers.
4. Consume the renderers. For more information, see Consume the custom renderers.

To demonstrate using custom renderers in .NET MAUI, consider a Xamarin.Forms control


named PressableView . This control exposes Pressed and Released events based on
platform-specific gestures. The custom renderer implementation is composed of 3 files:

PressableView.cs - the cross-platform class that extends ContentView .


PressableViewRenderer.cs - the Android implementation.

PressableViewRenderer.cs - the iOS implementation.

Add the code


If you're using a .NET MAUI multi-targeted project, the cross-platform file can be moved
to anywhere outside the Platforms folder, and the platform-specific implementation files
should be moved to the corresponding Platform folder:
If your solution has separate projects per-platform, then you should move the platform-
specific implementation files into the corresponding projects.

Modify using directives and other code


Any reference to the Xamarin.Forms.* namespaces need to be removed, and then you
can resolve the related types to Microsoft.Maui.* . This needs to occur in all files you've
added to the .NET MAUI project(s).

You should also remove any ExportRenderer attributes as they won't be needed in .NET
MAUI. For example, the following should be removed:

C#

[assembly: ExportRenderer(typeof(PressableView),
typeof(PressableViewRenderer))]

Register renderers
In your .NET MAUI app project, open MauiProgram.cs and add a using statement for the
Microsoft.Maui.Controls.Compatibility.Hosting namespace. Then, call
UseMauiCompatibility on the MauiAppBuilder object in the CreateMauiApp method, and
configure each renderer using conditional compilation per platform:

C#

using Microsoft.Maui.Controls.Compatibility.Hosting;

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.UseMauiCompatibility()

.ConfigureFonts(fonts =>

fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");

fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");

})

.ConfigureMauiHandlers((handlers) =>

#if ANDROID

handlers.AddHandler(typeof(PressableView),
typeof(XamarinCustomRenderer.Droid.Renderers.PressableViewRenderer));

#elif IOS

handlers.AddHandler(typeof(PressableView),
typeof(XamarinCustomRenderer.iOS.Renderers.PressableViewRenderer));

#endif

});

return builder.Build();

Consume the custom renderers


The custom renderer can be consumed in a .NET MAUI app as a custom control:

XML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:controls="clr-namespace:XamarinCustomRenderer.Controls"

x:Class="MauiCustomRenderer.MainPage">

<Grid BackgroundColor="#f1f1f1">

<controls:PressableView Pressed="Handle_Pressed"
Released="Handle_Released"

HorizontalOptions="Center"

VerticalOptions="Center">

<Grid BackgroundColor="#202020"

HorizontalOptions="Center"

VerticalOptions="Center">

<Label Text="Press Me"

FontSize="16"

TextColor="White"

Margin="24,20"

HorizontalTextAlignment="Center" />

</Grid>

</controls:PressableView>

</Grid>

</ContentPage>

Migrate a Xamarin.Forms custom


renderer to a .NET MAUI handler
Article • 04/13/2023

In Xamarin.Forms, custom renderers can be used to customize the appearance and


behavior of a control, and create new cross-platform controls. Each custom renderer has
a reference to the cross-platform control and often relies on INotifyPropertyChanged to
send property change notifications. Rather than using custom renderers, .NET Multi-
platform App UI (.NET MAUI) introduces a new concept called a handler.

Handlers offer many performance improvements over custom renderers. In


Xamarin.Forms, the ViewRenderer class creates a parent element. For example, on
Android, a ViewGroup is created which is used for auxiliary positioning tasks. In .NET
MAUI, the ViewHandler class doesn't create a parent element, which helps to reduce the
size of the visual hierarchy and improve your app's performance. Handlers also decouple
platform controls from the framework. The platform control only needs to handle the
needs of the framework. This is not only more efficient, but it's much easier to extend or
override when required. Handlers are also suitable for reuse by other frameworks such
as Comet and Fabulous . For more information about handlers, see Handlers.

In Xamarin.Forms, the OnElementChanged method in a custom renderer creates the


platform control, initializes default values, subscribes to events, and handles the
Xamarin.Forms element the renderer was attached to ( OldElement ) and the element that
the renderer is attached to ( NewElement ). In addition, a single OnElementPropertyChanged
method defines the operations to invoke when a property change occurs in the cross-
platform control. .NET MAUI simplifies this approach, so that every property change is
handled by a separate method, and so that code to create the platform control, perform
control setup, and perform control cleanup, is separated into distinct methods.

The process for migrating a Xamarin.Forms custom control that's backed by custom
renderers on each platform to a .NET MAUI custom control that's backed by a handler
on each platform is as follows:

1. Create a class for the cross-platform control, which provides the control's public
API. For more information, see Create the cross-platform control.
2. Create a partial handler class. For more information, see Create the handler.
3. In the handler class, create a PropertyMapper dictionary, which defines the Actions
to take when cross-platform property changes occur. For more information, see
Create the property mapper.
4. Create partial handler classes for each platform that create the native views that
implement the cross-platform control. For more information, see Create the
platform controls.
5. Register the handler using the ConfigureMauiHandlers and AddHandler methods in
your app's MauiProgram class. For more information, see Register the handler.

Then, the cross-platform control can be consumed. For more information, see Consume
the cross-platform control.

Alternatively, custom renderers that customize Xamarin.Forms controls can be converted


so that they modify .NET MAUI handlers. For more information, see Customize controls
with handlers.

Create the cross-platform control


To create a cross-platform control, you should create a class that derives from View:

C#

namespace MyMauiControl.Controls

public class CustomEntry : View

public static readonly BindableProperty TextProperty =

BindableProperty.Create(nameof(Text), typeof(string),
typeof(CustomEntry), null);

public static readonly BindableProperty TextColorProperty =

BindableProperty.Create(nameof(TextColor), typeof(Color),
typeof(CustomEntry), null);

public string Text

get { return (string)GetValue(TextProperty); }

set { SetValue(TextProperty, value); }

public Color TextColor

get { return (Color)GetValue(TextColorProperty); }

set { SetValue(TextColorProperty, value); }

The control should provide a public API that will be accessed by its handler, and control
consumers. Cross-platform controls should derive from View, which represents a visual
element that's used to place layouts and views on the screen.

Create the handler


After creating your cross-platform control, you should create a partial class for your
handler:

C#

#if IOS || MACCATALYST

using PlatformView = Microsoft.Maui.Platform.MauiTextField;

#elif ANDROID

using PlatformView = AndroidX.AppCompat.Widget.AppCompatEditText;

#elif WINDOWS

using PlatformView = Microsoft.UI.Xaml.Controls.TextBox;

#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)

using PlatformView = System.Object;

#endif

using MyMauiControl.Controls;

using Microsoft.Maui.Handlers;

namespace MyMauiControl.Handlers

public partial class CustomEntryHandler

The handler class is a partial class whose implementation will be completed on each
platform with an additional partial class.

The conditional using statements define the PlatformView type on each platform. The
final conditional using statement defines PlatformView to be equal to System.Object .
This is necessary so that the PlatformView type can be used within the handler for usage
across all platforms. The alternative would be to have to define the PlatformView
property once per platform, using conditional compilation.

Create the property mapper


Each handler typically provides a property mapper, which defines what Actions to take
when a property change occurs in the cross-platform control. The PropertyMapper type
is a Dictionary that maps the cross-platform control's properties to their associated
Actions.
7 Note

The property mapper is the replacement for the OnElementPropertyChanged method


in a Xamarin.Forms custom renderer.

PropertyMapper is defined in .NET MAUI's generic ViewHandler class, and requires two
generic arguments to be supplied:

The class for the cross-platform control, which derives from View.
The class for the handler.

The following code example shows the CustomEntryHandler class extended with the
PropertyMapper definition:

C#

public partial class CustomEntryHandler

public static PropertyMapper<CustomEntry, CustomEntryHandler>


PropertyMapper = new PropertyMapper<CustomEntry, CustomEntryHandler>
(ViewHandler.ViewMapper)

[nameof(CustomEntry.Text)] = MapText,

[nameof(CustomEntry.TextColor)] = MapTextColor

};

public CustomEntryHandler() : base(PropertyMapper)

The PropertyMapper is a Dictionary whose key is a string and whose value is a generic
Action . The string represents the cross-platform control's property name, and the
Action represents a static method that requires the handler and cross-platform

control as arguments. For example, the signature of the MapText method is public
static void MapText(CustomEntryHandler handler, CustomEntry view) .

Each platform handler must provide implementations of the Actions, which manipulate
the native view APIs. This ensures that when a property is set on a cross-platform
control, the underlying native view will be updated as required. The advantage of this
approach is that it allows for easy cross-platform control customization, because the
property mapper can be modified by cross-platform control consumers without
subclassing. For more information, see Customize controls with handlers.
Create the platform controls
After creating the mappers for your handler, you must provide handler implementations
on all platforms. This can be accomplished by adding partial class handler
implementations in the child folders of the Platforms folder. Alternatively you could
configure your project to support filename-based multi-targeting, or folder-based
multi-targeting, or both.

Filename-based multi-targeting is configured by adding the following XML to the


project file, as children of the <Project> node:

XML

<!-- Android -->

<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-android')) !=
true">

<Compile Remove="**\**\*.Android.cs" />

<None Include="**\**\*.Android.cs"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />

</ItemGroup>

<!-- iOS and Mac Catalyst -->

<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-ios')) != true


AND $(TargetFramework.StartsWith('net7.0-maccatalyst')) != true">

<Compile Remove="**\**\*.MaciOS.cs" />

<None Include="**\**\*.MaciOS.cs"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />

</ItemGroup>

<!-- Windows -->

<ItemGroup Condition="$(TargetFramework.Contains('-windows')) != true ">

<Compile Remove="**\*.Windows.cs" />

<None Include="**\*.Windows.cs"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />

</ItemGroup>

For more information about configuring multi-targeting, see Configure multi-targeting.

Each platform handler class should be a partial class and derive from the generic
ViewHandler class, which requires two type arguments:

The class for the cross-platform control, which derives from View.
The type of the native view that implements the cross-platform control on the
platform. This should be identical to the type of the PlatformView property in the
handler.

) Important
The ViewHandler class provides VirtualView and PlatformView properties. The
VirtualView property is used to access the cross-platform control from its handler.
The PlatformView property, is used to access the native view on each platform that
implements the cross-platform control.

Each of the platform handler implementations should override the following methods:

CreatePlatformView , which should create and return the native view that

implements the cross-platform control.


ConnectHandler , which should perform any native view setup, such as initializing

the native view and performing event subscriptions.


DisconnectHandler , which should perform any native view cleanup, such as

unsubscribing from events and disposing objects. This method is intentionally not
invoked by .NET MAUI. Instead, you must invoke it yourself from a suitable location
in your app's lifecycle. For more information, see Native view cleanup.

7 Note

The CreatePlatformView , ConnectHandler , and DisconnectHandler overrides are the


replacements for the OnElementChanged method in a Xamarin.Forms custom
renderer.

Each platform handler should also implement the Actions that are defined in the
mapper dictionaries. In addition, each platform handler should also provide code, as
required, to implement the functionality of the cross-platform control on the platform.
Alternatively, for more complex controls this can be provided by an additional type.

The following example shows the CustomEntryHandler implementation on Android:

C#

#nullable enable

using AndroidX.AppCompat.Widget;

using Microsoft.Maui.Handlers;

using Microsoft.Maui.Platform;

using MyMauiControl.Controls;

namespace MyMauiControl.Handlers

public partial class CustomEntryHandler : ViewHandler<CustomEntry,


AppCompatEditText>

protected override AppCompatEditText CreatePlatformView() => new


AppCompatEditText(Context);

protected override void ConnectHandler(AppCompatEditText


platformView)

base.ConnectHandler(platformView);

// Perform any control setup here

protected override void DisconnectHandler(AppCompatEditText


platformView)

// Perform any native view cleanup here

platformView.Dispose();

base.DisconnectHandler(platformView);

public static void MapText(CustomEntryHandler handler, CustomEntry


view)

handler.PlatformView.Text = view.Text;

handler.PlatformView?.SetSelection(handler.PlatformView?.Text?.Length ?? 0);

public static void MapTextColor(CustomEntryHandler handler,


CustomEntry view)

handler.PlatformView?.SetTextColor(view.TextColor.ToPlatform());

CustomEntryHandler derives from the ViewHandler class, with the generic CustomEntry
argument specifying the cross-platform control type, and the AppCompatEditText
argument specifying the type of native control.

The CreatePlatformView override creates and returns an AppCompatEditText object. The


ConnectHandler override is the location to perform any required native view setup. The
DisconnectHandler override is the location to perform any native view cleanup, and so

calls the Dispose method on the AppCompatEditText instance.

The handler also implements the Actions defined in the property mapper dictionary.
Each Action is executed in response to a property changing on the cross-platform
control, and is a static method that requires handler and cross-platform control
instances as arguments. In each case, the Action calls methods defined on the native
control.
Register the handler
A custom control and its handler must be registered with an app, before it can be
consumed. This should occur in the CreateMauiApp method in the MauiProgram class in
your app project, which is the cross-platform entry point for the app:

C#

using Microsoft.Extensions.Logging;

using MyMauiControl.Controls;

using MyMauiControl.Handlers;

namespace MyMauiControl;

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureFonts(fonts =>

fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");

fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");

})

.ConfigureMauiHandlers(handlers =>

handlers.AddHandler(typeof(CustomEntry),
typeof(CustomEntryHandler));

});

#if DEBUG

builder.Logging.AddDebug();

#endif

return builder.Build();

The handler is registered with the ConfigureMauiHandlers and AddHandler method. The
first argument to the AddHandler method is the cross-platform control type, with the
second argument being its handler type.

7 Note

This registration approach avoids Xamarin.Forms' assembly scanning, which is slow


and expensive.
Consume the cross-platform control
After registering the handler with your app, the cross-platform control can then be
consumed:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:controls="clr-namespace:MyMauiControl.Controls"

x:Class="MyMauiControl.MainPage">

<Grid>

<controls:CustomEntry Text="Hello world"

TextColor="Blue" />

</Grid>

</ContentPage>

Native view cleanup


Each platform's handler implementation overrides the DisconnectHandler
implementation, which is used to perform native view cleanup such as unsubscribing
from events and disposing objects. However, this override is intentionally not invoked
by .NET MAUI. Instead, you must invoke it yourself from a suitable location in your app's
lifecycle. This could be when the page containing the control is navigated away from,
which causes the page's Unloaded event to be raised.

An event handler for the page's Unloaded event can be registered in XAML:

XAML

<ContentPage ...

xmlns:controls="clr-namespace:MyMauiControl.Controls"

Unloaded="ContentPage_Unloaded">

<Grid>

<controls:CustomEntry x:Name="customEntry"

... />

</Grid>

</ContentPage>

The event handler for the Unloaded event can then invoke the DisconnectHandler
method on its Handler instance:

C#

void ContentPage_Unloaded(object sender, EventArgs e)

customEntry.Handler?.DisconnectHandler();

See also
Create a custom control using handler
Use effects in .NET MAUI
Article • 02/15/2023 • 2 minutes to read

While there are many benefits to using .NET Multi-platform App UI (.NET MAUI)
handlers to customize controls, it's still possible to use Xamarin.Forms effects in .NET
MAUI apps. For more information about effects, see Xamarin.Forms effects.

The process for migrating a Xamarin.Forms effect to .NET MAUI is to:

1. Remove the effect attributes from your effect classes. For more information, see
Remove effect attributes.
2. Remove the effect using directives. For more information, see Remove using
directives.
3. Add the effect code into the appropriate location in your .NET MAUI app project.
For more information, see Add the effect code.
4. Register the effect. For more information, see Register the effect.
5. Consume your .NET MAUI effect. For more information, see Consume the effect.

Remove effect attributes


Any ResolutionGroupNameAttribute and ExportEffectAttribute attributes should be
removed from your effect classes.

Remove using directives


Any references to the Xamarin.Forms and Xamarin.Forms.Platform.* namespaces should
be removed from your effect classes.

Add the effect code


If you're using a .NET MAUI multi-targeted project, your effect code should be
combined into a single file and placed outside the Platforms folder. This requires you to
combine your RoutingEffect implementation and PlatformEffect implementations into a
single file, using conditional compilation around platform code. However, if your
solution has separate projects per-platform, then you should move the platform-specific
effect files into the corresponding projects.

In .NET MAUI, the RoutingEffect class is in the Microsoft.Maui.Controls namespace. This


namespace is one of .NET MAUI's implicit global using directives, and so you don't
need to add a using directive for it. However, the PlatformEffect class is in the
Microsoft.Maui.Controls.Platform namespace, for which you must add a using
directive.

The following code example shows a FocusRoutingEffect class and its platform
implementations combined into a single file:

C#

using Microsoft.Maui.Controls.Platform;

namespace MyMauiApp.Effects;

internal class FocusRoutingEffect : RoutingEffect

#if ANDROID

internal class FocusPlatformEffect : PlatformEffect

protected override void OnAttached()

// Customize the control here

protected override void OnDetached()

// Cleanup the control customization here

#elif IOS

internal class FocusPlatformEffect : PlatformEffect

protected override void OnAttached()

// Customize the control here

protected override void OnDetached()

// Cleanup the control customization here

#elif WINDOWS

internal class FocusPlatformEffect : PlatformEffect

protected override void OnAttached()

// Customize the control here

protected override void OnDetached()

// Cleanup the control customization here

#endif

Register the effect


In your .NET MAUI app project, open MauiProgram.cs and call the ConfigureEffects
method on the MauiAppBuilder object in the CreateMauiApp method:

C#

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureEffects(effects =>

effects.Add<FocusRoutingEffect, FocusPlatformEffect>();

});

return builder.Build();

The effect is registered with the ConfigureEffects method, whose configureDelegate


registers the PlatformEffect implementation against its RoutingEffect implementation.

Consume the effect


The effect can be consumed in a .NET MAUI app by adding it to the Effects collection of
a control:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:MyMauiApp.Effects"

x:Class="MyMauiApp.MainPage">

<VerticalStackLayout>

<Entry Text="Enter your text">

<Entry.Effects>

<local:FocusRoutingEffect />

</Entry.Effects>

</Entry>

</VerticalStackLayout>

</ContentPage>

XAML
Article • 06/24/2022 • 2 minutes to read

The eXtensible Application Markup Language (XAML) is an XML-based language that's


an alternative to programming code for instantiating and initializing objects, and
organizing those objects in parent-child hierarchies.

XAML allows developers to define user interfaces in .NET Multi-platform App UI (.NET
MAUI) apps using markup rather than code. XAML is not required in a .NET MAUI app,
but it is the recommended approach to developing your UI because it's often more
succinct, more visually coherent, and has tooling support. XAML is also well suited for
use with the Model-View-ViewModel (MVVM) pattern, where XAML defines the view
that is linked to viewmodel code through XAML-based data bindings.

Within a XAML file, you can define user interfaces using all the .NET MAUI views, layouts,
and pages, as well as custom classes. The XAML file can be either compiled or
embedded in the app package. Either way, the XAML is parsed at build time to locate
named objects, and at runtime the objects represented by the XAML are instantiated
and initialized.

XAML has several advantages over equivalent code:

XAML is often more succinct and readable than equivalent code.


The parent-child hierarchy inherent in XML allows XAML to mimic with greater
visual clarity the parent-child hierarchy of user-interface objects.

There are also disadvantages, mostly related to limitations that are intrinsic to markup
languages:

XAML cannot contain code. All event handlers must be defined in a code file.
XAML cannot contain loops for repetitive processing.
XAML cannot contain conditional processing. However, a data-binding can
reference a code-based binding converter that effectively allows some conditional
processing.
XAML generally cannot instantiate classes that do not define a parameterless
constructor, although this restriction can sometimes be overcome.
XAML generally cannot call methods, although this restriction can sometimes be
overcome.

There is no visual designer for producing XAML in .NET MAUI apps. All XAML must be
hand-written, but you can use XAML hot reload to view your UI as you edit it.
XAML is basically XML, but XAML has some unique syntax features. The most important
are:

Property elements
Attached properties
Markup extensions

These features are not XML extensions. XAML is entirely legal XML. But these XAML
syntax features use XML in unique ways.
Get started with XAML
Article • 03/03/2023 • 8 minutes to read

Browse the sample

In a .NET Multi-platform App UI (.NET MAUI) app, XAML is mostly used to define the
visual contents of a page and works together with a C# code-behind file. The code-
behind file provides code support for the markup. Together, these two files contribute to
a new class definition that includes child views and property initialization. Within the
XAML file, classes and properties are referenced with XML elements and attributes, and
links between the markup and code are established.

Anatomy of a XAML file


A new .NET MAUI app contains three XAML files, and their associated code-behind files:

The first file pairing is App.xaml, a XAML file, and App.xaml.cs, a C# code-behind file
associated with the XAML file. Both App.xaml and App.xaml.cs contribute to a class
named App that derives from Application . The second file pairing is AppShell.xaml and
AppShell.xaml.cs, which contribute to a class named AppShell that derives from Shell.
Most other classes with XAML files contribute to a class that derives from ContentPage,
and define the UI of a page. This is true of the MainPage.xaml and MainPage.xaml.cs
files.

The MainPage.xaml file has the following structure:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="MyMauiApp.MainPage">

...

</ContentPage>

The two XML namespace ( xmlns ) declarations refer to URIs on microsoft.com. However,
there's no content at these URIs, and they basically function as version identifiers.

The first XML namespace declaration means that tags defined within the XAML file with
no prefix refer to classes in .NET MAUI, for example ContentPage. The second
namespace declaration defines a prefix of x . This is used for several elements and
attributes that are intrinsic to XAML itself and which are supported by other
implementations of XAML. However, these elements and attributes are slightly different
depending on the year embedded in the URI. .NET MAUI supports the 2009 XAML
specification.

At the end of the first tag, the x prefix is used for an attribute named Class . Because
the use of this x prefix is virtually universal for the XAML namespace, XAML attributes
such as Class are almost always referred to as x:Class . The x:Class attribute specifies
a fully qualified .NET class name: the MainPage class in the MyMauiApp namespace. This
means that this XAML file defines a new class named MainPage in the MyMauiApp
namespace that derives from ContentPage (the tag in which the x:Class attribute
appears).

The x:Class attribute can only appear in the root element of a XAML file to define a
derived C# class. This is the only new class defined in the XAML file. Everything else that
appears in a XAML file is instead simply instantiated from existing classes and initialized.

The MainPage.xaml.cs file looks similar to this:

C#

namespace MyMauiApp;

public partial class MainPage : ContentPage

public MainPage()

InitializeComponent();

The MainPage class derives from ContentPage, and is a partial class definition.
When Visual Studio builds the project, a source generator generates new C# source that
contains the definition of the InitializeComponent method that's called from the
MainPage constructor and adds it to the compilation object.

At runtime, code in the MauiProgram class bootstraps the app and executes the App class
constructor, which instantiates AppShell . The AppShell class instantiates the first page
of the app to be displayed, which is MainPage . The MainPage constructor calls
InitializeComponent , which initializes all the objects defined in the XAML file, connects
them all together in parent-child relationships, attaches event handlers defined in code
to events set in the XAML file, and sets the resultant tree of objects as the content of the
page.

7 Note

The AppShell class uses .NET MAUI Shell to set the first page of the app to be
displayed. However, Shell is beyond the scope of this introduction to XAML. For
more information, see .NET MAUI Shell.

Set page content


A ContentPage should contain a single child, that can be a view or a layout with child
views. The child of the ContentPage is automatically set as the value of the
ContentPage.Content property.

The following example shows a ContentPage containing a Label:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.HelloXamlPage"

Title="Hello XAML Page">

<Label Text="Hello, XAML!"

VerticalOptions="Center"

HorizontalTextAlignment="Center"

Rotation="-15"

FontSize="18"

FontAttributes="Bold"

TextColor="Blue" />

</ContentPage>

From the example above the relationship between classes, properties, and XML should
be evident. A .NET MAUI class (such as ContentPage or Label) appears in the XAML file
as an XML element. Properties of that class—including Title on ContentPage and seven
properties of Label usually appear as XML attributes.

Many shortcuts exist to set the values of these properties. Some properties are basic
data types. For example, the Title and Text properties are of type string , and
Rotation is of type double . The HorizontalTextAlignment property is of type

TextAlignment , which is an enumeration. For a property of any enumeration type, all you

need to supply is a member name.

For properties of more complex types, however, converters are used for parsing the
XAML. These are classes in .NET MAUI that derive from TypeConverter . For the example
above, several .NET MAUI converters are automatically applied to convert string values
to their correct type:

LayoutOptionsConverter for the VerticalOptions property. This converter converts


the names of public static fields of the LayoutOptions structure to values of type
LayoutOptions .
ColorTypeConverter for the TextColor property. This converter converts the names

of public static fields of the Colors class or hexadecimal RGB values, with or
without an alpha channel.

Page navigation
When you run a .NET MAUI app, the MainPage is typically displayed. To see a different
page you can either set that as the new startup page in the AppShell.xaml file, or
navigate to the new page from MainPage .

To implement navigation, in the MainPage.xaml.cs constructor, you can create a simple


Button and use the event handler to navigate to HelloXamlPage :

C#

public MainPage()

InitializeComponent();

Button button = new Button

Text = "Navigate!",

HorizontalOptions = LayoutOptions.Center,

VerticalOptions = LayoutOptions.Center

};

button.Clicked += async (sender, args) =>

await Navigation.PushAsync(new HelloXamlPage());

};

Content = button;

When you compile and deploy the new version of this app, a button appears on the
screen. Pressing it navigates to HelloXamlPage :

You can navigate back to MainPage using the navigation bar that appears on each
platform.

7 Note

An alternative to this navigation model is to use .NET MAUI Shell. For more
information, see .NET MAUI Shell overview.

XAML and code interactions


The child of most ContentPage derivatives is a layout, such as a StackLayout or a Grid,
and the layout can contain multiple children. In XAML, these parent-child relationships
are established with normal XML hierarchy:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.XamlPlusCodePage"

Title="XAML + Code Page">

<StackLayout>

<Slider VerticalOptions="Center" />

<Label Text="A simple Label"

FontSize="18"

HorizontalOptions="Center"

VerticalOptions="Center" />

<Button Text="Click Me!"

HorizontalOptions="Center"

VerticalOptions="Center" />

</StackLayout>

</ContentPage>

This XAML file is syntactically complete, and produces the following UI:

However, while you can interact with the Slider and Button, the UI isn't updated. The
Slider should cause the Label to display the current value, and the Button should do
something.

Displaying a Slider value using a Label can be achieved entirely in XAML with a data
binding. However, it's useful to see the code solution first. Even so, handling the Button
click definitely requires code. This means that the code-behind file for XamlPlusCodePage
must contain handlers for the ValueChanged event of the Slider and the Clicked event of
the Button:

C#

namespace XamlSamples

public partial class XamlPlusCodePage

public XamlPlusCodePage()

InitializeComponent();

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)

valueLabel.Text = args.NewValue.ToString("F3");

async void OnButtonClicked(object sender, EventArgs args)

Button button = (Button)sender;

await DisplayAlert("Clicked!", "The button labeled '" +


button.Text + "' has been clicked", "OK");

Back in the XAML file, the Slider and Button tags need to include attributes for the
ValueChanged and Clicked events that reference these handlers:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.XamlPlusCodePage"

Title="XAML + Code Page">

<StackLayout>

<Slider VerticalOptions="Center"

ValueChanged="OnSliderValueChanged" />

<Label x:Name="valueLabel"

Text="A simple Label"

FontSize="18"

HorizontalOptions="Center"

VerticalOptions="Center" />

<Button Text="Click Me!"

HorizontalOptions="Center"

VerticalOptions="Center"

Clicked="OnButtonClicked" />

</StackLayout>

</ContentPage>

Notice that assigning a handler to an event has the same syntax as assigning a value to
a property. In addition, for the ValueChanged event handler of the Slider to use the Label
to display the current value, the handler needs to reference that object from code.
Therefore, the Label needs a name, which is specified with the x:Name attribute. The x
prefix of the x:Name attribute indicates that this attribute is intrinsic to XAML. The name
you assign to the x:Name attribute has the same rules as C# variable names. For
example, it must begin with a letter or underscore and contain no embedded spaces.

The ValueChanged event handler can now set the Label to display the new Slider value,
which is available from the event arguments:

C#

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)

valueLabel.Text = args.NewValue.ToString("F3");

Alternatively, the handler could obtain the Slider object that is generating this event
from the sender argument and obtain the Value property from that:

C#

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)

valueLabel.Text = ((Slider)sender).Value.ToString("F3");

The result is that any manipulation of the Slider causes its value to be displayed in the
Label:

In the example above the Button simulates a response to a Clicked event by displaying
an alert with the Text of the button. Therefore, the event handler can cast the sender
argument to a Button and then access its properties:

C#

async void OnButtonClicked(object sender, EventArgs args)

Button button = (Button)sender;

await DisplayAlert("Clicked!", "The button labeled '" + button.Text + "'


has been clicked", "OK");

The OnButtonClicked method is defined as async because the DisplayAlert method is


asynchronous and should be prefaced with the await operator, which returns when the
method completes. Because this method obtains the Button firing the event from the
sender argument, the same handler could be used for multiple buttons.

Next steps
XAML is mostly designed for instantiating and initializing objects. But often, properties
must be set to complex objects that cannot easily be represented as XML strings, and
sometimes properties defined by one class must be set on a child class. These two
needs require the essential XAML syntax features of property elements and attached
properties.

Essential XAML syntax


Essential XAML syntax
Article • 12/23/2022 • 5 minutes to read

Browse the sample

XAML is mostly designed for instantiating and initializing objects. But often, properties
must be set to complex objects that cannot easily be represented as XML strings, and
sometimes properties defined by one class must be set on a child class. These two
needs require the essential XAML syntax features of property elements and attached
properties.

Property elements
In .NET Multi-platform App UI (.NET MAUI) XAML, properties of classes are normally set
as XML attributes:

XAML

<Label Text="Hello, XAML!"

VerticalOptions="Center"

FontAttributes="Bold"

FontSize="18"

TextColor="Aqua" />

However, there is an alternative way to set a property in XAML:

XAML

<Label Text="Hello, XAML!"

VerticalOptions="Center"

FontAttributes="Bold"

FontSize="18">

<Label.TextColor>

Aqua

</Label.TextColor>

</Label>

These two examples that specify the TextColor property are functionally equivalent, and
enable the introduction of some basic terminology:

Label is an object element. It is a .NET MAUI object expressed as an XML element.


Text , VerticalOptions , FontAttributes and FontSize are property attributes. They

are .NET MAUI properties expressed as XML attributes.


In the second example, TextColor has become a property element. It is a .NET
MAUI property expressed as an XML element.

7 Note

In a property element, the value of the property is always defined as the content
between the property-element start and end tags.

Property-element syntax can also be used on more than one property of an object:

XAML

<Label Text="Hello, XAML!"

VerticalOptions="Center">

<Label.FontAttributes>

Bold

</Label.FontAttributes>

<Label.FontSize>

Large

</Label.FontSize>

<Label.TextColor>

Aqua

</Label.TextColor>

</Label>

While property-element syntax might seem unnecessary, it's essential when the value of
a property is too complex to be expressed as a simple string. Within the property-
element tags you can instantiate another object and set its properties. For example, the
Grid layout has properties named RowDefinitions and ColumnDefinitions , which are of
type RowDefinitionCollection and ColumnDefinitionCollection respectively. These
types are collections of RowDefinition and ColumnDefinition objects, and you typically
use property element syntax to set them:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.GridDemoPage"

Title="Grid Demo Page">

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="*" />

<RowDefinition Height="100" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="*" />

<ColumnDefinition Width="100" />

</Grid.ColumnDefinitions>

...

</Grid>

</ContentPage>

Attached properties
In the previous example you saw that the Grid requires property elements for the
RowDefinitions and ColumnDefinitions collections to define the rows and columns. This

suggests that there must be a technique for indicating the row and column where each
child of the Grid resides.

Within the tag for each child of the Grid you specify the row and column of that child
using the Grid.Row and Grid.Column attributes, which have default values of 0. You can
also indicate if a child spans more than one row or column with the Grid.RowSpan and
Grid.ColumnSpan attributes, which have default values of 1.

The following example demonstrates placing children within a Grid:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.GridDemoPage"

Title="Grid Demo Page">

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="*" />

<RowDefinition Height="100" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="*" />

<ColumnDefinition Width="100" />

</Grid.ColumnDefinitions>

<Label Text="Autosized cell"

TextColor="White"

BackgroundColor="Blue" />

<BoxView Color="Silver"

Grid.Column="1" />

<BoxView Color="Teal"

Grid.Row="1" />

<Label Text="Leftover space"

Grid.Row="1" Grid.Column="1"

TextColor="Purple"

BackgroundColor="Aqua"

HorizontalTextAlignment="Center"

VerticalTextAlignment="Center" />

<Label Text="Span two rows (or more if you want)"

Grid.Column="2" Grid.RowSpan="2"

TextColor="Yellow"

BackgroundColor="Blue"

HorizontalTextAlignment="Center"

VerticalTextAlignment="Center" />

<Label Text="Span two columns"

Grid.Row="2" Grid.ColumnSpan="2"

TextColor="Blue"

BackgroundColor="Yellow"

HorizontalTextAlignment="Center"

VerticalTextAlignment="Center" />

<Label Text="Fixed 100x100"

Grid.Row="2" Grid.Column="2"

TextColor="Aqua"

BackgroundColor="Red"

HorizontalTextAlignment="Center"

VerticalTextAlignment="Center" />

</Grid>

</ContentPage>

This XAML results in the following layout:

The Grid.Row , Grid.Column , Grid.RowSpan , and Grid.ColumnSpan attributes appear to be


properties of the Grid class, but this class doesn't define anything named Row , Column ,
RowSpan , or ColumnSpan . Instead, the Grid class defines four bindable properties named
RowProperty , ColumnProperty , RowSpanProperty , and ColumnSpanProperty , that are

special types of bindable properties known as attached properties. They are defined by
the Grid class but set on children of the Grid.

7 Note

When you wish to use these attached properties in code, the Grid class provides
static methods named GetRow , SetRow , GetColumn , SetColumn , GetRowSpan ,
SetRowSpan , GetColumnSpan , and SetColumnSpan .

Attached properties are recognizable in XAML as attributes containing both a class and
a property name separated by a period. They are called attached properties because they
are defined by one class (in this case, Grid) but attached to other objects (in this case,
children of the Grid). During layout, the Grid can interrogate the values of these
attached properties to know where to place each child.

Content properties
In the previous example, the Grid object was set to the Content property of the
ContentPage. However, the Content property wasn't referenced in the XAML but can be:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.XamlPlusCodePage"

Title="XAML + Code Page">

<ContentPage.Content>

<Grid>

...

</Grid>

</ContentPage.Content>

</ContentPage>

The Content property isn't required in XAML because elements defined for use in .NET
MAUI XAML are allowed to have one property specified as the ContentProperty
attribute on the class:

C#

[ContentProperty("Content")]

public class ContentPage : TemplatedPage

...

Any property specified as the ContentProperty of a class means that the property-
element tags for the property are not required. Therefore, the example above specifies
that any XAML content that appears between the start and end ContentPage tags is
assigned to the Content property.

Many classes also have ContentProperty attribute definitions. For example, the content
property of Label is Text .

Platform differences
.NET MAUI apps can customize UI appearance on a per-platform basis. This can be
achieved in XAML using the OnPlatform and On classes:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="...">

<ContentPage.Padding>

<OnPlatform x:TypeArguments="Thickness">

<On Platform="iOS" Value="0, 20, 0, 0" />

<On Platform="Android" Value="10, 20, 20, 10" />

</OnPlatform>

</ContentPage.Padding>

...

</ContentPage>

OnPlatform is a generic class and so you need to specify the generic type argument, in

this case, Thickness , which is the type of Padding property. This is achieved with the
x:TypeArguments XAML attribute. The OnPlatform class has a property named

Platforms , that is an IList of On objects. Each On object can set the Platform and
Value property to define the Thickness value for a specific platform.

In addition, the Platform property of On is of type IList<string> , so you can include


multiple platforms if the values are the same:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="...">

<ContentPage.Padding>

<OnPlatform x:TypeArguments="Thickness">

<On Platform="iOS, Android" Value="10, 20, 20, 10" />

</OnPlatform>

</ContentPage.Padding>

...

</ContentPage>

This is the standard way to set a platform-dependent Padding property in XAML.

7 Note

If the Value property of an On object can't be represented by a single string, you


can define property elements for it.

For more information, see OnPlatform Markup Extension.

Next steps
.NET MAUI XAML markup extensions enable properties to be set to objects or values
that are referenced indirectly from other sources. XAML markup extensions are
particularly important for sharing objects, and referencing constants used throughout an
app.

XAML markup extensions


XAML markup extensions
Article • 04/03/2023 • 8 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) XAML markup extensions enable properties to
be set to objects or values that are referenced indirectly from other sources. XAML
markup extensions are particularly important for sharing objects, and referencing
constants used throughout an app, but they find their greatest utility in data bindings.

Typically, you use XAML to set properties of an object to explicit values, such as a string,
a number, an enumeration member, or a string that is converted to a value behind the
scenes. Sometimes, however, properties must instead reference values defined
somewhere else, or which might require a little processing by code at runtime. For these
purposes, XAML markup extensions are available.

XAML markup extensions are so named because they are backed by code in classes that
implement IMarkupExtension . It's also possible to write your own custom markup
extensions.

In many cases, XAML markup extensions are instantly recognizable in XAML files
because they appear as attribute values delimited by curly braces, { and }, but
sometimes markup extensions also appear in markup as conventional elements.

) Important

Markup extensions can have properties, but they are not set like XML attributes. In
a markup extension, property settings are separated by commas, and no quotation
marks appear within the curly braces.

Shared resources
Some XAML pages contain several views with properties set to the same values. For
example, many of the property settings for these Button objects are the same:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.SharedResourcesPage"

Title="Shared Resources Page">

<StackLayout>

<Button Text="Do this!"

HorizontalOptions="Center"

VerticalOptions="Center"

BorderWidth="3"

Rotation="-15"

TextColor="Red"

FontSize="24" />

<Button Text="Do that!"

HorizontalOptions="Center"

VerticalOptions="Center"

BorderWidth="3"

Rotation="-15"

TextColor="Red"

FontSize="24" />

<Button Text="Do the other thing!"

HorizontalOptions="Center"

VerticalOptions="Center"

BorderWidth="3"

Rotation="-15"

TextColor="Red"

FontSize="24" />

</StackLayout>

</ContentPage>

If one of these properties needs to be changed, you might prefer to make the change
just once rather than three times. If this were code, you’d likely be using constants and
static read-only objects to help keep such values consistent and easy to modify.

In XAML, one popular solution is to store such values or objects in a resource dictionary.
The VisualElement class defines a property named Resources of type
ResourceDictionary, which is a dictionary with keys of type string and values of type
object . You can put objects into this dictionary and then reference them from markup,

all in XAML.

To use a resource dictionary on a page, include a pair of Resources property-element


tags at the top of the page, and add resources within these tags. Objects and values of
various types can be added to the resource dictionary. These types must be instantiable.
They can’t be abstract classes, for example. These types must also have a public
parameterless constructor. Each item requires a dictionary key specified with the x:Key
attribute:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.SharedResourcesPage"

Title="Shared Resources Page">

<ContentPage.Resources>

<LayoutOptions x:Key="horzOptions"

Alignment="Center" />

<LayoutOptions x:Key="vertOptions"

Alignment="Center" />

</ContentPage.Resources>

...

</ContentPage>

In this example, the two resources are values of the structure type LayoutOptions , and
each has a unique key and one or two properties set. In code and markup, it’s much
more common to use the static fields of LayoutOptions , but here it’s more convenient to
set the properties.

7 Note

Optional ResourceDictionary tags can be included as the child of the Resources


tags.

The resources can then be consumed by the Button objects, by using the StaticResource
XAML markup extension to set their HorizontalOptions and VerticalOptions properties:

XAML

<Button Text="Do this!"

HorizontalOptions="{StaticResource horzOptions}"

VerticalOptions="{StaticResource vertOptions}"

BorderWidth="3"
Rotation="-15"

TextColor="Red"

FontSize="24" />

The StaticResource markup extension is always delimited with curly braces, and includes
the dictionary key. The name StaticResource distinguishes it from DynamicResource,
which .NET MAUI also supports. DynamicResource is for dictionary keys associated with
values that might change at runtime, while StaticResource accesses elements from the
dictionary just once when the elements on the page are constructed. Whenever the
XAML parser encounters a StaticResource markup extension, it searches up the visual
tree and uses the first ResourceDictionary it encounters containing that key.

It’s necessary to store doubles in the dictionary for the BorderWidth , Rotation , and
FontSize properties. XAML conveniently defines tags for common data types like
x:Double and x:Int32 :

XAML
<ContentPage.Resources>

<LayoutOptions x:Key="horzOptions"

Alignment="Center" />

<LayoutOptions x:Key="vertOptions"

Alignment="Center" />

<x:Double x:Key="borderWidth">3</x:Double>

<x:Double x:Key="rotationAngle">-15</x:Double>

<x:Double x:Key="fontSize">24</x:Double>

</ContentPage.Resources>

These additional three resources can be referenced in the same way as the
LayoutOptions values:

XAML

<Button Text="Do this!"

HorizontalOptions="{StaticResource horzOptions}"

VerticalOptions="{StaticResource vertOptions}"

BorderWidth="{StaticResource borderWidth}"

Rotation="{StaticResource rotationAngle}"

TextColor="Red"

FontSize="{StaticResource fontSize}" />

For resources of type Color, you can use the same string representations that you use
when directly assigning attributes of these types. Type converters included in .NET MAUI
are invoked when the resource is created. It's also possible to use the OnPlatform class
within the resource dictionary to define different values for the platforms. The following
example uses this class for setting different text colors:

XAML

<OnPlatform x:Key="textColor"

x:TypeArguments="Color">

<On Platform="iOS" Value="Red" />

<On Platform="Android" Value="Aqua" />

</OnPlatform>

The OnPlatform resource gets an x:Key attribute because it’s an object in the dictionary,
and an x:TypeArguments attribute because it’s a generic class. The iOS , and Android
attributes are converted to Color values when the object is initialized.

The following example shows the three buttons accessing six shared values:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.SharedResourcesPage"

Title="Shared Resources Page">

<ContentPage.Resources>

<LayoutOptions x:Key="horzOptions"

Alignment="Center" />

<LayoutOptions x:Key="vertOptions"

Alignment="Center" />

<x:Double x:Key="borderWidth">3</x:Double>

<x:Double x:Key="rotationAngle">-15</x:Double>

<x:Double x:Key="fontSize">24</x:Double>

<OnPlatform x:Key="textColor"

x:TypeArguments="Color">

<On Platform="iOS" Value="Red" />

<On Platform="Android" Value="Aqua" />

<On Platform="WinUI" Value="#80FF80" />

</OnPlatform>

</ContentPage.Resources>

<StackLayout>

<Button Text="Do this!"

HorizontalOptions="{StaticResource horzOptions}"

VerticalOptions="{StaticResource vertOptions}"

BorderWidth="{StaticResource borderWidth}"

Rotation="{StaticResource rotationAngle}"

TextColor="{StaticResource textColor}"

FontSize="{StaticResource fontSize}" />

<Button Text="Do that!"

HorizontalOptions="{StaticResource horzOptions}"

VerticalOptions="{StaticResource vertOptions}"

BorderWidth="{StaticResource borderWidth}"

Rotation="{StaticResource rotationAngle}"

TextColor="{StaticResource textColor}"

FontSize="{StaticResource fontSize}" />

<Button Text="Do the other thing!"

HorizontalOptions="{StaticResource horzOptions}"

VerticalOptions="{StaticResource vertOptions}"

BorderWidth="{StaticResource borderWidth}"

Rotation="{StaticResource rotationAngle}"

TextColor="{StaticResource textColor}"

FontSize="{StaticResource fontSize}" />

</StackLayout>

</ContentPage>

The following screenshot verifies the consistent styling:


Although it's common to define the Resources collection at the top of the page, you can
have Resources collections on other elements on the page. For example, the following
example shows resources added to a StackLayout:

XAML

<StackLayout>

<StackLayout.Resources>

<Color x:Key="textColor">Blue</Color>

</StackLayout.Resources>

...

</StackLayout>

One of the most common types of objects stored in resource dictionaries is the .NET
MAUI Style, which defines a collection of property settings. For more information about
styles, see Style apps using XAML.

7 Note

The purpose of a resource dictionary is to share objects. Therefore, it doesn't make


sense to put controls such as a Label or Button in a resource dictionary. Visual
elements can't be shared because the same instance can't appear twice on a page.

x:Static Markup Extension


In addition to the StaticResource markup extension, there's also an x:Static markup
extension. However, while StaticResource returns an object from a resource
dictionary, x:Static accesses a public static field, a public static property, a public
constant field, or an enumeration member.

7 Note

The StaticResource markup extension is supported by XAML implementations that


define a resource dictionary, while x:Static is an intrinsic part of XAML, as the x
prefix reveals.

The following example demonstrates how x:Static can explicitly reference static fields
and enumeration members:

XAML

<Label Text="Hello, XAML!"

VerticalOptions="{x:Static LayoutOptions.Start}"

HorizontalTextAlignment="{x:Static TextAlignment.Center}"

TextColor="{x:Static Colors.Aqua}" />

The main use of the x:Static markup extension is in referencing static fields or
properties from your own code. For example, here’s an AppConstants class that contains
some static fields that you might want to use on multiple pages throughout an app:

C#

namespace XamlSamples

static class AppConstants

public static readonly Color BackgroundColor = Colors.Aqua;

public static readonly Color ForegroundColor = Colors.Brown;

To reference the static fields of this class in a XAML file, you need to use an XML
namespace declaration to indicate where this file is located. Each additional XML
namespace declaration defines a new prefix. To access classes local to the root app
namespace, such as AppConstants , you could use the prefix local . The namespace
declaration must indicate the CLR (Common Language Runtime) namespace name, also
known as the .NET namespace name, which is the name that appears in a C# namespace
definition or in a using directive:
C#

xmlns:local="clr-namespace:XamlSamples"

You can also define XML namespace declarations for .NET namespaces. For example,
here’s a sys prefix for the standard .NET System namespace, which is in the netstandard
assembly. Because this is another assembly, you must also specify the assembly name, in
this case netstandard :

C#

xmlns:sys="clr-namespace:System;assembly=netstandard"

7 Note

The keyword clr-namespace is followed by a colon and then the .NET namespace
name, followed by a semicolon, the keyword assembly , an equal sign, and the
assembly name.

The static fields can then be consumed after declaring the XML namespace:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:XamlSamples"

xmlns:sys="clr-namespace:System;assembly=netstandard"

x:Class="XamlSamples.StaticConstantsPage"

Title="Static Constants Page"

Padding="5,25,5,0">

<StackLayout>

<Label Text="Hello, XAML!"

TextColor="{x:Static local:AppConstants.BackgroundColor}"

BackgroundColor="{x:Static
local:AppConstants.ForegroundColor}"

FontAttributes="Bold"

FontSize="30"

HorizontalOptions="Center" />

<BoxView WidthRequest="{x:Static sys:Math.PI}"

HeightRequest="{x:Static sys:Math.E}"

Color="{x:Static local:AppConstants.ForegroundColor}"

HorizontalOptions="Center"

VerticalOptions="CenterAndExpand"

Scale="100" />

</StackLayout>

</ContentPage>

In this example, the BoxView dimensions are set to Math.PI and Math.E , but scaled by a
factor of 100:

Other markup extensions


Several markup extensions are intrinsic to XAML and supported in .NET MAUI XAML.
Some of these are not used very often but are essential when you need them:

If a property has a non- null value by default but you want to set it to null , set it
to the {x:Null} markup extension.
If a property is of type Type , you can assign it to a Type object using the markup
extension {x:Type someClass} .
You can define arrays in XAML using the x:Array markup extension. This markup
extension has a required attribute named Type that indicates the type of the
elements in the array.

For more information about XAML markup extensions, see Consume XAML markup
extensions.

Next steps
.NET MAUI data bindings allow properties of two objects to be linked so that a change
in one causes a change in the other.

Data binding basics


Data binding basics
Article • 04/03/2023 • 10 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) data bindings allow properties of two objects
to be linked so that a change in one causes a change in the other. This is a very valuable
tool, and while data bindings can be defined entirely in code, XAML provides shortcuts
and convenience.

Data bindings
Data bindings connect properties of two objects, called the source and the target. In
code, two steps are required:

1. The BindingContext property of the target object must be set to the source object,
2. The SetBinding method (often used in conjunction with the Binding class) must
be called on the target object to bind a property of that object to a property of the
source object.

The target property must be a bindable property, which means that the target object
must derive from BindableObject. A property of Label, such as Text , is associated with
the bindable property TextProperty .

In XAML, you must also perform the same two steps that are required in code, except
that the Binding markup extension takes the place of the SetBinding call and the
Binding class. However, when you define data bindings in XAML, there are multiple
ways to set the BindingContext of the target object. Sometimes it’s set from the code-
behind file, sometimes using a StaticResource or x:Static markup extension, and
sometimes as the content of BindingContext property-element tags.

View-to-view bindings
You can define data bindings to link properties of two views on the same page. In this
case, you set the BindingContext of the target object using the x:Reference markup
extension.

The following example contains a Slider and two Label views, one of which is rotated by
the Slider value and another which displays the Slider value:
XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.SliderBindingsPage"

Title="Slider Bindings Page">

<StackLayout>

<Label Text="ROTATION"

BindingContext="{x:Reference slider}"

Rotation="{Binding Path=Value}"

FontAttributes="Bold"

FontSize="18"

HorizontalOptions="Center"

VerticalOptions="Center" />

<Slider x:Name="slider"

Maximum="360"

VerticalOptions="Center" />

<Label BindingContext="{x:Reference slider}"

Text="{Binding Value, StringFormat='The angle is {0:F0}


degrees'}"

FontAttributes="Bold"

FontSize="18"

HorizontalOptions="Center"

VerticalOptions="Center" />

</StackLayout>

</ContentPage>

The Slider contains an x:Name attribute that is referenced by the two Label views using
the x:Reference markup extension. The x:Reference binding extension defines a
property named Name to set to the name of the referenced element, in this case slider .
However, the ReferenceExtension class that defines the x:Reference markup extension
also defines a ContentProperty attribute for Name , which means that it isn’t explicitly
required.

The Binding markup extension itself can have several properties, just like the
BindingBase and Binding class. The ContentProperty for Binding is Path , but the

“Path=” part of the markup extension can be omitted if the path is the first item in the
Binding markup extension.

The second Binding markup extension sets the StringFormat property. In .NET MAUI,
bindings do not perform any implicit type conversions, and if you need to display a non-
string object as a string you must provide a type converter or use StringFormat .

) Important

Formatting strings must be placed in single quotation marks.


Binding mode
A single view can have data bindings on several of its properties. However, each view
can have only one BindingContext , so multiple data bindings on that view must all
reference properties of the same object.

The solution to this and other problems involves the Mode property, which is set to a
member of the BindingMode enumeration:

Default

OneWay — values are transferred from the source to the target

OneWayToSource — values are transferred from the target to the source


TwoWay — values are transferred both ways between source and target

OneTime — data goes from source to target, but only when the BindingContext
changes

The following example demonstrates one common use of the OneWayToSource and
TwoWay binding modes:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="XamlSamples.SliderTransformsPage"

Padding="5"

Title="Slider Transforms Page">

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*" />

<ColumnDefinition Width="Auto" />

</Grid.ColumnDefinitions>

<!-- Scaled and rotated Label -->

<Label x:Name="label"

Text="TEXT"

HorizontalOptions="Center"

VerticalOptions="CenterAndExpand" />

<!-- Slider and identifying Label for Scale -->

<Slider x:Name="scaleSlider"

BindingContext="{x:Reference label}"

Grid.Row="1" Grid.Column="0"

Maximum="10"

Value="{Binding Scale, Mode=TwoWay}" />

<Label BindingContext="{x:Reference scaleSlider}"

Text="{Binding Value, StringFormat='Scale = {0:F1}'}"

Grid.Row="1" Grid.Column="1"

VerticalTextAlignment="Center" />

<!-- Slider and identifying Label for Rotation -->

<Slider x:Name="rotationSlider"

BindingContext="{x:Reference label}"

Grid.Row="2" Grid.Column="0"

Maximum="360"

Value="{Binding Rotation, Mode=OneWayToSource}" />

<Label BindingContext="{x:Reference rotationSlider}"

Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"

Grid.Row="2" Grid.Column="1"

VerticalTextAlignment="Center" />

<!-- Slider and identifying Label for RotationX -->

<Slider x:Name="rotationXSlider"

BindingContext="{x:Reference label}"

Grid.Row="3" Grid.Column="0"

Maximum="360"

Value="{Binding RotationX, Mode=OneWayToSource}" />

<Label BindingContext="{x:Reference rotationXSlider}"

Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"

Grid.Row="3" Grid.Column="1"

VerticalTextAlignment="Center" />

<!-- Slider and identifying Label for RotationY -->

<Slider x:Name="rotationYSlider"

BindingContext="{x:Reference label}"

Grid.Row="4" Grid.Column="0"

Maximum="360"

Value="{Binding RotationY, Mode=OneWayToSource}" />

<Label BindingContext="{x:Reference rotationYSlider}"

Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"

Grid.Row="4" Grid.Column="1"

VerticalTextAlignment="Center" />

</Grid>

</ContentPage>

In this example, four Slider views are intended to control the Scale , Rotate , RotateX ,
and RotateY properties of a Label. At first, it seems as if these four properties of the
Label should be data-binding targets because each is being set by a Slider. However, the
BindingContext of Label can be only one object, and there are four different sliders. For

that reason, the BindingContext of each of the four sliders is set to the Label, and the
bindings are set on the Value properties of the sliders. By using the OneWayToSource and
TwoWay modes, these Value properties can set the source properties, which are the

Scale , Rotate , RotateX , and RotateY properties of the Label.

The bindings on three of the Slider views are OneWayToSource , meaning that the Slider
value causes a change in the property of its BindingContext , which is the Label named
label . These three Slider views cause changes to the Rotate , RotateX , and RotateY

properties of the Label:

However, the binding for the Scale property is TwoWay . This is because the Scale
property has a default value of 1, and using a TwoWay binding causes the Slider initial
value to be set at 1 rather than 0. If that binding were OneWayToSource , the Scale
property would initially be set to 0 from the Slider default value. The Label would not be
visible

7 Note

The VisualElement class also has ScaleX and ScaleY properties, which scale the
VisualElement on the x-axis and y-axis respectively.

Bindings and collections


ListView defines an ItemsSource property of type IEnumerable , and it displays the items
in that collection. These items can be objects of any type. By default, ListView uses the
ToString method of each item to display that item. Sometimes this is just what you

want, but in many cases, ToString returns only the fully-qualified class name of the
object.

However, the items in the ListView collection can be displayed any way you want
through the use of a template, which involves a class that derives from Cell. The
template is cloned for every item in the ListView, and data bindings that have been set
on the template are transferred to the individual clones. Custom cells can be created for
items using the ViewCell class.

ListView can display a list of every named color that's available in .NET MAUI, with the
help of the NamedColor class:

C#

using System.Reflection;

using System.Text;

namespace XamlSamples

public class NamedColor

public string Name { get; private set; }

public string FriendlyName { get; private set; }

public Color Color { get; private set; }

// Expose the Color fields as properties

public float Red => Color.Red;

public float Green => Color.Green;

public float Blue => Color.Blue;

public static IEnumerable<NamedColor> All { get; private set; }

static NamedColor()

List<NamedColor> all = new List<NamedColor>();

StringBuilder stringBuilder = new StringBuilder();

// Loop through the public static fields of the Color structure.

foreach (FieldInfo fieldInfo in


typeof(Colors).GetRuntimeFields())

if (fieldInfo.IsPublic &&

fieldInfo.IsStatic &&

fieldInfo.FieldType == typeof(Color))

// Convert the name to a friendly name.

string name = fieldInfo.Name;

stringBuilder.Clear();

int index = 0;

foreach (char ch in name)

if (index != 0 && Char.IsUpper(ch))

stringBuilder.Append(' ');

stringBuilder.Append(ch);

index++;

// Instantiate a NamedColor object.

NamedColor namedColor = new NamedColor

Name = name,

FriendlyName = stringBuilder.ToString(),

Color = (Color)fieldInfo.GetValue(null)

};

// Add it to the collection.

all.Add(namedColor);

all.TrimExcess();

All = all;

Each NamedColor object has Name and FriendlyName properties of type string , a Color
property of type Color, and Red , Green , and Blue properties. In addition, the
NamedColor static constructor creates an IEnumerable<NamedColor> collection that
contains NamedColor objects corresponding to the fields of type Color in the Colors
class, and assigns it to its public static All property.

Setting the static NamedColor.All property to the ItemsSource of a ListView can be


achieved using the x:Static markup extension:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"

x:Class="XamlSamples.ListViewDemoPage"

Title="ListView Demo Page">

<ListView ItemsSource="{x:Static local:NamedColor.All}" />

</ContentPage>

The result establishes that the items are of type XamlSamples.NamedColor :

To define a template for the items, the ItemTemplate should be set to a DataTemplate
that references a ViewCell. The ViewCell should define a layout of one or more views to
display each item:

XAML

<ListView ItemsSource="{x:Static local:NamedColor.All}">

<ListView.ItemTemplate>

<DataTemplate>

<ViewCell>

<Label Text="{Binding FriendlyName}" />

</ViewCell>

</DataTemplate>

</ListView.ItemTemplate>

</ListView>

7 Note
The binding source for cells, and children of cells, is the ListView.ItemsSource
collection.

In this example, the Label element is set to the View property of the ViewCell. The
ViewCell.View tags are not needed because the View property is the content property
of ViewCell. This XAML displays the FriendlyName property of each NamedColor object:

The item template can be expanded to display more information and the actual color:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:XamlSamples"

x:Class="XamlSamples.ListViewDemoPage"

Title="ListView Demo Page">

<ContentPage.Resources>

<x:Double x:Key="boxSize">50</x:Double>

<x:Int32 x:Key="rowHeight">60</x:Int32>

<local:FloatToIntConverter x:Key="intConverter" />

</ContentPage.Resources>

<ListView ItemsSource="{x:Static local:NamedColor.All}"

RowHeight="{StaticResource rowHeight}">

<ListView.ItemTemplate>

<DataTemplate>

<ViewCell>

<StackLayout Padding="5, 5, 0, 5"

Orientation="Horizontal"

Spacing="15">

<BoxView WidthRequest="{StaticResource boxSize}"

HeightRequest="{StaticResource boxSize}"

Color="{Binding Color}" />

<StackLayout Padding="5, 0, 0, 0"

VerticalOptions="Center">

<Label Text="{Binding FriendlyName}"

FontAttributes="Bold"
FontSize="14" />

<StackLayout Orientation="Horizontal"

Spacing="0">

<Label Text="{Binding Red,

Converter=
{StaticResource intConverter},

ConverterParameter=255,

StringFormat='R=
{0:X2}'}" />

<Label Text="{Binding Green,

Converter=
{StaticResource intConverter},

ConverterParameter=255,

StringFormat=', G=
{0:X2}'}" />

<Label Text="{Binding Blue,

Converter=
{StaticResource intConverter},

ConverterParameter=255,

StringFormat=', B=
{0:X2}'}" />

</StackLayout>

</StackLayout>

</StackLayout>

</ViewCell>

</DataTemplate>

</ListView.ItemTemplate>

</ListView>

</ContentPage>

Binding value converters


The previous XAML example displays the individual Red , Green , and Blue properties of
each NamedColor . These properties are of type float and range from 0 to 1. If you want
to display the hexadecimal values, you can’t simply use StringFormat with an “X2”
formatting specification. That only works for integers and besides, the float values
need to be multiplied by 255.

This issue can be solved with a value converter, also called a binding converter. This is a
class that implements the IValueConverter interface, which means it has two methods
named Convert and ConvertBack . The Convert method is called when a value is
transferred from source to target. The ConvertBack method is called for transfers from
target to source in OneWayToSource or TwoWay bindings:

C#

using System.Globalization;

namespace XamlSamples

public class FloatToIntConverter : IValueConverter

public object Convert(object value, Type targetType, object


parameter, CultureInfo culture)

float multiplier;

if (!float.TryParse(parameter as string, out multiplier))

multiplier = 1;

return (int)Math.Round(multiplier * (float)value);

public object ConvertBack(object value, Type targetType, object


parameter, CultureInfo culture)

float divider;

if (!float.TryParse(parameter as string, out divider))

divider = 1;

return ((float)(int)value) / divider;

7 Note

The ConvertBack method does not play a role in this example because the bindings
are only one way from source to target.
A binding references a binding converter with the Converter property. A binding
converter can also accept a parameter specified with the ConverterParameter property.
For some versatility, this is how the multiplier is specified. The binding converter checks
the converter parameter for a valid float value.

The converter is instantiated in the page's resource dictionary so it can be shared


among multiple bindings:

XAML

<local:FloatToIntConverter x:Key="intConverter" />

Three data bindings reference this single instance:

XAML

<Label Text="{Binding Red,

Converter={StaticResource intConverter},

ConverterParameter=255,

StringFormat='R={0:X2}'}" />

The item template dsplays the color, its friendly name, and its RGB values:
The ListView can handle changes that dynamically occur in the underlying data, but only
if you take certain steps. If the collection of items assigned to the ItemsSource property
of the ListView changes during runtime, use an ObservableCollection class for these
items. ObservableCollection implements the INotifyCollectionChanged interface, and
ListView will install a handler for the CollectionChanged event.

If properties of the items themselves change during runtime, then the items in the
collection should implement the INotifyPropertyChanged interface and signal changes
to property values using the PropertyChanged event.

Next steps
Data bindings provide a powerful mechanism for linking properties between two objects
within a page, or between visual objects and underlying data. But when the application
begins working with data sources, a popular app architectural pattern begins to emerge
as a useful paradigm.
Data binding and MVVM
Data binding and MVVM
Article • 04/03/2023 • 10 minutes to read

Browse the sample

The Model-View-ViewModel (MVVM) pattern enforces a separation between three


software layers — the XAML user interface, called the view, the underlying data, called
the model, and an intermediary between the view and the model, called the viewmodel.
The view and the viewmodel are often connected through data bindings defined in
XAML. The BindingContext for the view is usually an instance of the viewmodel.

) Important

.NET Multi-platform App UI (.NET MAUI) marshals binding updates to the UI thread.
When using MVVM this enables you to update data-bound viewmodel properties
from any thread, with .NET MAUI's binding engine bringing the updates to the UI
thread.

Simple MVVM
In XAML markup extensions you saw how to define a new XML namespace declaration
to allow a XAML file to reference classes in other assemblies. The following example
uses the x:Static markup extension to obtain the current date and time from the static
DateTime.Now property in the System namespace:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:sys="clr-namespace:System;assembly=netstandard"

x:Class="XamlSamples.OneShotDateTimePage"

Title="One-Shot DateTime Page">

<VerticalStackLayout BindingContext="{x:Static sys:DateTime.Now}"

Spacing="25" Padding="30,0"

VerticalOptions="Center"
HorizontalOptions="Center">

<Label Text="{Binding Year, StringFormat='The year is {0}'}" />

<Label Text="{Binding StringFormat='The month is {0:MMMM}'}" />

<Label Text="{Binding Day, StringFormat='The day is {0}'}" />

<Label Text="{Binding StringFormat='The time is {0:T}'}" />

</VerticalStackLayout>

</ContentPage>

In this example, the retrieved DateTime value is set as the BindingContext on a


StackLayout. When you set the BindingContext on an element, it is inherited by all the
children of that element. This means that all the children of the StackLayout have the
same BindingContext , and they can contain bindings to properties of that object:

However, the problem is that the date and time are set once when the page is
constructed and initialized, and never change.

A XAML page can display a clock that always shows the current time, but it requires
additional code. The MVVM pattern is a natural choice for .NET MAUI apps when data
binding from properties between visual objects and the underlying data. When thinking
in terms of MVVM, the model and viewmodel are classes written entirely in code. The
view is often a XAML file that references properties defined in the viewmodel through
data bindings. In MVVM, a model is ignorant of the viewmodel, and a viewmodel is
ignorant of the view. However, often you tailor the types exposed by the viewmodel to
the types associated with the UI.

7 Note

In simple examples of MVVM, such as those shown here, often there is no model at
all, and the pattern involves just a view and viewmodel linked with data bindings.

The following example shows a viewmodel for a clock, with a single property named
DateTime that's updated every second:

C#
using System.ComponentModel;

using System.Runtime.CompilerServices;

namespace XamlSamples;

class ClockViewModel: INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

private DateTime _dateTime;

private Timer _timer;

public DateTime DateTime

get => _dateTime;

set

if (_dateTime != value)

_dateTime = value;

OnPropertyChanged(); // reports this property

public ClockViewModel()

this.DateTime = DateTime.Now;

// Update the DateTime property every second.

_timer = new Timer(new TimerCallback((s) => this.DateTime =


DateTime.Now),

null, TimeSpan.Zero, TimeSpan.FromSeconds(1));

~ClockViewModel() =>

_timer.Dispose();

public void OnPropertyChanged([CallerMemberName] string name = "") =>

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

Viewmodels typically implement the INotifyPropertyChanged interface, which provides


the ability for a class to raise the PropertyChanged event whenever one of its properties
changes. The data binding mechanism in .NET MAUI attaches a handler to this
PropertyChanged event so it can be notified when a property changes and keep the

target updated with the new value. In the previous code example, the
OnPropertyChanged method handles raising the event while automatically determining

the property source name: DateTime .


The following example shows XAML that consumes ClockViewModel :

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:XamlSamples"

x:Class="XamlSamples.ClockPage"

Title="Clock Page">

<ContentPage.BindingContext>

<local:ClockViewModel />

</ContentPage.BindingContext>

<Label Text="{Binding DateTime, StringFormat='{0:T}'}"

FontSize="18"

HorizontalOptions="Center"

VerticalOptions="Center" />

</ContentPage>

In this example, ClockViewModel is set to the BindingContext of the ContentPage using


property element tags. Alternatively, the code-behind file could instantiate the
viewmodel.

The Binding markup extension on the Text property of the Label formats the DateTime
property. The following screenshot shows the result:

In addition, it’s possible to access individual properties of the DateTime property of the
viewmodel by separating the properties with periods:

XAML

<Label Text="{Binding DateTime.Second, StringFormat='{0}'}" … >

Interactive MVVM
MVVM is often used with two-way data bindings for an interactive view based on an
underlying data model.

The following example shows the HslViewModel that converts a Color value into Hue ,
Saturation , and Luminosity values, and back again:
C#

using System.ComponentModel;

using System.Runtime.CompilerServices;

namespace XamlSamples;

class HslViewModel: INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

private float _hue, _saturation, _luminosity;

private Color _color;

public float Hue

get => _hue;

set

if (_hue != value)

Color = Color.FromHsla(value, _saturation, _luminosity);

public float Saturation

get => _saturation;

set

if (_saturation != value)

Color = Color.FromHsla(_hue, value, _luminosity);

public float Luminosity

get => _luminosity;

set

if (_luminosity != value)

Color = Color.FromHsla(_hue, _saturation, value);

public Color Color

get => _color;

set

if (_color != value)

_color = value;

_hue = _color.GetHue();

_saturation = _color.GetSaturation();

_luminosity = _color.GetLuminosity();

OnPropertyChanged("Hue");

OnPropertyChanged("Saturation");

OnPropertyChanged("Luminosity");

OnPropertyChanged(); // reports this property

public void OnPropertyChanged([CallerMemberName] string name = "") =>

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

In this example, changes to the Hue , Saturation , and Luminosity properties cause the
Color property to change, and changes to the Color property causes the other three

properties to change. This might seem like an infinite loop, except that the viewmodel
doesn't invoke the PropertyChanged event unless the property has changed.

The following XAML example contains a BoxView whose Color property is bound to the
Color property of the viewmodel, and three Slider and three Label views bound to the
Hue , Saturation , and Luminosity properties:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:XamlSamples"

x:Class="XamlSamples.HslColorScrollPage"

Title="HSL Color Scroll Page">

<ContentPage.BindingContext>

<local:HslViewModel Color="Aqua" />

</ContentPage.BindingContext>

<VerticalStackLayout Padding="10, 0, 10, 30">

<BoxView Color="{Binding Color}"

HeightRequest="100"

WidthRequest="100"

HorizontalOptions="Center" />

<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}"

HorizontalOptions="Center" />

<Slider Value="{Binding Hue}"

Margin="20,0,20,0" />

<Label Text="{Binding Saturation, StringFormat='Saturation =


{0:F2}'}"

HorizontalOptions="Center" />

<Slider Value="{Binding Saturation}"

Margin="20,0,20,0" />

<Label Text="{Binding Luminosity, StringFormat='Luminosity =


{0:F2}'}"

HorizontalOptions="Center" />

<Slider Value="{Binding Luminosity}"

Margin="20,0,20,0" />

</VerticalStackLayout>

</ContentPage>

The binding on each Label is the default OneWay . It only needs to display the value.
However, the default binding on each Slider is TwoWay . This allows the Slider to be
initialized from the viewmodel. When the viewmodel is instantiated it's Color property
is set to Aqua . A change in a Slider sets a new value for the property in the viewmodel,
which then calculates a new color:

Commanding
Sometimes an app has needs that go beyond property bindings by requiring the user to
initiate commands that affect something in the viewmodel. These commands are
generally signaled by button clicks or finger taps, and traditionally they are processed in
the code-behind file in a handler for the Clicked event of the Button or the Tapped
event of a TapGestureRecognizer.

The commanding interface provides an alternative approach to implementing


commands that is much better suited to the MVVM architecture. The viewmodel can
contain commands, which are methods that are executed in reaction to a specific
activity in the view such as a Button click. Data bindings are defined between these
commands and the Button.

To allow a data binding between a Button and a viewmodel, the Button defines two
properties:

Command of type System.Windows.Input.ICommand

CommandParameter of type Object

7 Note

Many other controls also define Command and CommandParameter properties.

The ICommand interface is defined in the System.Windows.Input namespace, and


consists of two methods and one event:

void Execute(object arg)

bool CanExecute(object arg)


event EventHandler CanExecuteChanged

The viewmodel can define properties of type ICommand . You can then bind these
properties to the Command property of each Button or other element, or perhaps a
custom view that implements this interface. You can optionally set the CommandParameter
property to identify individual Button objects (or other elements) that are bound to this
viewmodel property. Internally, the Button calls the Execute method whenever the user
taps the Button, passing to the Execute method its CommandParameter .

The CanExecute method and CanExecuteChanged event are used for cases where a Button
tap might be currently invalid, in which case the Button should disable itself. The Button
calls CanExecute when the Command property is first set and whenever the
CanExecuteChanged event is raised. If CanExecute returns false , the Button disables itself

and doesn’t generate Execute calls.

You can use the Command or Command<T> class included in .NET MAUI to implement the
ICommand interface. These two classes define several constructors plus a
ChangeCanExecute method that the viewmodel can call to force the Command object to

raise the CanExecuteChanged event.

The following example shows a viewmodel for a simple keypad that is intended for
entering telephone numbers:

C#

using System.ComponentModel;

using System.Runtime.CompilerServices;

using System.Windows.Input;

namespace XamlSamples;

class KeypadViewModel: INotifyPropertyChanged

public event PropertyChangedEventHandler PropertyChanged;

private string _inputString = "";

private string _displayText = "";

private char[] _specialChars = { '*', '#' };

public ICommand AddCharCommand { get; private set; }

public ICommand DeleteCharCommand { get; private set; }

public string InputString

get => _inputString;

private set

if (_inputString != value)

_inputString = value;

OnPropertyChanged();

DisplayText = FormatText(_inputString);

// Perhaps the delete button must be enabled/disabled.

((Command)DeleteCharCommand).ChangeCanExecute();

public string DisplayText

get => _displayText;

private set

if (_displayText != value)

_displayText = value;

OnPropertyChanged();

public KeypadViewModel()

// Command to add the key to the input string

AddCharCommand = new Command<string>((key) => InputString += key);

// Command to delete a character from the input string when allowed

DeleteCharCommand =

new Command(

// Command will strip a character from the input string

() => InputString = InputString.Substring(0,


InputString.Length - 1),

// CanExecute is processed here to return true when there's


something to delete

() => InputString.Length > 0

);

string FormatText(string str)

bool hasNonNumbers = str.IndexOfAny(_specialChars) != -1;

string formatted = str;

// Format the string based on the type of data and the length

if (hasNonNumbers || str.Length < 4 || str.Length > 10)

// Special characters exist, or the string is too small or large


for special formatting

// Do nothing

else if (str.Length < 8)

formatted = string.Format("{0}-{1}", str.Substring(0, 3),


str.Substring(3));

else

formatted = string.Format("({0}) {1}-{2}", str.Substring(0, 3),


str.Substring(3, 3), str.Substring(6));

return formatted;

public void OnPropertyChanged([CallerMemberName] string name = "") =>

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

In this example, the Execute and CanExecute methods for the commands are defined as
lambda functions in the constructor. The viewmodel assumes that the AddCharCommand
property is bound to the Command property of several buttons (or anything other
controls that have a command interface), each of which is identified by the
CommandParameter . These buttons add characters to an InputString property, which is
then formatted as a phone number for the DisplayText property. There's also a second
property of type ICommand named DeleteCharCommand . This is bound to a back-spacing
button, but the button should be disabled if there are no characters to delete.

The following example shows the XAML that consumes the KeypadViewModel :

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:XamlSamples"

x:Class="XamlSamples.KeypadPage"

Title="Keypad Page">

<ContentPage.BindingContext>

<local:KeypadViewModel />

</ContentPage.BindingContext>

<Grid HorizontalOptions="Center" VerticalOptions="Center">

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="80" />

<ColumnDefinition Width="80" />

<ColumnDefinition Width="80" />

</Grid.ColumnDefinitions>

<Label Text="{Binding DisplayText}"

Margin="0,0,10,0" FontSize="20"
LineBreakMode="HeadTruncation"

VerticalTextAlignment="Center" HorizontalTextAlignment="End"

Grid.ColumnSpan="2" />

<Button Text="&#x21E6;" Command="{Binding DeleteCharCommand}"


Grid.Column="2"/>

<Button Text="1" Command="{Binding AddCharCommand}"


CommandParameter="1" Grid.Row="1" />

<Button Text="2" Command="{Binding AddCharCommand}"


CommandParameter="2" Grid.Row="1" Grid.Column="1" />

<Button Text="3" Command="{Binding AddCharCommand}"


CommandParameter="3" Grid.Row="1" Grid.Column="2" />

<Button Text="4" Command="{Binding AddCharCommand}"


CommandParameter="4" Grid.Row="2" />

<Button Text="5" Command="{Binding AddCharCommand}"


CommandParameter="5" Grid.Row="2" Grid.Column="1" />

<Button Text="6" Command="{Binding AddCharCommand}"


CommandParameter="6" Grid.Row="2" Grid.Column="2" />

<Button Text="7" Command="{Binding AddCharCommand}"


CommandParameter="7" Grid.Row="3" />

<Button Text="8" Command="{Binding AddCharCommand}"


CommandParameter="8" Grid.Row="3" Grid.Column="1" />

<Button Text="9" Command="{Binding AddCharCommand}"


CommandParameter="9" Grid.Row="3" Grid.Column="2" />

<Button Text="*" Command="{Binding AddCharCommand}"


CommandParameter="*" Grid.Row="4" />

<Button Text="0" Command="{Binding AddCharCommand}"


CommandParameter="0" Grid.Row="4" Grid.Column="1" />

<Button Text="#" Command="{Binding AddCharCommand}"


CommandParameter="#" Grid.Row="4" Grid.Column="2" />

</Grid>

</ContentPage>

In this example, the Command property of the first Button that is bound to the
DeleteCharCommand . The other buttons are bound to the AddCharCommand with a

CommandParameter that's the same as the character that appears on the Button:
XAML compilation
Article • 09/06/2022 • 2 minutes to read

.NET Multi-platform App UI (.NET MAUI) XAML is compiled directly into intermediate
language (IL) with the XAML compiler (XAMLC). XAML compilation offers a number of
benefits:

It performs compile-time checking of XAML, notifying you of any errors.


It removes some of the load and instantiation time for XAML elements.
It helps to reduce the file size of the final assembly by no longer including .xaml
files.

XAML compilation is enabled by default in .NET MAUI apps. For apps built using debug
configuration, XAML compilation provides compile-time validation of XAML, but does
not convert the XAML to IL in the assembly. Instead, XAML files are included as
embedded resources in the app package, and evaluated at runtime. For apps built using
release configuration, XAML compilation provides compile-time validation of XAML, and
converts the XAML to IL that's written to the assembly. However, XAML compilation
behavior can be overridden in both configurations with the XamlCompilationAttribute
class.

Enable compilation
XAML compilation can be enabled by passing XamlCompilationOptions.Compile to the
XamlCompilationAttribute :

C#

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]

In this example, XAML compilation is enabled for all of the XAML contained within the
assembly, with XAML errors being reported at compile-time rather than runtime.

 Tip

While the XamlCompilationAttribute can be placed anywhere, a good place to put


it is in MauiProgram.cs.

XAML compilation can also be enabled at the type level:


C#

[XamlCompilation (XamlCompilationOptions.Compile)]

public partial class MyPage : ContentPage

...

In this example, XAML compilation is enabled only for the MyPage class.

7 Note

Compiled bindings can be enabled to improve data binding performance in .NET


MAUI applications. For more information, see Compiled Bindings.

Disable compilation
XAML compilation can be disabled by passing XamlCompilationOptions.Skip to the
XamlCompilationAttribute :

C#

[assembly: XamlCompilation(XamlCompilationOptions.Skip)]

In this example, XAML compilation is disabled within the assembly, with XAML errors
being reported at runtime rather than compile-time.

XAML compilation can also be disabled at the type level:

C#

[XamlCompilation (XamlCompilationOptions.Skip)]

public partial class MyPage : ContentPage

...

In this example, XAML compilation is disabled only for the MyPage class.

2 Warning

Disabling XAML compilation is not recommended because XAML is then parsed


and interpreted at runtime, which will reduce app performance.
Field modifiers
Article • 06/24/2022 • 2 minutes to read

The .NET Multi-platform App UI (.NET MAUI) x:FieldModifier attribute specifies the
access level for generated fields for named XAML elements.

Valid values of the x:FieldModifier attribute are:

Private – specifies that the generated field for the XAML element is accessible

only within the body of the class in which it is declared.


Public – specifies that the generated field for the XAML element has no access
restrictions.
Protected – specifies that the generated field for the XAML element is accessible
within its class and by derived class instances.
Internal – specifies that the generated field for the XAML element is accessible

only within types in the same assembly.


NotPublic – identical to Internal .

By default, if the value of the attribute isn't set, the generated field for the element will
be private .

7 Note

The value of the attribute can use any casing, as it will be converted to lowercase by
.NET MAUI.

The following conditions must be met for an x:FieldModifier attribute to be processed:

The top-level XAML element must be a valid x:Class .


The current XAML element has an x:Name specified.

The following XAML shows examples of setting the attribute:

XAML

<Label x:Name="privateLabel" />

<Label x:Name="internalLabel" x:FieldModifier="NotPublic" />

<Label x:Name="publicLabel" x:FieldModifier="Public" />

) Important
The x:FieldModifier attribute cannot be used to specify the access level of a .NET
MAUI XAML class.
Generics
Article • 04/03/2023 • 3 minutes to read

.NET Multi-platform App UI (.NET MAUI) XAML provides support for consuming generic
CLR types by specifying the generic constraints as type arguments. This support is
provided by the x:TypeArguments directive, which passes the constraining type
arguments of a generic to the constructor of the generic type.

Type arguments are specified as a string, and are typically prefixed, such as sys:String
and sys:Int32 . Prefixing is required because the typical types of CLR generic constraints
come from libraries that are not mapped to the default .NET MAUI namespaces.
However, the XAML 2009 built-in types such as x:String and x:Int32 , can also be
specified as type arguments, where x is the XAML language namespace for XAML 2009.
For more information about the XAML 2009 built-in types, see XAML 2009 Language
Primitives.

) Important

Defining generic classes in .NET MAUI XAML, with the x:TypeArguments directive, is
unsupported.

Multiple type arguments can be specified by using a comma delimiter. In addition, if a


generic constraint uses generic types, the nested constraint type arguments should be
contained in parentheses.

7 Note

The x:Type markup extension supplies a Common Language Runtime (CLR) type
reference for a generic type, and has a similar function to the typeof operator in
C#. For more information, see x:Type markup extension.

Single primitive type argument


A single primitive type argument can be specified as a prefixed string argument using
the x:TypeArguments directive:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:scg="clr-
namespace:System.Collections.Generic;assembly=netstandard"

...>

<CollectionView>

<CollectionView.ItemsSource>

<scg:List x:TypeArguments="x:String">

<x:String>Baboon</x:String>

<x:String>Capuchin Monkey</x:String>

<x:String>Blue Monkey</x:String>

<x:String>Squirrel Monkey</x:String>

<x:String>Golden Lion Tamarin</x:String>


<x:String>Howler Monkey</x:String>

<x:String>Japanese Macaque</x:String>

</scg:List>

</CollectionView.ItemsSource>

</CollectionView>

</ContentPage>

In this example, System.Collections.Generic is defined as the scg XAML namespace.


The CollectionView.ItemsSource property is set to a List<T> that's instantiated with a
string type argument, using the XAML 2009 built-in x:String type. The List<string>
collection is initialized with multiple string items.

Alternatively, but equivalently, the List<T> collection can be instantiated with the CLR
String type:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:scg="clr-
namespace:System.Collections.Generic;assembly=netstandard"

xmlns:sys="clr-namespace:System;assembly=netstandard"

...>

<CollectionView>

<CollectionView.ItemsSource>

<scg:List x:TypeArguments="sys:String">

<sys:String>Baboon</sys:String>

<sys:String>Capuchin Monkey</sys:String>
<sys:String>Blue Monkey</sys:String>

<sys:String>Squirrel Monkey</sys:String>
<sys:String>Golden Lion Tamarin</sys:String>

<sys:String>Howler Monkey</sys:String>

<sys:String>Japanese Macaque</sys:String>

</scg:List>

</CollectionView.ItemsSource>

</CollectionView>

</ContentPage>

Single object type argument


A single object type argument can be specified as a prefixed string argument using the
x:TypeArguments directive:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:models="clr-namespace:GenericsDemo.Models"

xmlns:scg="clr-
namespace:System.Collections.Generic;assembly=netstandard"

...>

<CollectionView>

<CollectionView.ItemsSource>

<scg:List x:TypeArguments="models:Monkey">

<models:Monkey Name="Baboon"

Location="Africa and Asia"

ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_an
ubis_%28Serengeti%2C_2009%29.jpg/200px-
Papio_anubis_%28Serengeti%2C_2009%29.jpg" />

<models:Monkey Name="Capuchin Monkey"

Location="Central and South America"

ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin
_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg" />

<models:Monkey Name="Blue Monkey"

Location="Central and East Africa"

ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/BlueMonk
ey.jpg/220px-BlueMonkey.jpg" />

</scg:List>

</CollectionView.ItemsSource>

<CollectionView.ItemTemplate>

<DataTemplate>

<Grid Padding="10">

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="Auto" />

</Grid.ColumnDefinitions>

<Image Grid.RowSpan="2"

Source="{Binding ImageUrl}"

Aspect="AspectFill"

HeightRequest="60"

WidthRequest="60" />

<Label Grid.Column="1"

Text="{Binding Name}"

FontAttributes="Bold" />

<Label Grid.Row="1"

Grid.Column="1"

Text="{Binding Location}"

FontAttributes="Italic"

VerticalOptions="End" />

</Grid>

</DataTemplate>

</CollectionView.ItemTemplate>

</CollectionView>

</ContentPage>

In this example, GenericsDemo.Models is defined as the models XAML namespace, and


System.Collections.Generic is defined as the scg XAML namespace. The

CollectionView.ItemsSource property is set to a List<T> that's instantiated with a

Monkey type argument. The List<Monkey> collection is initialized with multiple Monkey
items, and a DataTemplate that defines the appearance of each Monkey object is set as
the ItemTemplate of the CollectionView.

Multiple type arguments


Multiple type arguments can be specified as prefixed string arguments, delimited by a
comma, using the x:TypeArguments directive. When a generic constraint uses generic
types, the nested constraint type arguments are contained in parentheses:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:models="clr-namespace:GenericsDemo.Models"

xmlns:scg="clr-
namespace:System.Collections.Generic;assembly=netstandard"

...>

<CollectionView>

<CollectionView.ItemsSource>

<scg:List
x:TypeArguments="scg:KeyValuePair(x:String,models:Monkey)">

<scg:KeyValuePair x:TypeArguments="x:String,models:Monkey">

<x:Arguments>

<x:String>Baboon</x:String>

<models:Monkey Location="Africa and Asia"

ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_an
ubis_%28Serengeti%2C_2009%29.jpg/200px-
Papio_anubis_%28Serengeti%2C_2009%29.jpg" />

</x:Arguments>

</scg:KeyValuePair>

<scg:KeyValuePair x:TypeArguments="x:String,models:Monkey">

<x:Arguments>

<x:String>Capuchin Monkey</x:String>

<models:Monkey Location="Central and South America"

ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin
_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg" />

</x:Arguments>

</scg:KeyValuePair>

<scg:KeyValuePair x:TypeArguments="x:String,models:Monkey">

<x:Arguments>

<x:String>Blue Monkey</x:String>
<models:Monkey Location="Central and East Africa"

ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/8/83/BlueMonk
ey.jpg/220px-BlueMonkey.jpg" />

</x:Arguments>

</scg:KeyValuePair>

</scg:List>

</CollectionView.ItemsSource>

<CollectionView.ItemTemplate>

<DataTemplate>

<Grid Padding="10">

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="Auto" />

</Grid.ColumnDefinitions>

<Image Grid.RowSpan="2"

Source="{Binding Value.ImageUrl}"

Aspect="AspectFill"

HeightRequest="60"

WidthRequest="60" />

<Label Grid.Column="1"

Text="{Binding Key}"

FontAttributes="Bold" />

<Label Grid.Row="1"

Grid.Column="1"

Text="{Binding Value.Location}"

FontAttributes="Italic"

VerticalOptions="End" />

</Grid>

</DataTemplate>

</CollectionView.ItemTemplate>

</CollectionView>

</ContentPage

In this example, GenericsDemo.Models is defined as the models XAML namespace, and


System.Collections.Generic is defined as the scg XAML namespace. The
CollectionView.ItemsSource property is set to a List<T> that's instantiated with a
KeyValuePair<TKey, TValue> constraint, with the inner constraint type arguments

string and Monkey . The List<KeyValuePair<string,Monkey>> collection is initialized with


multiple KeyValuePair items, using the non-default KeyValuePair constructor, and a
DataTemplate that defines the appearance of each Monkey object is set as the
ItemTemplate of the CollectionView. For information on passing arguments to a non-

default constructor, see Pass constructor arguments.


Consume XAML markup extensions
Article • 04/03/2023 • 14 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) XAML markup extensions help enhance the
power and flexibility of XAML by allowing element attributes to be set from a variety of
sources.

For example, you typically set the Color property of BoxView like this:

XAML

<BoxView Color="Blue" />

However, you might prefer instead to set the Color attribute from a value stored in a
resource dictionary, or from the value of a static property of a class that you've created,
or from a property of type Color of another element on the page, or constructed from
separate hue, saturation, and luminosity values. All these options are possible using
XAML markup extensions.

A markup extension is a different way to express an attribute of an element. .NET MAUI


XAML markup extensions are usually identifiable by an attribute value that is enclosed in
curly braces:

XAML

<BoxView Color="{StaticResource themeColor}" />

Any attribute value in curly braces is always a XAML markup extension. However, XAML
markup extensions can also be referenced without the use of curly braces.

7 Note

Several XAML markup extensions are part of the XAML 2009 specification. These
appear in XAML files with the customary x namespace prefix, and are commonly
referred to with this prefix.

In addition to the markup extensions discussed in this article, the following markup
extensions are included in .NET MAUI and discussed in other articles:
StaticResource - reference objects from a resource dictionary. For more
information, see Resource dictionaries.
DynamicResource - respond to changes in objects in a resource dictionary. For
more information, see Dynamic styles.
Binding - establish a link between properties of two objects. For more information,
see Data binding.
TemplateBinding - performs data binding from a control template. For more
information, see Control templates.
RelativeSource - sets the binding source relative to the position of the binding
target. For more information, see Relative bindings.

x:Static markup extension


The x:Static markup extension is supported by the StaticExtension class. The class has
a single property named Member of type string that you set to the name of a public
constant, static property, static field, or enumeration member.

One way to use x:Static is to first define a class with some constants or static variables,
such as this AppConstants class:

C#

static class AppConstants

public static double NormalFontSize = 18;

The following XAML demonstrates the most verbose approach to instantiating the
StaticExtension class between Label.FontSize property-element tags:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:sys="clr-namespace:System;assembly=netstandard"

xmlns:local="clr-namespace:MarkupExtensions"

x:Class="MarkupExtensions.StaticDemoPage"

Title="x:Static Demo">

<StackLayout Margin="10, 0">

<Label Text="Label No. 1">

<Label.FontSize>

<x:StaticExtension
Member="local:AppConstants.NormalFontSize" />

</Label.FontSize>

</Label>

···

</StackLayout>

</ContentPage>

The XAML parser also allows the StaticExtension class to be abbreviated as x:Static :

XAML

<Label Text="Label No. 2">

<Label.FontSize>

<x:Static Member="local:AppConstants.NormalFontSize" />

</Label.FontSize>

</Label>

This syntax can be simplified even further by putting the StaticExtension class and the
member setting in curly braces. The resulting expression is set directly to the FontSize
attribute:

XAML

<Label Text="Label No. 3"

FontSize="{x:StaticExtension
Member=local:AppConstants.NormalFontSize}" />

In this example, there are no quotation marks within the curly braces. The Member
property of StaticExtension is no longer an XML attribute. It is instead part of the
expression for the markup extension.

Just as you can abbreviate x:StaticExtension to x:Static when you use it as an object
element, you can also abbreviate it in the expression within curly braces:

XAML

<Label Text="Label No. 4"

FontSize="{x:Static Member=local:AppConstants.NormalFontSize}" />

The StaticExtension class has a ContentProperty attribute referencing the property


Member , which marks this property as the class's default content property. For XAML
markup extensions expressed with curly braces, you can eliminate the Member= part of
the expression:

XAML

<Label Text="Label No. 5"

FontSize="{x:Static local:AppConstants.NormalFontSize}" />

This is the most common form of the x:Static markup extension.

The root tag of the XAML example also contains an XML namespace declaration for the
.NET System namespace. This allows the Label font size to be set to the static field
Math.PI . That results in rather small text, so the Scale property is set to Math.E :

XAML

<Label Text="&#x03C0; &#x00D7; E sized text"

FontSize="{x:Static sys:Math.PI}"

Scale="{x:Static sys:Math.E}"

HorizontalOptions="Center" />

The following screenshot shows the XAML output:

x:Reference markup extension


The x:Reference markup extension is supported by the ReferenceExtension class. The
class has a single property named Name of type string that you set to the name of an
element on the page that has been given a name with x:Name . This Name property is the
content property of ReferenceExtension, so Name= is not required when x:Reference
appears in curly braces. The x:Reference markup extension is used exclusively with data
bindings. For more information about data bindings, see Data binding.

The following XAML example shows two uses of x:Reference with data bindings, the
first where it's used to set the Source property of the Binding object, and the second
where it's used to set the BindingContext property for two data bindings:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="MarkupExtensions.ReferenceDemoPage"

x:Name="page"

Title="x:Reference Demo">

<StackLayout Margin="10, 0">

<Label Text="{Binding Source={x:Reference page},


StringFormat='The type of this page is {0}'}"

FontSize="18"

VerticalOptions="Center"

HorizontalTextAlignment="Center" />

<Slider x:Name="slider"

Maximum="360"

VerticalOptions="Center" />

<Label BindingContext="{x:Reference slider}"

Text="{Binding Value, StringFormat='{0:F0}&#x00B0;


rotation'}"

Rotation="{Binding Value}"

FontSize="24"

HorizontalOptions="Center"

VerticalOptions="Center" />

</StackLayout>

</ContentPage>

In this example, both x:Reference expressions use the abbreviated version of the
ReferenceExtension class name and eliminate the Name= part of the expression. In the
first example, the x:Reference markup extension is embedded in the Binding markup
extension and the Source and StringFormat properties are separated by commas.

The following screenshot shows the XAML output:

x:Type markup extension


The x:Type markup extension is the XAML equivalent of the C# typeof keyword. It's
supported by the TypeExtension class, which defines a property named TypeName of type
string that should be set to a class or structure name. The x:Type markup extension

returns the Type object of that class or structure. TypeName is the content property of
TypeExtension, so TypeName= is not required when x:Type appears with curly braces.
The x:Type markup extension is commonly used with the x:Array markup extension.
For more information, see x:Array markup extension.

The following XAML example demonstrates using the x:Type markup extension to
instantiate .NET MAUI objects and add them to a StackLayout. The XAML consists of
three Button elements with their Command properties set to a Binding and the
CommandParameter properties set to types of three .NET MAUI views:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="MarkupExtensions.TypeDemoPage"

Title="x:Type Demo">

<StackLayout x:Name="stackLayout"

Padding="10, 0">

<Button Text="Create a Slider"

HorizontalOptions="Center"

VerticalOptions="Center"

Command="{Binding CreateCommand}"

CommandParameter="{x:Type Slider}" />

<Button Text="Create a Stepper"

HorizontalOptions="Center"

VerticalOptions="Center"

Command="{Binding CreateCommand}"

CommandParameter="{x:Type Stepper}" />

<Button Text="Create a Switch"

HorizontalOptions="Center"

VerticalOptions="Center"

Command="{Binding CreateCommand}"

CommandParameter="{x:Type Switch}" />

</StackLayout>

</ContentPage>

The code-behind file defines and initializes the CreateCommand property:

C#

public partial class TypeDemoPage : ContentPage

public ICommand CreateCommand { get; private set; }

public TypeDemoPage()

InitializeComponent();

CreateCommand = new Command<Type>((Type viewType) =>

View view = (View)Activator.CreateInstance(viewType);

view.VerticalOptions = LayoutOptions.Center;

stackLayout.Add(view);

});

BindingContext = this;

When a Button is pressed a new instance of the CommandParameter argument is created


and added to the StackLayout. The three Button objects then share the page with
dynamically created views:

x:Array markup extension


The x:Array markup extension enables you to define an array in markup. It is supported
by the ArrayExtension class, which defines two properties:

Type of type Type , which indicates the type of the elements in the array. This

property should be set to an x:Type markup extension.


Items of type IList , which is a collection of the items themselves. This is the

content property of ArrayExtension.

The x:Array markup extension itself never appears in curly braces. Instead, x:Array
start and end tags delimit the list of items.

The following XAML example shows how to use x:Array to add items to a ListView by
setting the ItemsSource property to an array:
XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="MarkupExtensions.ArrayDemoPage"

Title="x:Array Demo Page">

<ListView Margin="10">

<ListView.ItemsSource>

<x:Array Type="{x:Type Color}">

<Color>Aqua</Color>

<Color>Black</Color>

<Color>Blue</Color>

<Color>Fuchsia</Color>

<Color>Gray</Color>

<Color>Green</Color>

<Color>Lime</Color>

<Color>Maroon</Color>

<Color>Navy</Color>

<Color>Olive</Color>

<Color>Pink</Color>

<Color>Purple</Color>

<Color>Red</Color>

<Color>Silver</Color>

<Color>Teal</Color>

<Color>White</Color>

<Color>Yellow</Color>

</x:Array>

</ListView.ItemsSource>

<ListView.ItemTemplate>

<DataTemplate>

<ViewCell>

<BoxView Color="{Binding}"

Margin="3" />

</ViewCell>

</DataTemplate>

</ListView.ItemTemplate>

</ListView>

</ContentPage>

In this example, the ViewCell creates a simple BoxView for each color entry:
7 Note

When defining arrays of common types like strings or numbers, use the XAML
language primitives tags listed in Pass arguments.

x:Null markup extension


The x:Null markup extension is supported by the NullExtension class. It has no
properties and is simply the XAML equivalent of the C# null keyword.

The following XAML example shows how to use the x:Null markup extension:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="MarkupExtensions.NullDemoPage"

Title="x:Null Demo">

<ContentPage.Resources>

<Style TargetType="Label">

<Setter Property="FontSize" Value="48" />

<Setter Property="FontFamily" Value="OpenSansRegular" />

</Style>

</ContentPage.Resources>

<StackLayout Padding="10, 0">

<Label Text="Text 1" />

<Label Text="Text 2" />

<Label Text="Text 3"

FontFamily="{x:Null}" />

<Label Text="Text 4" />

<Label Text="Text 5" />

</StackLayout>

</ContentPage>

In this example, an implicit Style is defined for Label that includes a Setter that sets the
FontFamily property to a specific font. However, the third Label avoids using the font
defined in the implicit style by setting its FontFamily to x:Null :

OnPlatform markup extension


The OnPlatform markup extension enables you to customize UI appearance on a per-
platform basis. It provides the same functionality as the OnPlatform and On classes, but
with a more concise representation.

The OnPlatform markup extension is supported by the OnPlatformExtension class, which


defines the following properties:

Default of type object , that you set to a default value to be applied to the
properties that represent platforms.
Android of type object , that you set to a value to be applied on Android.

iOS of type object , that you set to a value to be applied on iOS.


MacCatalyst of type object , that you set to a value to be applied on Mac Catalyst.

Tizen of type object , that you set to a value to be applied on the Tizen platform.
WinUI of type object , that you set to a value to be applied on WinUI.

Converter of type IValueConverter, that can be set to an IValueConverter

implementation.
ConverterParameter of type object , that can be set to a value to pass to the

IValueConverter implementation.
7 Note

The XAML parser allows the OnPlatformExtension class to be abbreviated as


OnPlatform .

The Default property is the content property of OnPlatformExtension. Therefore, for


XAML markup expressions expressed with curly braces, you can eliminate the Default=
part of the expression provided that it's the first argument. If the Default property isn't
set, it will default to the BindableProperty.DefaultValue property value, provided that
the markup extension is targeting a BindableProperty.

) Important

The XAML parser expects that values of the correct type will be provided to
properties consuming the OnPlatform markup extension. If type conversion is
necessary, the OnPlatform markup extension will attempt to perform it using the
default converters provided by .NET MAUI. However, there are some type
conversions that can't be performed by the default converters and in these cases
the Converter property should be set to an IValueConverter implementation.

The OnPlatform Demo page shows how to use the OnPlatform markup extension:

XAML

<BoxView Color="{OnPlatform Yellow, iOS=Red, Android=Green}"

WidthRequest="{OnPlatform 250, iOS=200, Android=300}"

HeightRequest="{OnPlatform 250, iOS=200, Android=300}"

HorizontalOptions="Center" />

In this example, all three OnPlatform expressions use the abbreviated version of the
OnPlatformExtension class name. The three OnPlatform markup extensions set the
xref:Microsoft.Maui.Graphics.Color , WidthRequest, and HeightRequest properties of

the BoxView to different values on iOS and Android. The markup extensions also
provide default values for these properties on the platforms that aren't specified, while
eliminating the Default= part of the expression.

OnIdiom markup extension


The OnIdiom markup extension enables you to customize UI appearance based on the
idiom of the device the application is running on. It's supported by the
OnIdiomExtension class, which defines the following properties:

Default of type object , that you set to a default value to be applied to the
properties that represent device idioms.
Phone of type object , that you set to a value to be applied on phones.
Tablet of type object , that you set to a value to be applied on tablets.

Desktop of type object , that you set to a value to be applied on desktop

platforms.
TV of type object , that you set to a value to be applied on TV platforms.

Watch of type object , that you set to a value to be applied on Watch platforms.
Converter of type IValueConverter, that can be set to an IValueConverter

implementation.
ConverterParameter of type object , that can be set to a value to pass to the
IValueConverter implementation.

7 Note

The XAML parser allows the OnIdiomExtension class to be abbreviated as OnIdiom .

The Default property is the content property of OnIdiomExtension. Therefore, for XAML
markup expressions expressed with curly braces, you can eliminate the Default= part of
the expression provided that it's the first argument.

) Important

The XAML parser expects that values of the correct type will be provided to
properties consuming the OnIdiom markup extension. If type conversion is
necessary, the OnIdiom markup extension will attempt to perform it using the
default converters provided by .NET MAUI. However, there are some type
conversions that can't be performed by the default converters and in these cases
the Converter property should be set to an IValueConverter implementation.

The following XAML example shows how to use the OnIdiom markup extension:

XAML

<BoxView Color="{OnIdiom Yellow, Phone=Red, Tablet=Green, Desktop=Blue}"

WidthRequest="{OnIdiom 100, Phone=200, Tablet=300, Desktop=400}"

HeightRequest="{OnIdiom 100, Phone=200, Tablet=300, Desktop=400}"

HorizontalOptions="Center" />

In this example, all three OnIdiom expressions use the abbreviated version of the
OnIdiomExtension class name. The three OnIdiom markup extensions set the Color ,
WidthRequest, and HeightRequest properties of the BoxView to different values on the
phone, tablet, and desktop idioms. The markup extensions also provide default values
for these properties on the idioms that aren't specified, while eliminating the Default=
part of the expression.

DataTemplate markup extension


The DataTemplate markup extension enables you to convert a type into a DataTemplate.
It's supported by the DataTemplateExtension class, which defines a TypeName property,
of type string , that is set to the name of the type to be converted into a DataTemplate.
The TypeName property is the content property of DataTemplateExtension. Therefore, for
XAML markup expressions expressed with curly braces, you can eliminate the TypeName=
part of the expression.

7 Note

The XAML parser allows the DataTemplateExtension class to be abbreviated as


DataTemplate.

A typical usage of this markup extension is in a Shell application, as shown in the


following example:

XAML

<ShellContent Title="Monkeys"

Icon="monkey.png"

ContentTemplate="{DataTemplate views:MonkeysPage}" />

In this example, MonkeysPage is converted from a ContentPage to a DataTemplate, which


is set as the value of the ShellContent.ContentTemplate property. This ensures that
MonkeysPage is only created when navigation to the page occurs, rather than at
application startup.

For more information about Shell apps, see Shell.

FontImage markup extension


The FontImage markup extension enables you to display a font icon in any view that can
display an ImageSource . It provides the same functionality as the FontImageSource class,
but with a more concise representation.

The FontImage markup extension is supported by the FontImageExtension class, which


defines the following properties:

FontFamily of type string , the font family to which the font icon belongs.

Glyph of type string , the unicode character value of the font icon.
Color of type Color, the color to be used when displaying the font icon.

Size of type double , the size, in device-independent units, of the rendered font
icon. The default value is 30. In addition, this property can be set to a named font
size.

7 Note

The XAML parser allows the FontImageExtension class to be abbreviated as


FontImage .

The Glyph property is the content property of FontImageExtension. Therefore, for XAML
markup expressions expressed with curly braces, you can eliminate the Glyph= part of
the expression provided that it's the first argument.

The following XAML example shows how to use the FontImage markup extension:

XAML

<Image BackgroundColor="#D1D1D1"

Source="{FontImage &#xf30c;, FontFamily=Ionicons, Size=44}" />

In this example, the abbreviated version of the FontImageExtension class name is used
to display an XBox icon, from the Ionicons font family, in an Image:

While the unicode character for the icon is \uf30c , it has to be escaped in XAML and so
becomes &#xf30c; .
For information about displaying font icons by specifying the font icon data in a
FontImageSource object, see Display font icons.

AppThemeBinding markup extension


The AppThemeBinding markup extension enables you to specify a resource to be
consumed, such as an image or color, based on the current system theme.

The AppThemeBinding markup extension is supported by the AppThemeBindingExtension


class, which defines the following properties:

Default , of type object , that you set to the resource to be used by default.

Light , of type object , that you set to the resource to be used when the device is
using its light theme.
Dark , of type object , that you set to the resource to be used when the device is
using its dark theme.
Value , of type object , that returns the resource that's currently being used by the

markup extension.

7 Note

The XAML parser allows the AppThemeBindingExtension class to be abbreviated


as AppBindingTheme .

The Default property is the content property of AppThemeBindingExtension. Therefore,


for XAML markup expressions expressed with curly braces, you can eliminate the
Default= part of the expression provided that it's the first argument.

The following XAML example shows how to use the AppThemeBinding markup extension:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="MarkupExtensions.AppThemeBindingDemoPage"

Title="AppThemeBinding Demo">

<ContentPage.Resources>

<Style x:Key="labelStyle"

TargetType="Label">

<Setter Property="TextColor"

Value="{AppThemeBinding Black, Light=Blue, Dark=Teal}"


/>

</Style>

</ContentPage.Resources>

<StackLayout Margin="20">

<Label Text="This text is green in light mode, and red in dark


mode."

TextColor="{AppThemeBinding Light=Green, Dark=Red}" />

<Label Text="This text is black by default, blue in light mode, and


teal in dark mode."

Style="{StaticResource labelStyle}" />

</StackLayout>

</ContentPage>

In this example, the text color of the first Label is set to green when the device is using
its light theme, and is set to red when the device is using its dark theme. The second
Label has its TextColor property set through a Style. This Style sets the text color of the
Label to black by default, to blue when the device is using its light theme, and to teal
when the device is using its dark theme:
Create XAML markup extensions
Article • 04/03/2023 • 3 minutes to read

Browse the sample

At the developer level, a .NET Multi-platform App UI (.NET MAUI) XAML markup
extension is a class that implements the IMarkupExtension or IMarkupExtension<T>
interface. It's also possible to define your own custom XAML markup extensions by
deriving from IMarkupExtension or IMarkupExtension<T> . Use the generic form if the
markup extension obtains a value of a particular type. This is the case with several of the
.NET MAUI markup extensions:

TypeExtension derives from IMarkupExtension<Type>


ArrayExtension derives from IMarkupExtension<Array>

DynamicResourceExtension derives from IMarkupExtension<DynamicResource>

BindingExtension derives from IMarkupExtension<BindingBase>

The two IMarkupExtension interfaces define only one method each, named
ProvideValue :

C#

public interface IMarkupExtension

object ProvideValue(IServiceProvider serviceProvider);

public interface IMarkupExtension<out T> : IMarkupExtension

new T ProvideValue(IServiceProvider serviceProvider);

Since IMarkupExtension<T> derives from IMarkupExtension and includes the new


keyword on ProvideValue , it contains both ProvideValue methods.

Often, XAML markup extensions define properties that contribute to the return value,
and the ProvideValue method has a single argument of type IServiceProvider . For
more information about service providers, see Service providers.

Create a markup extension


The following XAML markup extension demonstrates how to create your own markup
extension. It allows you to construct a Color value using hue, saturation, and luminosity
components. It defines four properties for the four components of the color, including
an alpha component that is initialized to 1. The class derives from
IMarkupExtension<Color> to indicate a Color return value:

C#

public class HslColorExtension : IMarkupExtension<Color>

public float H { get; set; }

public float S { get; set; }

public float L { get; set; }

public float A { get; set; } = 1.0f;

public Color ProvideValue(IServiceProvider serviceProvider)

return Color.FromHsla(H, S, L, A);

object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)

return (this as
IMarkupExtension<Color>).ProvideValue(serviceProvider);

Because IMarkupExtension<T> derives from IMarkupExtension , the class must contain


two ProvideValue methods, one that returns a Color and another that returns an
object , but the second method can call the first method.

Consume a markup extension


The following XAML demonstrates a variety of approaches that can be used to invoke
the HslColorExtension to specify the color for a BoxView:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:MarkupExtensions"

x:Class="MarkupExtensions.HslColorDemoPage"

Title="HSL Color Demo">

<ContentPage.Resources>

<Style TargetType="BoxView">

<Setter Property="WidthRequest" Value="80" />

<Setter Property="HeightRequest" Value="80" />

<Setter Property="HorizontalOptions" Value="Center" />

<Setter Property="VerticalOptions" Value="Center" />

</Style>

</ContentPage.Resources>

<StackLayout>

<BoxView>

<BoxView.Color>

<local:HslColorExtension H="0" S="1" L="0.5" A="1" />

</BoxView.Color>

</BoxView>

<BoxView>

<BoxView.Color>

<local:HslColor H="0.33" S="1" L="0.5" />

</BoxView.Color>

</BoxView>

<BoxView Color="{local:HslColorExtension H=0.67, S=1, L=0.5}" />

<BoxView Color="{local:HslColor H=0, S=0, L=0.5}" />

<BoxView Color="{local:HslColor A=0.5}" />

</StackLayout>

</ContentPage>

In this example, when HslColorExtension is an XML tag the four properties are set as
attributes, but when it appears between curly braces, the four properties are separated
by commas without quotation marks. The default values for H , S , and L are 0, and the
default value of A is 1, so those properties can be omitted if you want them set to
default values. The last example shows an example where the luminosity is 0, which
normally results in black, but the alpha channel is 0.5, so it is half transparent and
appears gray against the white background of the page:

Service providers
By using the IServiceProvider argument to ProvideValue , XAML markup extensions can
get access to data about the XAML file in which they're being used. For example, the
IProvideValueTarget service enables you to retrieve data about the object the markup

extension is applied to:

C#

IProvideValueTarget provideValueTarget =
serviceProvider.GetService(typeof(IProvideValueTarget)) as
IProvideValueTarget;

The IProvideValueTarget interface defines two properties, TargetObject and


TargetProperty . When this information is obtained in the HslColorExtension class,

TargetObject is the BoxView and TargetProperty is the Color property of BoxView. This
is the property on which the XAML markup extension has been set.
XAML namespaces
Article • 04/03/2023 • 3 minutes to read

XAML uses the xmlns XML attribute for namespace declarations. There are two XAML
namespace declarations that are always within the root element of a XAML file. The first
defines the default namespace:

XAML

xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

The default namespace specifies that elements defined within the XAML file with no
prefix refer to .NET Multi-platform App UI (.NET MAUI) classes, such as ContentPage,
Label, and Button.

The second namespace declaration uses the x prefix:

XAML

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

XAML uses prefixes to declare non-default namespaces, with the prefix being used when
referencing types within the namespace. The x namespace declaration specifies that
elements defined within XAML with a prefix of x are used for elements and attributes
that are intrinsic to XAML (specifically the 2009 XAML specification).

The following table outlines the x constructs supported by .NET MAUI:

Construct Description

x:Arguments Specifies constructor arguments for a non-default constructor, or for a factory


method object declaration.

x:Class Specifies the namespace and class name for a class defined in XAML. The class
name must match the class name of the code-behind file. Note that this
construct can only appear in the root element of a XAML file.

x:DataType Specifies the type of the object that the XAML element, and it's children, will
bind to.

x:FactoryMethod Specifies a factory method that can be used to initialize an object.

x:FieldModifier Specifies the access level for generated fields for named XAML elements.
Construct Description

x:Key Specifies a unique user-defined key for each resource in a ResourceDictionary.


The key's value is used to retrieve the XAML resource, and is typically used as
the argument for the StaticResource markup extension.

x:Name Specifies a runtime object name for the XAML element. Setting x:Name is
similar to declaring a variable in code.

x:TypeArguments Specifies the generic type arguments to the constructor of a generic type.

For more information about the x:DataType attribute, see Compiled bindings. For more
information about the x:FieldModifier attribute, see Field modifiers. For more
information about the x:Arguments and x:FactoryMethod attributes, see Pass arguments.
For more information about the x:TypeArguments attribute, see Generics.

7 Note

In addition to the constructs listed above, .NET MAUI also includes markup
extensions that can be consumed through the x namespace prefix. For more
information, see Consume XAML Markup Extensions.

In XAML, namespace declarations inherit from parent element to child element.


Therefore, when defining a namespace in the root element of a XAML file, all elements
within that file inherit the namespace declaration.

Declare namespaces for types


Types can be referenced in XAML by declaring a XAML namespace with a prefix, with the
namespace declaration specifying the Common Language Runtime (CLR) namespace
name, and optionally an assembly name. This is achieved by defining values for the
following keywords within the namespace declaration:

clr-namespace: or using: – the CLR namespace declared within the assembly that

contains the types to expose as XAML elements. This keyword is required.


assembly= – the assembly that contains the referenced CLR namespace. This value

is the name of the assembly, without the file extension. The path to the assembly
should be established as a reference in the project that contains the XAML file that
will reference the assembly. This keyword can be omitted if the clr-namespace
value is within the same assembly as the app code that's referencing the types.
7 Note

The character separating the clr-namespace or using token from its value is a
colon, whereas the character separating the assembly token from its value is an
equal sign. The character to use between the two tokens is a semicolon.

The following code example shows a XAML namespace declaration:

XAML

<ContentPage ... xmlns:local="clr-namespace:MyMauiApp">

...

</ContentPage>

Alternatively, this can be written as:

XAML

<ContentPage ... xmlns:local="using:MyMauiApp">

...

</ContentPage>

The local prefix is a convention used to indicate that the types within the namespace
are local to the app. Alternatively, if the types are in a different assembly, the assembly
name should also be defined in the namespace declaration:

XAML

<ContentPage ... xmlns:controls="clr-


namespace:Controls;assembly=MyControlLibrary" ...>

...

</ContentPage>

The namespace prefix is then specified when declaring an instance of a type from an
imported namespace:

XAML

<controls:Expander IsExpanded="True">

...

</controls:Expander>

For information about defining a custom namespace schema, see Custom namespace
schemas.
Custom namespace schemas
Article • 02/15/2023 • 3 minutes to read

Types in a .NET Multi-platform App UI (.NET MAUI) library can be referenced in XAML by
declaring an XML namespace for the library, with the namespace declaration specifying
the Common Language Runtime (CLR) namespace name and an assembly name:

XAML

<ContentPage ...

xmlns:controls="clr-
namespace:MyCompany.Controls;assembly=MyCompany.Controls">

...

</ContentPage>

However, specifying a CLR namespace and assembly name in a xmlns definition can be
awkward and error prone. In addition, multiple XML namespace declarations may be
required if the library contains types in multiple namespaces.

An alternative approach is to define a custom namespace schema, such as


http://mycompany.com/schemas/controls , that maps to one or more CLR namespaces.
This enables a single XML namespace declaration to reference all the types in an
assembly, even if they are in different namespaces. It also enables a single XML
namespace declaration to reference types in multiple assemblies.

For more information about XAML namespaces, see XAML namespaces.

Define a custom namespace schema


The sample application contains a library that exposes some simple controls, such as
CircleButton :

C#

namespace MyCompany.Controls

public class CircleButton : Button

...

All the controls in the library reside in the MyCompany.Controls namespace. These
controls can be exposed to a calling assembly through a custom namespace schema.

A custom namespace schema is defined with the XmlnsDefinitionAttribute class, which


specifies the mapping between a XAML namespace and one or more CLR namespaces.
The XmlnsDefinitionAttribute takes two arguments: the XAML namespace name, and
the CLR namespace name. The XAML namespace name is stored in the
XmlnsDefinitionAttribute.XmlNamespace property, and the CLR namespace name is
stored in the XmlnsDefinitionAttribute.ClrNamespace property.

7 Note

The XmlnsDefinitionAttribute class also has a property named AssemblyName ,


which can be optionally set to the name of the assembly. This is only required when
a CLR namespace referenced from a XmlnsDefinitionAttribute is in a external
assembly.

The XmlnsDefinitionAttribute should be defined at the assembly level in the project


that contains the CLR namespaces that will be mapped in the custom namespace
schema. The following example shows the AssemblyInfo.cs file from a class library:

C#

using MyCompany.Controls;

[assembly: Preserve]

[assembly: XmlnsDefinition("http://mycompany.com/schemas/controls",
"MyCompany.Controls")]

This code creates a custom namespace schema that maps the


http://mycompany.com/schemas/controls URL to the MyCompany.Controls CLR

namespace. In addition, the Preserve attribute is specified on the assembly, to ensure


that the linker preserves all the types in the assembly.

) Important

The Preserve attribute should be applied to classes in the assembly that are
mapped through the custom namespace schema, or applied to the entire assembly.

The custom namespace schema can then be used for type resolution in XAML files.
Consume a custom namespace schema
To consume types from the custom namespace schema, the XAML compiler requires
that there's a code reference from the assembly that consumes the types, to the
assembly that defines the types. This can be accomplished by adding a class containing
an Init method to the assembly that defines the types that will be consumed through
XAML:

C#

namespace MyCompany.Controls

public static class Controls

public static void Init()

The Init method can then be called from the assembly that consumes types from the
custom namespace schema:

C#

using MyCompany.Controls;

namespace CustomNamespaceSchemaDemo

public partial class MainPage : ContentPage

public MainPage()

Controls.Init();

InitializeComponent();

2 Warning

Failure to include such a code reference will result in the XAML compiler being
unable to locate the assembly containing the custom namespace schema types.

To consume the CircleButton control, a XAML namespace is declared, with the


namespace declaration specifying the custom namespace schema URL:
XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:controls="http://mycompany.com/schemas/controls"

x:Class="CustomNamespaceSchemaDemo.MainPage">

<StackLayout>

...

<controls:CircleButton Text="+"

BackgroundColor="Fuchsia"

BorderColor="Black"

CircleDiameter="100" />

<controls:CircleButton Text="-"

BackgroundColor="Teal"

BorderColor="Silver"

CircleDiameter="70" />

...

</StackLayout>

</ContentPage>

CircleButton instances can then be added to the ContentPage by declaring them with
the controls namespace prefix.

To find the custom namespace schema types, .NET MAUI will search referenced
assemblies for XmlnsDefinitionAttribute instances. If the xmlns attribute for an element
in a XAML file matches the XmlNamespace property value in a XmlnsDefinitionAttribute ,
.NET MAUI will attempt to use the XmlnsDefinitionAttribute.ClrNamespace property
value for resolution of the type. If type resolution fails, .NET MAUI will continue to
attempt type resolution based on any additional matching XmlnsDefinitionAttribute
instances.

The result is that two CircleButton instances are displayed:


Custom namespace prefixes
Article • 08/22/2022 • 2 minutes to read

The NET Multi-platform App UI (.NET MAUI) XmlnsPrefixAttribute class can be used by
control authors to specify a recommended prefix to associate with a XAML namespace,
for XAML usage. The prefix is useful when supporting object tree serialization to XAML,
or when interacting with a design environment that has XAML editing features. For
example:

XAML text editors could use the XmlnsPrefixAttribute as a hint for an initial XAML
namespace xmlns mapping.
XAML design environments could use the XmlnsPrefixAttribute to add mappings
to the XAML when dragging objects out of a toolbox and onto a visual design
surface.

Recommended namespace prefixes should be defined at the assembly level with the
XmlnsPrefixAttribute constructor, which takes two arguments: a string that specifies

the identifier of a XAML namespace, and a string that specifies a recommended prefix:

C#

[assembly: XmlnsPrefix("http://schemas.microsoft.com/dotnet/2021/maui",
"maui")]

Prefixes should use short strings, because the prefix is typically applied to all serialized
elements that come from the XAML namespace. Therefore, the prefix string length can
have a noticeable effect on the size of the serialized XAML output.

7 Note

More than one XmlnsPrefixAttribute can be applied to an assembly. For example,


if you have an assembly that defines types for more than one XAML namespace,
you could define different prefix values for each XAML namespace.
Pass arguments
Article • 04/03/2023 • 3 minutes to read

It's often necessary to instantiate objects with constructors that require arguments, or by
calling a static creation method. This can be achieved in .NET Multi-platform App UI
(.NET MAUI) XAML by using the x:Arguments and x:FactoryMethod attributes:

The x:Arguments attribute is used to specify constructor arguments for a non-


default constructor, or for a factory method object declaration. For more
information, see Pass constructor arguments.
The x:FactoryMethod attribute is used to specify a factory method that can be used
to initialize an object. For more information, see Call factory methods.

In addition, the x:TypeArguments attribute can be used to specify the generic type
arguments to the constructor of a generic type. For more information, see Specify a
generic type argument.

Arguments can be passed to constructors and factory methods using the following .NET
MAUI XAML language primitives:

x:Array , which corresponds to Array.

x:Boolean , which corresponds to Boolean.

x:Byte , which corresponds to Byte.


x:Char , which corresponds to Char.

x:DateTime , which corresponds to DateTime.


x:Decimal , which corresponds to Decimal.

x:Double , which corresponds to Double.

x:Int16 , which corresponds to Int16.


x:Int32 , which corresponds to Int32.

x:Int64 , which corresponds to Int64.


x:Object , which corresponds to the Object.

x:Single , which corresponds to Single.

x:String , which corresponds to String.


x:TimeSpan , which corresponds to TimeSpan.

With the exception of x:DateTime , the other language primitives are in the XAML 2009
specification.

7 Note
The x:Single language primitive can be used to pass float arguments.

Pass constructor arguments


Arguments can be passed to a non-default constructor using the x:Arguments attribute.
Each constructor argument must be delimited within an XML element that represents
the type of the argument.

The following example demonstrates using the x:Arguments attribute with three
different Color constructors:

XAML

<BoxView HeightRequest="150"

WidthRequest="150"

HorizontalOptions="Center">

<BoxView.Color>

<Color>

<x:Arguments>

<x:Single>0.9</x:Single>

</x:Arguments>

</Color>

</BoxView.Color>

</BoxView>

<BoxView HeightRequest="150"

WidthRequest="150"

HorizontalOptions="Center">

<BoxView.Color>

<Color>

<x:Arguments>

<x:Single>0.25</x:Single>

<x:Single>0.5</x:Single>

<x:Single>0.75</x:Single>

</x:Arguments>

</Color>

</BoxView.Color>

</BoxView>

<BoxView HeightRequest="150"

WidthRequest="150"

HorizontalOptions="Center">

<BoxView.Color>

<Color>

<x:Arguments>

<x:Single>0.8</x:Single>

<x:Single>0.5</x:Single>

<x:Single>0.2</x:Single>

<x:Single>0.5</x:Single>

</x:Arguments>

</Color>

</BoxView.Color>

</BoxView>

The number of elements within the x:Arguments tag, and the types of these elements,
must match one of the Color constructors. The Color constructor with a single
parameter requires a grayscale float value from 0 (black) to 1 (white). The Color
constructor with three parameters requires float red, green, and blue values ranging
from 0 to 1. The Color constructor with four parameters adds a float alpha channel as
the fourth parameter.

Call factory methods


Factory methods can be called in .NET MAUI XAML by specifying the method's name
using the x:FactoryMethod attribute, and its arguments using the x:Arguments attribute.
A factory method is a public static method that returns objects or values of the same
type as the class or structure that defines the methods.

The Color class defines a number of factory methods, and the following example
demonstrates calling three of them:

XAML

<BoxView HeightRequest="150"

WidthRequest="150"

HorizontalOptions="Center">

<BoxView.Color>

<Color x:FactoryMethod="FromRgba">

<x:Arguments>

<x:Byte>192</x:Byte>

<x:Byte>75</x:Byte>

<x:Byte>150</x:Byte>

<x:Byte>128</x:Byte>

</x:Arguments>

</Color>

</BoxView.Color>

</BoxView>

<BoxView HeightRequest="150"

WidthRequest="150"

HorizontalOptions="Center">

<BoxView.Color>

<Color x:FactoryMethod="FromHsla">

<x:Arguments>

<x:Double>0.23</x:Double>

<x:Double>0.42</x:Double>

<x:Double>0.69</x:Double>

<x:Double>0.7</x:Double>

</x:Arguments>

</Color>

</BoxView.Color>

</BoxView>

<BoxView HeightRequest="150"

WidthRequest="150"

HorizontalOptions="Center">

<BoxView.Color>

<Color x:FactoryMethod="FromHex">

<x:Arguments>

<x:String>#FF048B9A</x:String>

</x:Arguments>

</Color>

</BoxView.Color>

</BoxView>

The number of elements within the x:Arguments tag, and the types of these elements,
must match the arguments of the factory method being called. The FromRgba factory
method requires four byte arguments, which represent the red, green, blue, and alpha
values, ranging from 0 to 255 respectively. The FromHsla factory method requires four
float arguments, which represent the hue, saturation, luminosity, and alpha values,

ranging from 0 to 1 respectively. The FromHex factory method requires a string


argument that represents the hexadecimal (A)RGB color.

Specify a generic type argument


Generic type arguments for the constructor of a generic type can be specified using the
x:TypeArguments attribute, as demonstrated in the following example:

XAML

<StackLayout>

<StackLayout.Margin>

<OnPlatform x:TypeArguments="Thickness">

<On Platform="iOS" Value="0,20,0,0" />

<On Platform="Android" Value="5, 10" />

</OnPlatform>

</StackLayout.Margin>

</StackLayout>

The OnPlatform class is a generic class and must be instantiated with an


x:TypeArguments attribute that matches the target type. In the On class, the Platform

attribute can accept a single string value, or multiple comma-delimited string values.
In this example, the StackLayout.Margin property is set to a platform-specific Thickness .

For more information about generic type arguments, see Generics in XAML.
Load XAML at runtime
Article • 12/23/2022 • 2 minutes to read

When a .NET Multi-platform App UI (.NET MAUI) XAML class is constructed, a


LoadFromXaml method is indirectly called. This occurs because the code-behind file for a

XAML class calls the InitializeComponent method from its constructor:

C#

public partial class MainPage : ContentPage

public MainPage()

InitializeComponent();

When Visual Studio builds a project containing a XAML file, a source generator
generates new C# source that contains the definition of the InitializeComponent
method and adds it to the compilation object. The following example shows the
generated InitializeComponent method for the MainPage class:

C#

private void InitializeComponent()

global::Microsoft.Maui.Controls.Xaml.Extensions.LoadFromXaml(this,
typeof(MainPage));

...

The InitializeComponent method calls the LoadFromXaml method to extract the XAML
compiled binary (or its file) from the app package. After extraction, it initializes all of the
objects defined in the XAML, connects them all together in parent-child relationships,
attaches event handlers defined in code to events set in the XAML file, and sets the
resultant tree of objects as the content of the page.

Load XAML at runtime


The Extensions class, in the Microsoft.Maui.Controls.Xaml namespace, includes
LoadFromXaml extension methods that can be used to load and parse XAML at runtime.
The LoadFromXaml methods are public , and therefore can be called from .NET MAUI
applications to load, and parse XAML at runtime. This enables scenarios such as an app
downloading XAML from a web service, creating the required view from the XAML, and
displaying it in the app.

2 Warning

Loading XAML at runtime has a significant performance cost, and generally should
be avoided.

The following code example shows a simple usage:

C#

string navigationButtonXAML = "<Button Text=\"Navigate\" />";

Button navigationButton = new Button().LoadFromXaml(navigationButtonXAML);

...

stackLayout.Add(navigationButton);

In this example, a Button instance is created, with its Text property value being set from
the XAML defined in the string . The Button is then added to a StackLayout that has
been defined in the XAML for the page.

7 Note

The LoadFromXaml extension methods allow a generic type argument to be


specified. However, it's rarely necessary to specify the type argument, as it will be
inferred from the type of the instance it's operating on.

The LoadFromXaml method can be used to inflate any XAML, with the following example
inflating a ContentPage and then navigating to it:

C#

// See the sample for the full XAML string

string pageXAML = "<?xml version=\"1.0\" encoding=\"utf-8\"?


>\r\n<ContentPage
xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\nxmlns:x=\"http://sc
hemas.microsoft.com/winfx/2009/xaml\"\nx:Class=\"LoadRuntimeXAML.CatalogItem
sPage\"\nTitle=\"Catalog Items\">\n</ContentPage>";

ContentPage page = new ContentPage().LoadFromXaml(pageXAML);

await Navigation.PushAsync(page);
Access elements
Loading XAML at runtime with the LoadFromXaml method does not allow strongly-typed
access to the XAML elements that have specified runtime object names (using x:Name ).
However, these XAML elements can be retrieved using the FindByName method, and
then accessed as required:

C#

// See the sample for the full XAML string

string pageXAML = "<?xml version=\"1.0\" encoding=\"utf-8\"?


>\r\n<ContentPage
xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\nxmlns:x=\"http://sc
hemas.microsoft.com/winfx/2009/xaml\"\nx:Class=\"LoadRuntimeXAML.CatalogItem
sPage\"\nTitle=\"Catalog Items\">\n<StackLayout>\n<Label
x:Name=\"monkeyName\"\n />\n</StackLayout>\n</ContentPage>";

ContentPage page = new ContentPage().LoadFromXaml(pageXAML);

Label monkeyLabel = page.FindByName<Label>("monkeyName");

monkeyLabel.Text = "Seated Monkey";

In this example, the XAML for a ContentPage is inflated. This XAML includes a Label
named monkeyName , which is retrieved using the FindByName method, before its Text
property is set.
XAML Hot Reload for .NET MAUI
Article • 10/25/2022 • 4 minutes to read

.NET Multi-platform App UI (.NET MAUI) XAML Hot Reload is a Visual Studio feature that
enables you to view the result of XAML changes in your running app, without having to
rebuild your project. Without XAML Hot Reload, you have to build and deploy your app
every time you want to view the result of a XAML change.

When your .NET MAUI app is running in debug configuration, with the debugger
attached, XAML Hot Reload parses your XAML edits and sends those changes to the
running app. It preserves your UI state, since it doesn't recreate the UI for the full page,
and updates changed properties on controls affected by edits. In addition, your navigate
state and data will be maintained, enabling you to quickly iterate on your UI without
losing your location in the app. Therefore, you'll spend less time rebuilding and
deploying your apps to validate UI changes.

By default, you don't need to save your XAML file to see the results of your edits.
Instead, updates are applied immediately as you type. However, on Windows you can
change this behavior to update only on file save. This can be accomplished by checking
the Apply XAML Hot Reload on document save checkbox in the Hot Reload IDE
settings available by selecting Debug > Options> XAML Hot Reload from the Visual
Studio menu bar. Only updating on file save can sometimes be useful if you make
bigger XAML updates and don't wish them to be displayed until they are complete.

7 Note

If you're writing a native UWP or WPF app, without using .NET MAUI, see What is
XAML Hot Reload for WPF and UWP apps?.

XAML Hot Reload is available in both Visual Studio 2022 and Visual Studio 2022 for Mac.
On Windows, XAML Hot Reload is available on Android, iOS, and WinUI on emulators,
simulators, and physical devices. On Mac, XAML Hot Reload is available on Android, iOS,
and Mac Catalyst on emulators, simulators, and physical devices.

) Important

XAML Hot Reload doesn't reload C# code, including event handlers.

Enable XAML Hot Reload


Visual Studio

XAML Hot Reload is enabled by default in Visual Studio 2022. If it's been previously
disabled, it can be enabled by selecting Debug > Options > XAML Hot Reload
from the Visual Studio menu bar. Next, in the Options dialog box, ensure that the
Enable XAML Hot Reload, WinUI (including .NET MAUI), and Android and iOS
(.NET MAUI) options are checked:

Then, on iOS in your build settings, check that the Linker is set to "Don't Link".

Reload on multiple platforms


XAML Hot Reload supports simultaneous debugging of multiple platforms in Visual
Studio and Visual Studio for Mac, provided that you have separate head projects per
platform rather than a single project app. For example, you can deploy an Android and
an iOS target at the same time to see your changes reflected on both platforms at once.
To debug on multiple platforms on Windows, see How To: Set multiple startup projects.
To debug on multiple platforms on a Mac, see Set multiple startup projects.

Troubleshooting
The XAML Hot Reload output displays status messages that can help with
troubleshooting. In Visual Studio, these can be displayed by selecting View > Output
from the menu bar, and then selecting Xamarin Hot Reload in the Show output from:
drop-down. In Visual Studio for Mac, these can be displayed by hovering your mouse
cursor over XAML Hot Reload in the status bar.

If XAML Hot Reload fails to initialize you should ensure that you're using the latest
version of .NET MAUI, the latest version of the IDE, and that your iOS linker settings are
set to Don't Link in the project's build settings.

If nothing happens when saving your XAML file, ensure that XAML Hot Reload is
enabled in the IDE. For more information, see Enable XAML Hot Reload.

If you make a change that the XAML Hot Reload parser sees as invalid, it will show the
error underlined in the editor and include it in the Error List window. Hot Reload errors
have an error code starting with "XHR" (for XAML Hot Reload). If there are any such
errors on the page, XAML Hot Reload won't apply changes to your running app until the
errors have been fixed.

You can't add, remove, or rename files or NuGet packages during a XAML Hot Reload
session. If you add or remove a file or NuGet package, rebuild and redeploy your app to
continue using XAML Hot Reload.

Disabling XAML compilation with [XamlCompilation(XamlCompilationOptions.Skip)] isn't


supported and can cause issues with the Live Visual Tree. For more information about
Live Visual Tree, see Inspect the visual tree of a .NET MAUI app.
Build accessible apps with semantic
properties
Article • 04/03/2023 • 15 minutes to read

Semantics for accessibility is concerned with building experiences that make your apps
inclusive for people who use technology in a wide range of environments and approach
your UI with a range of needs and experiences. In many situations, legal requirements
for accessibility may provide an impetus for developers to address accessibility issues.
Regardless, it's advisable to build inclusive and accessible apps so that your apps reach
the largest possible audience.

The Web Content Accessibility Guidelines (WCAG) are the global accessibility standard
and legal benchmark for web and mobile. These guidelines describe the various ways in
which apps can be made more perceivable, operable, understandable, and robust, for
all.

Many user accessibility needs are met by assistive technology products installed by the
user or by tools and settings provided by the operating system. This includes
functionality such as screen readers, screen magnification, and high-contrast settings.

Screen readers typically provide auditory descriptions of controls that are displayed on
the screen. These descriptions help users navigate through the app and provide
references to controls, such as images, that have no input or text. Screen readers are
often controlled through gestures on the touchscreen, trackpad, or keyboard. For
information about enabling screen readers, see Enable screen readers.

Operating systems have their own screen readers with their own unique behavior and
configuration. For example, most screen readers read the text associated with a control
when it receives focus, enabling users to orient themselves as they navigate through the
app. However, some screen readers can also read the entire app user interface when a
page appears, which enables the user to receive all of the page's available informational
content before attempting to navigate it.

Most screen readers will automatically read any text associated with a control that
receives accessibility focus. This means that controls, such as Label or Button, that have
a Text property set will be accessible for the user. However, Image, ImageButton,
ActivityIndicator, and others might not be in the accessibility tree because no text is
associated with them.

.NET Multi-platform App UI (.NET MAUI) supports two approaches to providing access
to the accessibility experience of the underlying platform. Semantic properties are the
.NET MAUI approach to providing accessibility values in apps, and are the
recommended approach. Automation properties are the Xamarin.Forms approach to
providing accessibility values in apps, and have been superseded by semantic
properties. In both cases, the default accessibility order of controls is the same order in
which they're listed in XAML or added to the layout. However, different layouts might
have additional factors that influence accessibility order. For example, the accessibility
order of StackLayout is also based on its orientation, and the accessibility order of Grid
is based on its row and column arrangement. For more information about content
ordering, see Meaningful Content Ordering on the Xamarin blog.

7 Note

When a WebView displays a website that's accessible, it will also be accessible in a


.NET MAUI app. Conversely, when a WebView displays a website that's not
accessible, it won't be accessible in a .NET MAUI app.

Semantic properties
Semantic properties are used to define information about which controls should receive
accessibility focus and which text should be read aloud to the user. Semantic properties
are attached properties that can be added to any element to set the underlying platform
accessibility APIs.

) Important

Semantic properties don't try to force equivalent behavior on each platform.


Instead, they rely on the accessibility experience provided by each platform.

The SemanticProperties class defines the following attached properties:

Description, of type string , which represents a description that will be read aloud
by the screen reader. For more information, see Description.
Hint, of type string , which is similar to Description , but provides additional
context such as the purpose of a control. For more information, see Hint.
HeadingLevel, of type SemanticHeadingLevel, which enables an element to be
marked as a heading to organize the UI and make it easier to navigate. For more
information, see Heading levels.

These attached properties set platform accessibility values so that a screen reader can
speak about the element. For more information about attached properties, see Attached
properties.

Description
The Description attached property represents a short, descriptive string that a screen
reader uses to announce an element. This property should be set for elements that have
a meaning that's important for understanding the content or interacting with the user
interface. Setting this property can be accomplished in XAML:

XAML

<Image Source="dotnet_bot.png"

SemanticProperties.Description="Cute dot net bot waving hi to you!"


/>

Alternatively, it can be set in C#:

C#

Image image = new Image { Source = "dotnet_bot.png" };

SemanticProperties.SetDescription(image, "Cute dot net bot waving hi to


you!");

In addition, the SetValue method can also be used to set the Description attached
property:

C#

image.SetValue(SemanticProperties.DescriptionProperty, "Cute dot net bot


waving hi to you!");

2 Warning

Avoid setting the Description attached property on a Label. This will stop the Text
property being spoken by the screen reader. This is because the visual text should
ideally match the text read aloud by the screen reader.

The accessibility information for an element can also be defined on another element. For
example, a Label next to an Entry can be used to describe what the Entry represents.
This can be accomplished in XAML as follows:

XAML
<Label x:Name="label"

Text="Enter your name: " />

<Entry SemanticProperties.Description="{Binding Source={x:Reference label}


Path=Text}" />

Alternatively, it can be set in C# as follows:

C#

Label label = new Label


{

Text = "Enter your name: "

};

Entry entry = new Entry();

SemanticProperties.SetDescription(entry, label.Text);

2 Warning

On iOS, if you set the Description property on any control that has children the
screen reader will be unable to reach the children. This is because iOS doesn't
provide accessibility features that allow the navigation from a parent element into a
child element.

Hint
The Hint attached property represents a string that provides additional context to the
Description attached property, such as the purpose of a control. Setting this property
can be accomplished in XAML:

XAML

<Image Source="like.png"

SemanticProperties.Description="Like"

SemanticProperties.Hint="Like this post." />

Alternatively, it can be set in C#:

C#

Image image = new Image { Source = "like.png" };

SemanticProperties.SetDescription(image, "Like");

SemanticProperties.SetHint(image, "Like this post.");

In addition, the SetValue method can also be used to set the Hint attached property:

C#

image.SetValue(SemanticProperties.HintProperty, "Like this post.");

On Android, this property behaves slightly differently depending on the control it's
attached to. For example, for controls without text values, such as Switch and CheckBox,
the controls will display the hint with the control. However, for controls with text values,
the hint is not displayed and is read after the text value.

2 Warning

The Hint property conflicts with the Entry.Placeholder property on Android, which
both map to the same platform property. Therefore, setting a different Hint value
to the Entry.Placeholder value isn't recommended.

Heading levels
The HeadingLevel attached property enables an element to be marked as a heading to
organize the UI and make it easier to navigate. Some screen readers enable users to
quickly jump between headings.

Headings have a level from 1 to 9, and are represented by the SemanticHeadingLevel


enumeration, which defines None , and Level1 through Level9 members.

) Important

While Windows offers 9 levels of headings, Android and iOS only offer a single
heading. Therefore, when HeadingLevel is set on Windows it maps to the correct
heading level. However, when set on Android and iOS it maps to a single heading
level.

The following example demonstrates setting this attached property:

XAML

<Label Text="Get started with .NET MAUI"

SemanticProperties.HeadingLevel="Level1" />

<Label Text="Paragraphs of text go here." />

<Label Text="Installation"

SemanticProperties.HeadingLevel="Level2" />

<Label Text="Paragraphs of text go here." />

<Label Text="Build your first app"

SemanticProperties.HeadingLevel="Level3" />

<Label Text="Paragraphs of text go here." />

<Label Text="Publish your app"

SemanticProperties.HeadingLevel="Level4" />

<Label Text="Paragraphs of text go here." />

Alternatively, it can be set in C#:

C#

Label label1 = new Label { Text = "Get started with .NET MAUI" };

Label label2 = new Label { Text = "Paragraphs of text go here." };

Label label3 = new Label { Text = "Installation" };

Label label4 = new Label { Text = "Paragraphs of text go here." };

Label label5 = new Label { Text = "Build your first app" };

Label label6 = new Label { Text = "Paragraphs of text go here." };

Label label7 = new Label { Text = "Publish your app" };

Label label8 = new Label { Text = "Paragraphs of text go here." };

SemanticProperties.SetHeadingLevel(label1, SemanticHeadingLevel.Level1);

SemanticProperties.SetHeadingLevel(label3, SemanticHeadingLevel.Level1);

SemanticProperties.SetHeadingLevel(label5, SemanticHeadingLevel.Level1);

SemanticProperties.SetHeadingLevel(label7, SemanticHeadingLevel.Level1);

In addition, the SetValue method can also be used to set the HeadingLevel attached
property:

C#

label1.SetValue(SemanticProperties.HeadingLevelProperty,
SemanticHeadingLevel.Level1);

Semantic focus
Controls have a SetSemanticFocus extension method which forces screen reader focus
to a specified element. For example, given a Label named label , screen reader focus
can be forced to the element with the following code:

C#

label.SetSemanticFocus();

Semantic screen reader


.NET MAUI provides the ISemanticScreenReader interface, with which you can instruct a
screen reader to announce text to the user. The interface is exposed through the Default
property, and is available in the Microsoft.Maui.Accessibility namespace.

To instruct a screen reader to announce text, use the Announce method, passing a
string argument that represents the text. The following example demonstrates using

this method:

C#

SemanticScreenReader.Default.Announce("This is the announcement text.");

Limitations
The default platform screen reader must be enabled for text to be read aloud.

Automation properties
Automation properties are attached properties that can be added to any element to
indicate how the element is reported to the underlying platform's accessibility
framework.

The AutomationProperties class defines the following attached properties:

ExcludedWithChildren, of type bool? , determines if an element and its children


should be excluded from the accessibility tree. For more information, see
ExcludedWithChildren.
IsInAccessibleTree, of type bool? , indicates whether the element is available in the
accessibility tree. For more information, see IsInAccessibleTree.
Name, of type string , represents a short description of the element that serves as
a speakable identifier for that element. For more information, see Name.
HelpText, of type string , represents a longer description of the element, which can
be thought of as tooltip text that's associated with the element. For more
information, see HelpText.
LabeledBy, of type VisualElement, which enables another element to define
accessibility information for the current element. For more information, see
LabeledBy.

These attached properties set platform accessibility values so that a screen reader can
speak about the element. For more information about attached properties, see Attached
properties.
Different screen readers read different accessibility values. Therefore, when using
automation properties it's recommended that thorough accessibility testing is carried
out on each platform to ensure an optimal experience.

) Important

Automation properties are the Xamarin.Forms approach to providing accessibility


values in apps, and have been superseded by semantic properties. For more
information about semantic properties, see Semantic properties.

ExcludedWithChildren
The ExcludedWithChildren attached property, of type bool? , determines if an element
and its children should be excluded from the accessibility tree. This enables scenarios
such as displaying an AbsoluteLayout over another layout such as a StackLayout, with
the StackLayout being excluded from the accessibility tree when it's not visible. It can be
used from XAML as follows:

XAML

<StackLayout AutomationProperties.ExcludedWithChildren="true">

...

</StackLayout>

Alternatively, it can be set in C# as follows:

C#

StackLayout stackLayout = new StackLayout();

...

AutomationProperties.SetExcludedWithChildren(stackLayout, true);

When this attached property is set, .NET MAUI sets the IsInAccessibleTree attached
property to false on the specified element and its children.

IsInAccessibleTree

2 Warning

This attached property should typically remain unset. The majority of controls
should be present in the accessibility tree, and the
AutomationProperties.ExcludedWithChildren attached property can be set in

scenarios where an element and its children need removing from the accessibility
tree.

The IsInAccessibleTree attached property, of type bool? , determines if the element is


visible to screen readers. It must be set to true to use the other automation properties.
This can be accomplished in XAML as follows:

XAML

<Entry AutomationProperties.IsInAccessibleTree="true" />

Alternatively, it can be set in C# as follows:

C#

Entry entry = new Entry();

AutomationProperties.SetIsInAccessibleTree(entry, true);

2 Warning

On iOS, if the IsInAccessibleTree property is true on any control that has children
the screen reader will be unable to reach the children. This is because iOS doesn't
provide accessibility features that allow the navigation from a parent element into a
child element.

Name

) Important

The Name attached property has been superseded by the Description attached
property.

The Name attached property value should be a short, descriptive text string that a
screen reader uses to announce an element. This property should be set for elements
that have a meaning that is important for understanding the content or interacting with
the user interface. This can be accomplished in XAML as follows:

XAML
<ActivityIndicator AutomationProperties.IsInAccessibleTree="true"

AutomationProperties.Name="Progress indicator" />

Alternatively, it can be set in C# as follows:

C#

ActivityIndicator activityIndicator = new ActivityIndicator();

AutomationProperties.SetIsInAccessibleTree(activityIndicator, true);

AutomationProperties.SetName(activityIndicator, "Progress indicator");

HelpText

) Important

The HelpText attached property has been superseded by the Hint attached
property.

The HelpText attached property should be set to text that describes the user interface
element, and can be thought of as tooltip text associated with the element. This can be
accomplished in XAML as follows:

XAML

<Button Text="Toggle ActivityIndicator"

AutomationProperties.IsInAccessibleTree="true"

AutomationProperties.HelpText="Tap to toggle the activity indicator"


/>

Alternatively, it can be set in C# as follows:

C#

Button button = new Button { Text = "Toggle ActivityIndicator" };

AutomationProperties.SetIsInAccessibleTree(button, true);

AutomationProperties.SetHelpText(button, "Tap to toggle the activity


indicator");

On some platforms, for edit controls such as an Entry, the HelpText property can
sometimes be omitted and replaced with placeholder text. For example, "Enter your
name here" is a good candidate for the Entry.Placeholder property that places the text
in the control prior to the user's actual input.
LabeledBy

) Important

The LabeledBy attached property has been superseded by bindings. For more
information, see SemanticProperties: Description.

The LabeledBy attached property allows another element to define accessibility


information for the current element. For example, a Label next to an Entry can be used
to describe what the Entry represents. This can be accomplished in XAML as follows:

XAML

<Label x:Name="label" Text="Enter your name: " />

<Entry AutomationProperties.IsInAccessibleTree="true"

AutomationProperties.LabeledBy="{x:Reference label}" />

Alternatively, it can be set in C# as follows:

C#

Label label = new Label { Text = "Enter your name: " };

Entry entry = new Entry();

AutomationProperties.SetIsInAccessibleTree(entry, true);

AutomationProperties.SetLabeledBy(entry, label);

) Important

The AutomationProperties.LabeledByProperty is not supported on iOS.

Testing accessibility
.NET MAUI apps typically target multiple platforms, which means testing the accessibility
features according to the platform. Follow these links to learn how to test accessibility
on each platform:

Test your app's accessibility on Android.


Verifying app accessibility on iOS .
Testing for accessibility on OS X
Accessibility testing on Windows.
The following tools can assist with your accessibility testing:

Accessibility Insights for Android and Windows apps.


Accessibility Scanner for Android apps.
Accessibility Inspector for iOS and macOS apps.
Android Studio Layout Inspector for Android apps.
Xcode View Debugger for iOS and macOS apps.

However, none of these tools can perfectly emulate the screen reader user experience,
and the best way to test and troubleshoot your apps for accessibility will always be
manually on physical devices with screen readers.

Enabling screen readers


Each platform has a different default screen reader to narrate accessibility values:

Android has TalkBack. For information on enabling TalkBack, see Enable TalkBack.
iOS and macOS have VoiceOver. For information on enabling VoiceOver, see
Enable VoiceOver.
Windows has Narrator. For information on enabling Narrator, see Enable Narrator.

Enable TalkBack
TalkBack is the primary screen reader used on Android. How it's enabled depends on the
device manufacturer, Android version, and TalkBack version. However, TalkBack can
typically be enabled on your Android device via the device settings:

1. Open the Settings app.


2. Select Accessibility > TalkBack.
3. Turn Use TalkBack on.
4. Select OK.

7 Note

While these steps apply to most devices, you might experience some differences.

A TalkBack tutorial opens automatically the first time you enable TalkBack.

For alternative methods of enabling TalkBack, see Turn Talkback on or off .

Enable VoiceOver
VoiceOver is the primary screen reader used on iOS and macOS. On iOS, VoiceOver can
be enabled as follows:

1. Open the Settings app.


2. Select Accessibility > VoiceOver.
3. Turn VoiceOver on.

A VoiceOver tutorial can be opened by selecting VoiceOver Practice, once VoiceOver is


enabled.

For alternative methods of enabling VoiceOver, see Turn on and practice VoiceOver on
iPhone and Turn on and practice VoiceOver on iPad .

On macOS, VoiceOver can be enabled as follows:

1. Open the System Preferences.


2. Select Accessibility > VoiceOver.
3. Select Enable VoiceOver.
4. Select Use VoiceOver.

A VoiceOver tutorial can be opened by selecting Open VoiceOver Training....

For alternative methods of enabling VoiceOver, see Turn VoiceOver on or off on Mac .

Enable Narrator
Narrator is the primary screen reader used on Windows. Narrator can be enabled by
pressing the Windows logo key + Ctrl + Enter together. These keys can be pressed
again to stop Narrator.

For more information about Narrator, see Complete guide to Narrator .

Accessibility checklist
Follow these tips to ensure that your .NET MAUI apps are accessible to the widest
audience possible:

" Ensure your app is perceivable, operable, understandable, and robust for all by
following the Web Content Accessibility Guidelines (WCAG). WCAG is the global
accessibility standard and legal benchmark for web and mobile. For more
information, see Web Content Accessibility Guidelines (WCAG) Overview .
" Make sure the user interface is self-describing. Test that all the elements of your
user interface are screen reader accessible. Add descriptive text and hints when
necessary.
" Ensure that images and icons have alternate text descriptions.
" Support large fonts and high contrast. Avoid hardcoding control dimensions, and
instead prefer layouts that resize to accommodate larger font sizes. Test color
schemes in high-contrast mode to ensure they are readable.
" Design the visual tree with navigation in mind. Use appropriate layout controls so
that navigating between controls using alternate input methods follows the same
logical flow as using touch. In addition, exclude unnecessary elements from screen
readers (for example, decorative images or labels for fields that are already
accessible).
" Don't rely on audio or color cues alone. Avoid situations where the sole indication
of progress, completion, or some other state is a sound or color change. Either
design the user interface to include clear visual cues, with sound and color for
reinforcement only, or add specific accessibility indicators. When choosing colors,
try to avoid a palette that is hard to distinguish for users with color blindness.
" Provide captions for video content and a readable script for audio content. It's also
helpful to provide controls that adjust the speed of audio or video content, and
ensure that volume and transport controls are easy to find and use.
" Localize your accessibility descriptions when the app supports multiple languages.
" Test the accessibility features of your app on each platform it targets. For more
information, see Testing accessibility.
App lifecycle
Article • 12/23/2022 • 16 minutes to read

.NET Multi-platform App UI (.NET MAUI) apps generally have four execution states: not running,
running, deactivated, and stopped. .NET MAUI raises cross-platform lifecycle events on the Window
class when an app transitions from the not running state to the running state, the running state to
the deactivated state, the deactivated state to the stopped state, the stopped state to the running
state, and the stopped state to the not running state.

The following diagram shows an overview of the .NET MAUI app lifecycle:

In the diagram, the gray oval indicates that the app isn't loaded into memory. The light blue ovals
indicate that the app is in memory. Text on arcs indicates events that are raised by .NET MAUI, that
provide notifications to the running app.

The execution state of an app depends on the app's history. For example, when an app is installed
for the first time, or a device is started, the app can be considered to be not running. When the app is
started, the Created and Activated events are raised and the app is running. If a different app
window gains focus, the Deactivated event is raised and the app is deactivated. If the user switches
to a different app or returns to the device's Home screen, so that the app window is no longer
visible, the Deactivated and Stopped events are raised and the app is stopped. If the user returns to
the app, the Resuming event is raised and app is running. Alternatively, an app might be terminated
by a user while it's running. In this situation the app is deactivated then stopped, the Destroying
event is raised, and the app is not running. Similarly, a device might terminate an app while it's
stopped, due to resource restrictions, and the Destroying event is raised and the app is not running.

In addition, .NET MAUI enables apps to be notified when platform lifecycle events are raised. For
more information, see Platform lifecycle events.

Cross-platform lifecycle events


The Window class defines the following cross-platform lifecycle events:
Event Description Action to take

Created This event is raised after the native window has been
created. At this point the cross-platform window will have a
native window handler, but the window might not be
visible yet.

Activated This event is raised when the window has been activated,
and is, or will become, the focused window.

Deactivated This event is raised when the window is no longer the


focused window. However, the window might still be
visible.

Stopped This event is raised when the window is no longer visible. Disconnect from any long running
There's no guarantee that an app will resume from this processes, or cancel any pending
state, because it may be terminated by the operating requests that might consume
system. device resources.

Resumed This event is raised when an app resumes after being Subscribe to any required events,
stopped. This event won't be raised the first time your app and refresh any content that's on
launches, and can only be raised if the Stopped event has the visible page.
previously been raised.

Destroying This event is raised when the native window is being Remove any event subscriptions
destroyed and deallocated. The same cross-platform that you've attached to the native
window might be used against a new native window when window.
the app is reopened.

These cross-platform events map to different platform events, and the following table shows this
mapping:

Event Android iOS Windows

Created OnPostCreate FinishedLaunching Created

Activated OnResume OnActivated Activated ( CodeActivated and PointerActivated )

Deactivated OnPause OnResignActivation Activated ( Deactivated )

Stopped OnStop DidEnterBackground VisibilityChanged

Resumed OnRestart WillEnterForeground Resumed

Destroying OnDestroy WillTerminate Closed

In addition, the Windows class also defines a Backgrounding event that's raised on iOS and Mac
Catalyst when the Window is closed or enters a background state. A BackgroundingEventArgs object
accompanies this event, and any string state should be persisted to the State property of the
BackgroundingEventArgs object, which the OS will preserve until it's time to resume the window.
When the window is resumed the state is provided by the IActivationState argument to the
CreateWindow override.

In addition to these events, the Window class also has the following overridable lifecycle methods:
OnCreated , which is invoked when the Created event is raised.

OnActivated , which is invoked when the Activated event is raised.


OnDeactivated , which is invoked when the Deactivated event is raised.

OnStopped , which is invoked when the Stopped event is raised.


OnResumed , which is invoked when the Resumed event is raised.

OnDestroying , which is invoked when the Destroying event is raised.

OnBackgrounding , which is invoked when the Backgrounding event is raised.

To subscribe to the Window lifecycle events, override the CreateWindow method in your App class to
create a Window instance on which you can subscribe to events:

C#

namespace MyMauiApp

public partial class App : Application

public App()

InitializeComponent();

MainPage = new MainPage();

protected override Window CreateWindow(IActivationState activationState)

Window window = base.CreateWindow(activationState);

window.Created += (s, e) =>

// Custom logic

};

return window;

Alternatively, to consume the lifecycle overrides, create a class that derives from the Window class

C#

namespace MyMauiApp

public class MyWindow : Window

public MyWindow() : base()

public MyWindow(Page page) : base(page)

protected override void OnCreated()

// Register services

The Window -derived class can then be consumed by overriding the CreateWindow method in your App
class to return a MyWindow instance.

2 Warning

An InvalidOperationException will be thrown if the App.MainPage property is set and the


CreateWindow method creates a Window object using the override that accepts a Page argument.

Platform lifecycle events


.NET MAUI defines delegates that are invoked in response to platform lifecycle events being raised.
Handlers can be specified for these delegates, using named methods or anonymous functions, which
are executed when the delegate is invoked. This mechanism enables apps to be notified when
common platform lifecycle events are raised.

) Important

The ConfigureLifecycleEvents method is in the Microsoft.Maui.LifecycleEvents namespace.

Android
The following table lists the .NET MAUI delegates that are invoked in response to Android lifecycle
events being raised:

Delegate Arguments Description Comments

OnActivityResult Android.App.Activity , int , Invoked when an


Android.App.Result , activity you launched
Android.Content.Intent? exits.

OnApplicationConfigurationChanged Android.App.Application , Invoked when the


Android.Content.Res.Configuration device configuration
changes while your
component is running.

OnApplicationCreate Android.App.Application Invoked when the app


has started, before an
activity, service, or
receiver objects
(excluding content
providers) have been
created.
Delegate Arguments Description Comments

OnApplicationCreating Android.App.Application Invoked when the app is


starting, before an
activity, service, or
receiver objects
(excluding content
providers) have been
created.

OnApplicationLowMemory Android.App.Application Invoked when the


system is running low
on memory, and actively
running processes
should trim their
memory usage.

OnApplicationTrimMemory Android.App.Application , Invoked when the


Android.Content.TrimMemory operating system has
determined that it's a
good time for a process
to trim unneeded
memory from its
process.

OnBackPressed Android.App.Activity Invoked when the


activity has detected a
press of the back key.

OnConfigurationChanged Android.App.Activity , Invoked when the


Android.Content.Res.Configuration device configuration
changes while your
activity is running.

OnCreate Android.App.Activity , Raised when the activity


Android.OS.Bundle? is created.

OnDestroy Android.App.Activity Invoked when the Always call the


activity is finishing, or super class's
because the system is implementation.
temporarily destroying
the activity instance to
save space.

OnNewIntent Android.App.Activity , Invoked when the


Android.Content.Intent? activity is relaunched
while at the top of the
activity stack instead of
a new instance of the
activity being started.

OnPause Android.App.Activity Invoked when an Always call the


activity is going into the super class's
background, but has not implementation.
yet been killed.
Delegate Arguments Description Comments

OnPostCreate Android.App.Activity , Invoked when activity Always call the


Android.OS.Bundle? startup is complete, super class's
after OnStart and implementation.
OnRestoreInstanceState This is a system-
have been called. only event that
generally
shouldn't be
used by apps.

OnPostResume Android.App.Activity Invoked when activity Always call the


resume is complete, super class's
after OnResume has been implementation.
called. This is a system-
only event that
generally
shouldn't be
used by apps.

OnRequestPermissionsResult Android.App.Activity , int , Invoked as a callback for


string[] , the result from
Android.Content.PM.Permission[] requesting permissions.

OnRestart Android.App.Activity Invoked after OnStop Always call the


when the current super class's
activity is being implementation.
redisplayed to the user
(the user has navigated
back to it).

OnRestoreInstanceState Android.App.Activity , Invoked after OnStart


Android.OS.Bundle when the activity is
being reinitialized from
a previously saved state.

OnResume Android.App.Activity Invoked after


OnRestoreInstanceState ,
OnRestart , or OnPause ,
to indicate that the
activity is active and is
ready to receive input.

OnSaveInstanceState Android.App.Activity , Invoked to retrieve per-


Android.OS.Bundle instance state from an
activity being killed so
that the state can be
restored in OnCreate or
OnRestoreInstanceState .

OnStart Android.App.Activity Invoked after OnCreate Always call the


or OnRestart when the super class's
activity has been implementation.
stopped, but is now
being displayed to the
user.
Delegate Arguments Description Comments

OnStop Android.App.Activity Invoked when the Always call the


activity is no longer super class's
visible to the user. implementation.

) Important

Each delegate has a corresponding identically named extension method, that can be called to
register a handler for the delegate.

To respond to an Android lifecycle delegate being invoked, call the ConfigureLifecycleEvents


method on the MauiAppBuilder object in the CreateMauiapp method of your MauiProgram class. Then,
on the ILifecycleBuilder object, call the AddAndroid method and specify the Action that registers
handlers for the required delegates:

C#

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureLifecycleEvents(events =>

#if ANDROID

events.AddAndroid(android => android

.OnActivityResult((activity, requestCode, resultCode, data) =>


LogEvent(nameof(AndroidLifecycle.OnActivityResult), requestCode.ToString()))

.OnStart((activity) =>
LogEvent(nameof(AndroidLifecycle.OnStart)))

.OnCreate((activity, bundle) =>


LogEvent(nameof(AndroidLifecycle.OnCreate)))

.OnBackPressed((activity) =>
LogEvent(nameof(AndroidLifecycle.OnBackPressed)) && false)

.OnStop((activity) =>
LogEvent(nameof(AndroidLifecycle.OnStop))));

#endif

static bool LogEvent(string eventName, string type = null)

System.Diagnostics.Debug.WriteLine($"Lifecycle event:
{eventName}{(type == null ? string.Empty : $" ({type})")}");

return true;

});

return builder.Build();

For more information about the Android app lifecycle, see Understand the Activity Lifecycle on
developer.android.com.

iOS
The following table lists the .NET MAUI delegates that are invoked in response to iOS lifecycle events
being raised:

Delegate Arguments Description

ApplicationSignificantTimeChange UIKit.UIApplication Invoked when a


significant time change
occurs, such as midnight,
carrier-changed time, or
the start or stop of
daylight savings.

ContinueUserActivity UIKit.UIApplication , Invoked when the app


Foundation.NSUserActivity , receives data associated
UIKit.UIApplicationRestorationHandler with a user activity, such
as transferring an activity
from a different device
using Handoff.

DidEnterBackground UIKit.UIApplication Invoked when the app


has entered the
background.

FinishedLaunching UIKit.UIApplication , Invoked when the app


Foundation.NSDictionary has launched.

OnActivated UIKit.UIApplication Invoked when the app is


launched and every time
the app returns to the
foreground.

OnResignActivation UIKit.UIApplication Invoked when the app is


about to enter the
background, be
suspended, or when the
user receives an
interruption such as a
phone call or text.

OpenUrl UIKit.UIApplication , Invoked when the app


Foundation.NSDictionary should open a specified
URL.

PerformActionForShortcutItem UIKit.UIApplication , Invoked when a Home


UIKit.UIApplicationShortcutItem , screen quick action is
UIKit.UIOperationHandler initiated.
Delegate Arguments Description

SceneContinueUserActivity UIKit.UIScene , Foundation.NSUserActivity Invoked to handle the


specified Handoff-related
activity.

SceneDidDisconnect UIKit.UIScene Invoked when a scene is


removed from the app.

SceneDidEnterBackground UIKit.UIScene Invoked when a scene is


running in the
background and isn't
onscreen.

SceneDidFailToContinueUserActivity UIKit.UIScene , string , Invoked to inform the


Foundation.NSError user that the activity
couldn't be completed.

SceneDidUpdateUserActivity UIKit.UIScene , Foundation.NSUserActivity Invoked when the


specified activity is
updated.

SceneOnActivated UIKit.UIScene Invoked when the scene


becomes active and able
to respond to user events.

SceneOnResignActivation UIKit.UIScene Invoked when the scene is


about to resign the active
state and stop
responding to user
events.

SceneOpenUrl UIKit.UIScene , Invoked when a scene


Foundation.NSSet<UIKit.UIOpenUrlContext> asks to open one or more
URLs.

SceneRestoreInteractionState UIKit.UIScene , Foundation.NSUserActivity Invoked to restore the


activity state.

SceneWillConnect UIKit.UIScene , UIKit.UISceneSession , Invoked when a scene is


UIKit.UISceneConnectionOptions added to the app.

SceneWillContinueUserActivity UIKit.UIScene , string Invoked to prepare to


receive Handoff-related
data.

SceneWillEnterForeground UIKit.UIScene Invoked when a scene is


about to run in the
foreground and become
visible to the user.

WillEnterForeground UIKit.UIApplication Invoked if the app will be


returning from a
backgrounded state.
Delegate Arguments Description

WillFinishLaunching UIKit.UIApplication , Invoked when app


Foundation.NSDictionary launching has begun, but
state restoration has not
yet occurred.

WillTerminate UIKit.UIApplication Invoked if the app is


being terminated due to
memory constraints, or
directly by the user.

WindowSceneDidUpdateCoordinateSpace UIKit.UIWindowScene , Invoked when the size,


UIKit.IUICoordinateSpace , orientation, or traits of a
UIKit.UIInterfaceOrientation , scene change.
UIKit.UITraitCollection

) Important

Each delegate has a corresponding identically named extension method, that can be called to
register a handler for the delegate.

To respond to an iOS lifecycle delegate being invoked, call the ConfigureLifecycleEvents method on
the MauiAppBuilder object in the CreateMauiapp method of your MauiProgram class. Then, on the
ILifecycleBuilder object, call the AddiOS method and specify the Action that registers handlers for
the required delegates:

C#

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureLifecycleEvents(events =>

#if IOS

events.AddiOS(ios => ios

.OnActivated((app) =>
LogEvent(nameof(iOSLifecycle.OnActivated)))

.OnResignActivation((app) =>
LogEvent(nameof(iOSLifecycle.OnResignActivation)))

.DidEnterBackground((app) =>
LogEvent(nameof(iOSLifecycle.DidEnterBackground)))

.WillTerminate((app) =>
LogEvent(nameof(iOSLifecycle.WillTerminate))));

#endif

static bool LogEvent(string eventName, string type = null)

System.Diagnostics.Debug.WriteLine($"Lifecycle event:
{eventName}{(type == null ? string.Empty : $" ({type})")}");

return true;

});

return builder.Build();

For more information about the iOS app lifecycle, see Managing Your App's Life Cycle on
developer.apple.com.

Windows
The following table lists the .NET MAUI delegates that are invoked in response to Windows lifecycle
events being raised:

Delegate Arguments Description

OnActivated Microsoft.UI.Xaml.Window , Invoked when the


Microsoft.UI.Xaml.WindowActivatedEventArgs platform Activated event
is raised, if the app isn't
resuming.

OnClosed Microsoft.UI.Xaml.Window , Invoked when the


Microsoft.UI.Xaml.WindowEventArgs platform Closed event is
raised.

OnLaunched Microsoft.UI.Xaml.Window , Invoked by .NET MAUI's


Microsoft.UI.Xaml.LaunchActivatedEventArgs Application.OnLaunched
override once the native
window has been
created and activated.

OnLaunching Microsoft.UI.Xaml.Window , Invoked by .NET MAUI's


Microsoft.UI.Xaml.LaunchActivatedEventArgs Application.OnLaunched
override before the
native window has been
created and activated.

OnPlatformMessage Microsoft.UI.Xaml.Window , Invoked when .NET


WindowsPlatformMessageEventArgs MAUI receives specific
native Windows
messages.

OnPlatformWindowSubclassed Microsoft.UI.Xaml.Window , Invoked by .NET MAUI


WindowsPlatformWindowSubclassedEventArgs when the Win32 window
is subclassed.

OnResumed Microsoft.UI.Xaml.Window Invoked when the


platform Activated event
is raised, if the app is
resuming.
Delegate Arguments Description

OnVisibilityChanged Microsoft.UI.Xaml.Window , Invoked when the


Microsoft.UI.Xaml.WindowVisibilityChangedEventArgs platform
VisibilityChanged event
is raised.

OnWindowCreated Microsoft.UI.Xaml.Window Invoked when the native


window is created for
the cross-platform
Window .

.NET MAUI exposes specific native Windows messages as a lifecycle event with the
OnPlatformMessage delegate. The WindowsPlatformMessageEventArgs object that accompanies this
delegate includes a MessageId property, of type uint . The value of this property can be examined to
determine which message has been passed to your app window. For more information about
windows messages, see Windows Messages (Get Started with Win32 and C++). For a list of window
message constants, see Window notifications.

) Important

Each delegate has a corresponding identically named extension method, that can be called to
register a handler for the delegate.

To respond to a Windows lifecycle delegate being invoked, call the ConfigureLifecycleEvents


method on the MauiAppBuilder object in the CreateMauiApp method of your MauiProgram class. Then,
on the ILifecycleBuilder object, call the AddWindows method and specify the Action that registers
handlers for the required delegates:

C#

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureLifecycleEvents(events =>

#if WINDOWS

events.AddWindows(windows => windows

.OnActivated((window, args) =>


LogEvent(nameof(WindowsLifecycle.OnActivated)))

.OnClosed((window, args) =>


LogEvent(nameof(WindowsLifecycle.OnClosed)))

.OnLaunched((window, args) =>


LogEvent(nameof(WindowsLifecycle.OnLaunched)))

.OnLaunching((window, args) =>


LogEvent(nameof(WindowsLifecycle.OnLaunching)))

.OnVisibilityChanged((window, args) =>


LogEvent(nameof(WindowsLifecycle.OnVisibilityChanged)))

.OnPlatformMessage((window, args) =>

if (args.MessageId == Convert.ToUInt32("031A", 16))

// System theme has changed

}));

#endif

static bool LogEvent(string eventName, string type = null)

System.Diagnostics.Debug.WriteLine($"Lifecycle event:
{eventName}{(type == null ? string.Empty : $" ({type})")}");

return true;

});

return builder.Build();

Retrieve the Window object


Platform code can retrieve the app's Window object from platform lifecycle events, with the
GetWindow extension method:

C#

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureLifecycleEvents(events =>

#if WINDOWS

events.AddWindows(windows => windows

.OnClosed((window, args) =>

IWindow appWindow = window.GetWindow();

}));

#endif

});

return builder.Build();

Custom lifecycle events


While .NET MAUI defines delegates that are invoked in response to platform lifecycle events being
raised, it only exposes a common set of platform lifecycle events. However, it also includes a
mechanism, typically for library authors, that enables apps to be notified when additional platform
lifecycle events are raised. The process for accomplishing this is as follows:

Register an event handler for a platform lifecycle event that isn't exposed by .NET MAUI.
In the event handler for the platform lifecycle event, retrieve the ILifecycleEventService
instance and call its InvokeEvents method, specifying the platform event name as its argument.

Then, apps that want to receive notification of the platform lifecycle event should modify the
CreateMauiApp method of their MauiProgram class to call the ConfigureLifecycleEvents method on

the MauiAppBuilder object. Then, on the ILifecycleBuilder object, call the AddEvent method and
specify the platform event name and the Action that will be invoked when the platform event is
raised.

Example
The WinUI 3 Window.SizeChanged event occurs when the native app window has first rendered, or
has changed its rendering size. .NET MAUI doesn't expose this platform event as a lifecycle event.
However, apps can receive notification when this platform event is raised by using the following
approach:

Register an event handler for the Window.SizeChanged platform lifecycle event:

C#

using Microsoft.Maui.LifecycleEvents;

...

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureLifecycleEvents(events =>

#if WINDOWS

events.AddWindows(windows => windows

.OnWindowCreated(window =>

window.SizeChanged += OnSizeChanged;

}));

#endif

});

return builder.Build();

In the event handler for the platform lifecycle event, retrieve the ILifecycleEventService
instance and call its InvokeEvents method, specifying the platform event name as its argument:
C#

using Microsoft.Maui.LifecycleEvents;

...

#if WINDOWS

static void OnSizeChanged(object sender,


Microsoft.UI.Xaml.WindowSizeChangedEventArgs args)

ILifecycleEventService service =
MauiWinUIApplication.Current.Services.GetRequiredService<ILifecycleEventService>();

service.InvokeEvents(nameof(Microsoft.UI.Xaml.Window.SizeChanged));

#endif

The MauiWinUIApplication type on Windows can be used to access the native app instance via
its Current property. The MauiApplication type on Android can be used to access the native
app instance. Similarly, the MauiUIApplicationDelegate type on iOS can be used to access the
native app instance.

2 Warning

Invoking an unregistered event, with the InvokeEvents method, doesn't throw an


exception.

In the CreateMauiApp method of your MauiProgram class, call the ConfigureLifecycleEvents


method on the MauiAppBuilder object. Then, on the ILifecycleBuilder object, call the
AddEvent method and specify the platform event name and the Action that will be invoked

when the platform event is raised:

C#

using Microsoft.Maui.LifecycleEvents;

namespace PlatformLifecycleDemo

public static class MauiProgram

public static MauiApp CreateMauiApp()

var builder = MauiApp.CreateBuilder();

builder

.UseMauiApp<App>()

.ConfigureLifecycleEvents(events =>

#if WINDOWS

events.AddWindows(windows => windows

.OnWindowCreated(window =>

window.SizeChanged += OnSizeChanged;

}));

events.AddEvent(nameof(Microsoft.UI.Xaml.Window.SizeChanged),
() => LogEvent("Window SizeChanged"));

#endif

static bool LogEvent(string eventName, string type = null)

System.Diagnostics.Debug.WriteLine($"Lifecycle event:
{eventName}{(type == null ? string.Empty : $" ({type})")}");

return true;

});

return builder.Build();

The overall effect is that when a user changes the app window size on Windows, the action specified
in the AddEvent method is executed.

7 Note

The AddEvent method also has an overload that enables a delegate to be specified.
Behaviors
Article • 04/03/2023 • 9 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) behaviors let you add functionality to user
interface controls without having to subclass them. Instead, the functionality is
implemented in a behavior class and attached to the control as if it was part of the
control itself.

Behaviors enable you to implement code that you would normally have to write as
code-behind, because it directly interacts with the API of the control in such a way that it
can be concisely attached to the control and packaged for reuse across more than one
application. They can be used to provide a full range of functionality to controls, such as:

Adding an email validator to an Entry.


Creating a rating control using a tap gesture recognizer.
Controlling an animation.

.NET MAUI supports two different types of behaviors:

Attached behaviors are static classes with one or more attached properties. For
more information about attached behaviors, see Attached behaviors.
.NET MAUI behaviors are classes that derive from the Behavior or Behavior<T>
class, where T is the type of the control to which the behavior should apply. For
more information, see .NET MAUI Behaviors.

Attached behaviors
Attached behaviors are static classes with one or more attached properties. An attached
property is a special type of bindable property. They are defined in one class but
attached to other objects, and they are recognizable in XAML as attributes that contain
a class and a property name separated by a period. For more information about
attached properties, see Attached properties.

An attached property can define a propertyChanged delegate that will be executed when
the value of the property changes, such as when the property is set on a control. When
the propertyChanged delegate executes, it's passed a reference to the control on which it
is being attached, and parameters that contain the old and new values for the property.
This delegate can be used to add new functionality to the control that the property is
attached to by manipulating the reference that is passed in, as follows:
1. The propertyChanged delegate casts the control reference, which is received as a
BindableObject, to the control type that the behavior is designed to enhance.
2. The propertyChanged delegate modifies properties of the control, calls methods of
the control, or registers event handlers for events exposed by the control, to
implement the core behavior functionality.

2 Warning

Attached behaviors are defined in a static class, with static properties and
methods. This makes it difficult to create attached behaviors that have state.

Create an attached behavior


An attached behavior can be implemented by creating a static class that contains an
attached property that specifies a propertyChanged delegate.

The following example shows the AttachedNumericValidationBehavior class, which


highlights the value entered by the user into an Entry control in red if it's not a double :

C#

public static class AttachedNumericValidationBehavior

public static readonly BindableProperty AttachBehaviorProperty =

BindableProperty.CreateAttached("AttachBehavior", typeof(bool),
typeof(AttachedNumericValidationBehavior), false, propertyChanged:
OnAttachBehaviorChanged);

public static bool GetAttachBehavior(BindableObject view)

return (bool)view.GetValue(AttachBehaviorProperty);

public static void SetAttachBehavior(BindableObject view, bool value)

view.SetValue(AttachBehaviorProperty, value);

static void OnAttachBehaviorChanged(BindableObject view, object


oldValue, object newValue)

Entry entry = view as Entry;

if (entry == null)

return;

bool attachBehavior = (bool)newValue;

if (attachBehavior)

entry.TextChanged += OnEntryTextChanged;

else

entry.TextChanged -= OnEntryTextChanged;

static void OnEntryTextChanged(object sender, TextChangedEventArgs args)

double result;

bool isValid = double.TryParse(args.NewTextValue, out result);

((Entry)sender).TextColor = isValid ? Colors.Black : Colors.Red;

In this example, the AttachedNumericValidationBehavior class contains an attached


property named AttachBehavior with a static getter and setter, which controls the
addition or removal of the behavior to the control to which it will be attached. This
attached property registers the OnAttachBehaviorChanged method that will be executed
when the value of the property changes. This method registers or de-registers an event
handler for the TextChanged event, based on the value of the AttachBehavior attached
property. The core functionality of the behavior is provided by the OnEntryTextChanged
method, which parses the value entered in the Entry and sets the TextColor property to
red if the value isn't a double .

Consume an attached behavior


An attached behavior can be consumed by setting its attached property on the target
control.

The following example shows consuming the AttachedNumericValidationBehavior class


on an Entry by adding the AttachBehavior attached property to the Entry:

XAML

<ContentPage ...

xmlns:local="clr-namespace:BehaviorsDemos">
<Entry Placeholder="Enter a System.Double"
local:AttachedNumericValidationBehavior.AttachBehavior="true" />

</ContentPage>

The equivalent Entry in C# is shown in the following code example:

C#

Entry entry = new Entry { Placeholder = "Enter a System.Double" };

AttachedNumericValidationBehavior.SetAttachBehavior(entry, true);

The following screenshot shows the attached behavior responding to invalid input:

7 Note

Attached behaviors are written for a specific control type (or a superclass that can
apply to many controls), and they should only be added to a compatible control.

Remove an attached behavior


The AttachedNumericValidationBehavior class can be removed from a control by setting
the AttachBehavior attached property to false :

XAML

<Entry Placeholder="Enter a System.Double"


local:AttachedNumericValidationBehavior.AttachBehavior="false" />

At runtime, the OnAttachBehaviorChanged method will be executed when the value of the
AttachBehavior attached property is set to false . The OnAttachBehaviorChanged method

will then de-register the event handler for the TextChanged event, ensuring that the
behavior isn't executed as you interact with the control.

.NET MAUI behaviors


.NET MAUI behaviors are created by deriving from the Behavior or Behavior<T> class.

The process for creating a .NET MAUI behavior is as follows:

1. Create a class that inherits from the Behavior or Behavior<T> class, where T is the
type of the control to which the behavior should apply.
2. Override the OnAttachedTo method to perform any required setup.
3. Override the OnDetachingFrom method to perform any required cleanup.
4. Implement the core functionality of the behavior.

This results in the structure shown in the following example:

C#

public class MyBehavior : Behavior<View>

protected override void OnAttachedTo(View bindable)

base.OnAttachedTo(bindable);

// Perform setup

protected override void OnDetachingFrom(View bindable)

base.OnDetachingFrom(bindable);

// Perform clean up

// Behavior implementation

The OnAttachedTo method is called immediately after the behavior is attached to a


control. This method receives a reference to the control to which it is attached, and can
be used to register event handlers or perform other setup that's required to support the
behavior functionality. For example, you could subscribe to an event on a control. The
behavior functionality would then be implemented in the event handler for the event.

The OnDetachingFrom method is called when the behavior is removed from the control.
This method receives a reference to the control to which it is attached, and is used to
perform any required cleanup. For example, you could unsubscribe from an event on a
control to prevent memory leaks.

The behavior can then be consumed by attaching it to the Behaviors collection of the
control.

Create a .NET MAUI Behavior


A .NET MAUI behavior can be implemented by creating a class that derives from the
Behavior or Behavior<T> class, and overriding the OnAttachedTo and OnDetachingFrom
methods.

The following example shows the NumericValidationBehavior class, which highlights the
value entered by the user into an Entry control in red if it's not a double :
C#

public class NumericValidationBehavior : Behavior<Entry>

protected override void OnAttachedTo(Entry entry)

entry.TextChanged += OnEntryTextChanged;

base.OnAttachedTo(entry);
}

protected override void OnDetachingFrom(Entry entry)

entry.TextChanged -= OnEntryTextChanged;

base.OnDetachingFrom(entry);

void OnEntryTextChanged(object sender, TextChangedEventArgs args)

double result;

bool isValid = double.TryParse(args.NewTextValue, out result);

((Entry)sender).TextColor = isValid ? Colors.Black : Colors.Red;

In this example, the NumericValidationBehavior class derives from the Behavior<T>


class, where T is an Entry. The OnAttachedTo method registers an event handler for the
TextChanged event, with the OnDetachingFrom method de-registering the TextChanged

event to prevent memory leaks. The core functionality of the behavior is provided by the
OnEntryTextChanged method, which parses the value entered in the Entry and sets the
TextColor property to red if the value isn't a double .

) Important

.NET MAUI does not set the BindingContext of a behavior, because behaviors can
be shared and applied to multiple controls through styles.

Consume a .NET MAUI behavior


Every .NET MAUI control has a Behaviors collection, to which one or more behaviors can
be added:

XAML

<Entry Placeholder="Enter a System.Double">

<Entry.Behaviors>

<local:NumericValidationBehavior />

</Entry.Behaviors>

</Entry>

The equivalent Entry in C# is shown in the following code example:

C#

Entry entry = new Entry { Placeholder = "Enter a System.Double" };

entry.Behaviors.Add(new NumericValidationBehavior());

The following screenshot shows the .NET MAUI behavior responding to invalid input:

2 Warning

.NET MAUI behaviors are written for a specific control type (or a superclass that can
apply to many controls), and they should only be added to a compatible control.
Attempting to attach a .NET MAUI behavior to an incompatible control will result in
an exception being thrown.

Consume a .NET MAUI behavior with a style


.NET MAUI behaviors can be consumed by an explicit or implicit style. However, creating
a style that sets the Behaviors property of a control is not possible because the property
is read-only. The solution is to add an attached property to the behavior class that
controls adding and removing the behavior. The process is as follows:

1. Add an attached property to the behavior class that will be used to control the
addition or removal of the behavior to the control to which the behavior will be
attached. Ensure that the attached property registers a propertyChanged delegate
that will be executed when the value of the property changes.
2. Create a static getter and setter for the attached property.
3. Implement logic in the propertyChanged delegate to add and remove the behavior.

The following example shows the NumericValidationStyleBehavior class, which has an


attached property that controls adding and removing the behavior:

C#

public class NumericValidationStyleBehavior : Behavior<Entry>

public static readonly BindableProperty AttachBehaviorProperty =

BindableProperty.CreateAttached("AttachBehavior", typeof(bool),
typeof(NumericValidationStyleBehavior), false, propertyChanged:
OnAttachBehaviorChanged);

public static bool GetAttachBehavior(BindableObject view)

return (bool)view.GetValue(AttachBehaviorProperty);

public static void SetAttachBehavior(BindableObject view, bool value)

view.SetValue(AttachBehaviorProperty, value);

static void OnAttachBehaviorChanged(BindableObject view, object


oldValue, object newValue)

Entry entry = view as Entry;

if (entry == null)

return;

bool attachBehavior = (bool)newValue;

if (attachBehavior)

entry.Behaviors.Add(new NumericValidationStyleBehavior());

else

Behavior toRemove = entry.Behaviors.FirstOrDefault(b => b is


NumericValidationStyleBehavior);

if (toRemove != null)

entry.Behaviors.Remove(toRemove);

...

In this example, the NumericValidationStyleBehavior class contains an attached


property named AttachBehavior with a static getter and setter, which controls the
addition or removal of the behavior to the control to which it will be attached. This
attached property registers the OnAttachBehaviorChanged method that will be executed
when the value of the property changes. This method adds or removes the behavior to
the control, based on the value of the AttachBehavior attached property.

The following code example shows an explicit style for the


NumericValidationStyleBehavior that uses the AttachBehavior attached property, and
which can be applied to Entry controls:

XAML

<Style x:Key="NumericValidationStyle" TargetType="Entry">

<Style.Setters>

<Setter
Property="local:NumericValidationStyleBehavior.AttachBehavior" Value="true"
/>

</Style.Setters>

</Style>

The Style can be applied to an Entry by setting its Style property to the style using the
StaticResource markup extension:

XAML

<Entry Placeholder="Enter a System.Double" Style="{StaticResource


NumericValidationStyle}">

For more information about styles, see Styles.

7 Note

While you can add bindable properties to a behavior that is set or queried in XAML,
if you do create behaviors that have state they should not be shared between
controls in a Style in a ResourceDictionary.

Remove a .NET MAUI behavior


The OnDetachingFrom method is called when a behavior is removed from a control, and
is used to perform any required cleanup such as unsubscribing from an event to prevent
a memory leak. However, behaviors are not implicitly removed from controls unless the
control's Behaviors collection is modified by the Remove or Clear method:

C#

Behavior toRemove = entry.Behaviors.FirstOrDefault(b => b is


NumericValidationStyleBehavior);

if (toRemove != null)

entry.Behaviors.Remove(toRemove);

Alternatively, the control's Behaviors collection can be cleared:

C#

entry.Behaviors.Clear();

7 Note

.NET MAUI behaviors are not implicitly removed from controls when pages are
popped from the navigation stack. Instead, they must be explicitly removed prior to
pages going out of scope.
Data binding
Article • 04/04/2023 • 2 minutes to read

Browse the sample

A .NET Multi-platform App UI (.NET MAUI) app consists of one or more pages, each of
which typically contains multiple user-interface objects called views. One of the primary
tasks of the app is to keep these views synchronized, and to keep track of the various
values or selections that they represent. Often the views represent values from an
underlying data source, and users manipulate these views to change that data. When
the view changes, the underlying data must reflect that change, and similarly, when the
underlying data changes, that change must be reflected in the view.

To handle this successfully, the app must be notified of changes in these views or the
underlying data. The common solution is to define events that signal when a change
occurs. An event handler can then be installed that is notified of these changes. It
responds by transferring data from one object to another. However, when there are
many views, there must also be many event handlers, which results in a lot of boilerplate
code.

Data binding automates this task, and renders the event handlers unnecessary. Data
bindings can be implemented either in XAML or code, but they are much more common
in XAML where they help to reduce the size of the code-behind file. By replacing
procedural code in event handlers with declarative code or markup, the app is simplified
and clarified.

Data binding is therefore the technique of linking properties of two objects so that
changes in one property are automatically reflected in the other property. One of the
two objects involved in a data binding is almost always an element that derives from
View and forms part of the visual interface of a page. The other object is either:

Another View derivative, usually on the same page.


An object in a code file.

Data bindings between two View derivatives are often shown in these articles, for
purposes of clarity and simplicity. However, the same principles can be applied to data
bindings between a View and other objects. When an application is built using the
Model-View-ViewModel (MVVM) architecture, the class with underlying data is often
called a viewmodel.

) Important
.NET MAUI marshals binding updates to the UI thread. When using MVVM this
enables you to update data-bound viewmodel properties from any thread, with
.NET MAUI's binding engine bringing the updates to the UI thread.
Basic bindings
Article • 04/03/2023 • 7 minutes to read

Browse the sample

A .NET Multi-platform App UI (.NET MAUI) data binding links a pair of properties
between two objects, at least one of which is usually a user-interface object. These two
objects are called the target and the source:

The target is the object (and property) on which the data binding is set.
The source is the object (and property) referenced by the data binding.

In the simplest case, data flows from the source to the target, which means that the
value of the target property is set from the value of the source property. However, in
some cases, data can alternatively flow from the target to the source, or in both
directions.

) Important

The target is always the object on which the data binding is set even if it's
providing data rather than receiving data.

Bindings with a binding context


Consider the following XAML example, whose intent is to rotate a Label by manipulating
a Slider:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="DataBindingDemos.BasicCodeBindingPage"

Title="Basic Code Binding">

<StackLayout Padding="10, 0">

<Label x:Name="label"

Text="TEXT"

FontSize="48"

HorizontalOptions="Center"

VerticalOptions="Center" />

<Slider x:Name="slider"

Maximum="360"

VerticalOptions="Center" />

</StackLayout>

</ContentPage>

Without data bindings, you would set the ValueChanged event of the Slider to an event
handler that accesses the Value property of the Slider and sets that value to the
Rotation property of the Label. The data binding automates this task, and so the event

handler and the code within it are no longer necessary.

You can set a binding on an instance of any class that derives from BindableObject,
which includes Element, VisualElement, View, and View derivatives. The binding is always
set on the target object. The binding references the source object. To set the data
binding, use the following two members of the target class:

The BindingContext property specifies the source object.


The SetBinding method specifies the target property and source property.

In this example, the Label is the binding target, and the Slider is the binding source.
Changes in the Slider source affect the rotation of the Label target. Data flows from the
source to the target.

The SetBinding method defined by BindableObject has an argument of type


BindingBase from which the Binding class derives, but there are other SetBinding

methods defined by the BindableObjectExtensions class. The code-behind for the XAML
uses a simpler SetBinding extension method from the BindableObjectExtensions class:

C#

public partial class BasicCodeBindingPage : ContentPage

public BasicCodeBindingPage()

InitializeComponent();

label.BindingContext = slider;

label.SetBinding(Label.RotationProperty, "Value");

The Label object is the binding target so that's the object on which this property is set
and on which the method is called. The BindingContext property indicates the binding
source, which is the Slider. The SetBinding method is called on the binding target but
specifies both the target property and the source property. The target property is
specified as a BindableProperty object: Label.RotationProperty . The source property is
specified as a string and indicates the Value property of Slider.
) Important

The target property must be backed by a bindable property. Therefore, the target
object must be an instance of a class that derives from BindableObject. For more
information, see Bindable properties.

The source property is specified as a string. Internally, reflection is used to access the
actual property. In this particular case, however, the Value property is also backed by a
bindable property.

As you manipulate the Slider, the Label rotates accordingly:

Alternatively, the data binding can be specified in XAML:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="DataBindingDemos.BasicXamlBindingPage"

Title="Basic XAML Binding">

<StackLayout Padding="10, 0">

<Label Text="TEXT"

FontSize="80"

HorizontalOptions="Center"

VerticalOptions="Center"

BindingContext="{x:Reference Name=slider}"

Rotation="{Binding Path=Value}" />

<Slider x:Name="slider"

Maximum="360"

VerticalOptions="Center" />

</StackLayout>

</ContentPage>

Just as in code, the data binding is set on the target object, which is the Label. Two
XAML markup extensions are used to define the data binding:

The x:Reference markup extension is required to reference the source object,


which is the Slider named slider .
The Binding markup extension links the Rotation property of the Label to the
Value property of the Slider.

For more information about XAML markup extensions, see Consume XAML markup
extensions.

7 Note

The source property is specified with the Path property of the Binding markup
extension, which corresponds with the Path property of the Binding class.

XAML markup extensions such as x:Reference and Binding can have content property
attributes defined, which for XAML markup extensions means that the property name
doesn't need to appear. The Name property is the content property of x:Reference , and
the Path property is the content property of Binding , which means that they can be
eliminated from the expressions:

XAML

<Label Text="TEXT"

FontSize="80"

HorizontalOptions="Center"

VerticalOptions="Center"

BindingContext="{x:Reference slider}"

Rotation="{Binding Value}" />

Bindings without a binding context


The BindingContext property is an important component of data bindings, but it is not
always necessary. The source object can instead be specified in the SetBinding call or
the Binding markup extension:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="DataBindingDemos.AlternativeCodeBindingPage"
Title="Alternative Code Binding">

<StackLayout Padding="10, 0">

<Label x:Name="label"

Text="TEXT"

FontSize="40"

HorizontalOptions="Center"

VerticalOptions="CenterAndExpand" />

<Slider x:Name="slider"

Minimum="-2"

Maximum="2"

VerticalOptions="CenterAndExpand" />

</StackLayout>

</ContentPage>

In this example, the Slider is defined to control the Scale property of the Label. For that
reason, the Slider is set for a range of -2 to 2.

The code-behind file sets the binding with the SetBinding method, with the second
argument being a constructor for the Binding class:

C#

public partial class AlternativeCodeBindingPage : ContentPage

public AlternativeCodeBindingPage()

InitializeComponent();

label.SetBinding(Label.ScaleProperty, new Binding("Value", source:


slider));

The Binding constructor has 6 parameters, so the source parameter is specified with a
named argument. The argument is the slider object.

7 Note

The VisualElement class also defines ScaleX and ScaleY properties, which can
scale the VisualElement differently in the horizontal and vertical directions.

Alternatively, the data binding can be specified in XAML:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="DataBindingDemos.AlternativeXamlBindingPage"
Title="Alternative XAML Binding">

<StackLayout Padding="10, 0">

<Label Text="TEXT"

FontSize="40"

HorizontalOptions="Center"

VerticalOptions="Center"

Scale="{Binding Source={x:Reference slider},

Path=Value}" />

<Slider x:Name="slider"

Minimum="-2"

Maximum="2"

VerticalOptions="Center" />

</StackLayout>

</ContentPage>

In this example, the Binding markup extension has two properties set, Source and Path ,
separated by a comma. The Source property is set to an embedded x:Reference
markup extension that otherwise has the same syntax as setting the BindingContext .

The content property of the Binding markup extension is Path , but the Path= part of
the markup extension can only be eliminated if it is the first property in the expression.
To eliminate the Path= part, you need to swap the two properties:

XAML

Scale="{Binding Value, Source={x:Reference slider}}" />

Although XAML markup extensions are usually delimited by curly braces, they can also
be expressed as object elements:

XAML

<Label Text="TEXT"

FontSize="40"

HorizontalOptions="Center"

VerticalOptions="Center">

<Label.Scale>

<Binding Source="{x:Reference slider}"

Path="Value" />

</Label.Scale>

</Label>

In this example, the Source and Path properties are regular XAML attributes. The values
appear within quotation marks and the attributes are not separated by a comma. The
x:Reference markup extension can also become an object element:

XAML

<Label Text="TEXT"

FontSize="40"

HorizontalOptions="Center"

VerticalOptions="Center">

<Label.Scale>

<Binding Path="Value">

<Binding.Source>

<x:Reference Name="slider" />

</Binding.Source>

</Binding>

</Label.Scale>

</Label>

This syntax isn't common, but sometimes it's necessary when complex objects are
involved.

The examples shown so far set the BindingContext property and the Source property of
Binding to an x:Reference markup extension to reference another view on the page.

These two properties are of type Object , and they can be set to any object that includes
properties that are suitable for binding sources. You can also set the BindingContext or
Source property to an x:Static markup extension to reference the value of a static

property or field, or a StaticResource markup extension to reference an object stored in


a resource dictionary, or directly to an object, which is often an instance of a viewmodel.

7 Note

The BindingContext property can also be set to a Binding object so that the
Source and Path properties of Binding define the binding context.

Binding context inheritance


You can specify the source object using the BindingContext property or the Source
property of the Binding object. If both are set, the Source property of the Binding takes
precedence over the BindingContext .

) Important
The BindingContext property value is inherited through the visual tree.

The following XAML example demonstrates binding context inheritance:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="DataBindingDemos.BindingContextInheritancePage"

Title="BindingContext Inheritance">

<StackLayout Padding="10">

<StackLayout VerticalOptions="Fill"

BindingContext="{x:Reference slider}">

<Label Text="TEXT"

FontSize="80"

HorizontalOptions="Center"

VerticalOptions="End"

Rotation="{Binding Value}" />

<BoxView Color="#800000FF"

WidthRequest="180"

HeightRequest="40"

HorizontalOptions="Center"

VerticalOptions="Start"

Rotation="{Binding Value}" />

</StackLayout>

<Slider x:Name="slider"

Maximum="360" />

</StackLayout>

</ContentPage>

In this example, the BindingContext property of the StackLayout is set to the slider
object. This binding context is inherited by both the Label and the BoxView, both of
which have their Rotation properties set to the Value property of the Slider:
Binding mode
Article • 12/23/2022 • 7 minutes to read

Browse the sample

Every .NET Multi-platform App UI (.NET MAUI) bindable property has a default binding
mode that is set when the bindable property is created, and which is available from the
DefaultBindingMode property of the BindableProperty object. This default binding mode

indicates the mode in effect when that property is a data-binding target. The default
binding mode for most properties such as Rotation , Scale , and Opacity is OneWay .
When these properties are data-binding targets, then the target property is set from the
source.

The following example shows a data binding defined on a Slider:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

x:Class="DataBindingDemos.ReverseBindingPage"

Title="Reverse Binding">

<StackLayout Padding="10, 0">

<Label x:Name="label"

Text="TEXT"

FontSize="80"

HorizontalOptions="Center"

VerticalOptions="Center" />

<Slider x:Name="slider"

VerticalOptions="Center"

Value="{Binding Source={x:Reference label},

Path=Opacity}" />

</StackLayout>

</ContentPage>

In this example, the Label is the data-binding source, and the Slider is the target. The
binding references the Opacity property of the Label, which has a default value of 1.
Therefore, the Slider is initialized to the value 1 from the initial Opacity value of Label.
This is shown in the following screenshot:
In addition, the Slider continues to work. This is because the default binding mode for
the Value property of Slider is TwoWay . This means that when the Value property is a
data-binding target, then the target is set from the source but the source is also set
from the target. This allows the Slider to be set from the initial Opacity value.

7 Note

Bindable properties don't signal a property change unless the property actually
changes. This prevents an infinite loop.

If the default binding mode on the target property is not suitable for a particular data
binding, it's possible to override it by setting the Mode property of Binding (or the Mode
property of the Binding markup extension) to one of the members of the BindingMode
enumeration:

Default
TwoWay — data goes both ways between source and target

OneWay — data goes from source to target


OneWayToSource — data goes from target to source

OneTime — data goes from source to target, but only when the BindingContext

changes

Two-way bindings
Most bindable properties have a default binding mode of OneWay but some properties
have a default binding mode of TwoWay , including the following:

Date property of DatePicker


Text property of Editor, Entry, SearchBar, and EntryCell

IsRefreshing property of ListView

SelectedItem property of MultiPage


SelectedIndex and SelectedItem properties of Picker

Value property of Slider and Stepper


IsToggled property of Switch

On property of SwitchCell

Time property of TimePicker

These properties are defined as TwoWay because when data bindings are used with the
Model-View-ViewModel (MVVM) pattern, the viewmodel class is the data-binding
source, and the view, which consists of views such as Slider, are data-binding targets.
MVVM bindings resemble the example above, because it's likely that you want each
view on the page to be initialized with the value of the corresponding property in the
viewmodel, but changes in the view should also affect the viewmodel property.

One-way-to-source bindings
Read-only bindable properties have a default binding mode of OneWayToSource . For
example, the SelectedItem property of ListView has a default binding mode of
OneWayToSource . This is because a binding on the SelectedItem property should result in

setting the binding source.

One-time bindings
Target properties with a binding mode of OneTime are updated only when the binding
context changes. For bindings on these target properties, this simplifies the binding
infrastructure because it is not necessary to monitor changes in the source properties.

Several properties have a default binding mode of OneTime , including the


IsTextPredictionEnabled property of Entry.

Viewmodels and property-change notifications


When using a viewmodel in a data-binding, the viewmodel is the data-binding source.
The viewmodel doesn't define bindable properties, but it does implement a notification
mechanism that allows the binding infrastructure to be notified when the value of a
property changes. This notification mechanism is the INotifyPropertyChanged interface,
which defines a single event named PropertyChanged. A class that implements this
interface typically fires the event when one of its public properties changes value. The
event does not need to be raised if the property never changes. The
INotifyPropertyChanged interface is also implemented by BindableObject and a

PropertyChanged event is raised whenever a bindable property changes value.

In the following example, data bindings allow you to select a color using three Slider
elements for the hue, saturation, and luminosity:

C#

public class HslColorViewModel : INotifyPropertyChanged

Color color;

string name;

float hue;

float saturation;

float luminosity;

public event PropertyChangedEventHandler PropertyChanged;

public float Hue

get

return hue;

set

if (hue != value)

Color = Color.FromHsla(value, saturation, luminosity);

public float Saturation

get

return saturation;

set

if (saturation != value)

Color = Color.FromHsla(hue, value, luminosity);

public float Luminosity

get

return luminosity;

set

if (luminosity != value)

Color = Color.FromHsla(hue, saturation, value);

public Color Color

get

return color;

set

if (color != value)

color = value;

hue = color.GetHue();

saturation = color.GetSaturation();

luminosity = color.GetLuminosity();

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Hue"));

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Saturation"));

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Luminosity"));

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Color"));

Name = NamedColor.GetNearestColorName(color);

public string Name

get

return name;

private set

if (name != value)

name = value;

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Name"));

In this example, the HslColorViewModel class defines Hue , Saturation , Luminosity ,


Color , and Name properties. When any one of the three color components changes

value, the Color property is recalculated, and PropertyChanged events are raised for all
four properties. When the Color property changes, the static GetNearestColorName
method in the NamedColor class obtains the closest named color and sets the Name
property.
When a viewmodel is set as a binding source, the binding infrastructure attaches a
handler to the PropertyChanged event. In this way, the binding can be notified of
changes to properties, and can then set the target properties from the changed values.
However, when a target property (or the Binding definition on a target property) has a
BindingMode of OneTime , it is not necessary for the binding infrastructure to attach a

handler on the PropertyChanged event. The target property is updated only when the
BindingContext changes and not when the source property itself changes.

The following XAML consumes the HslColorViewModel :

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.SimpleColorSelectorPage">

<ContentPage.BindingContext>

<local:HslColorViewModel Color="MediumTurquoise" />

</ContentPage.BindingContext>

<ContentPage.Resources>

<Style TargetType="Slider">

<Setter Property="VerticalOptions" Value="CenterAndExpand" />

</Style>

</ContentPage.Resources>

<Grid>

<Grid.RowDefinitions>

<RowDefinition Height="*" />

<RowDefinition Height="*" />

</Grid.RowDefinitions>

<BoxView Color="{Binding Color}"

Grid.Row="0" />

<StackLayout Grid.Row="1"

Margin="10, 0">

<Label Text="{Binding Name}"

HorizontalTextAlignment="Center" />

<Slider Value="{Binding Hue}" />

<Slider Value="{Binding Saturation}" />

<Slider Value="{Binding Luminosity}" />

</StackLayout>

</Grid>

</ContentPage>

In this example, the HslColorViewModel is instantiated, and Color property set, and set
as the page's BindingContext . The BoxView, Label, and three Slider views inherit the
binding context from the ContentPage. These views are all binding targets that
reference source properties in the viewmodel. For the Color property of the BoxView,
and the Text property of the Label, the data bindings are OneWay - the properties in the
view are set from the properties in the viewmodel. The Value property of the Slider,
however, uses a TwoWay binding mode. This enables each Slider to be set from the
viewmodel, and also for the viewmodel to be set from each Slider.

When the example is first run, the BoxView, Label, and three Slider elements are all set
from the viewmodel based on the initial Color property set when the viewmodel was
instantiated:

As you manipulate the sliders, the BoxView and Label are updated accordingly.

Overriding the binding mode


The binding mode for a target property can be overridden by setting the Mode property
of Binding (or the Mode property of the Binding markup extension) to one of the
members of the BindingMode enumeration.
However, setting the Mode property doesn't always produce the expected result. For
example, in the following example setting the Mode property to TwoWay doesn't work as
you might expect:

XAML

<Label Text="TEXT"

FontSize="40"

HorizontalOptions="Center"

VerticalOptions="CenterAndExpand"

Scale="{Binding Source={x:Reference slider},

Path=Value,

Mode=TwoWay}" />

In this example, it might be expected that the Slider would be initialized to the initial
value of the Scale property, which is 1, but that doesn't happen. When a TwoWay
binding is initialized, the target is set from the source first, which means that the Scale
property is set to the Slider default value of 0. When the TwoWay binding is set on the
Slider, then the Slider is initially set from the source.

Alternatively, you can set the binding mode to OneWayToSource :

XAML

<Label Text="TEXT"

FontSize="40"

HorizontalOptions="Center"

VerticalOptions="CenterAndExpand"

Scale="{Binding Source={x:Reference slider},

Path=Value,

Mode=OneWayToSource}" />

Now the Slider is initialized to 1 (the default value of Scale ) but manipulating the Slider
doesn't affect the Scale property.

7 Note

The VisualElement class also defines ScaleX and ScaleY properties, which can
scale the VisualElement differently in the horizontal and vertical directions.

A very useful application of overriding the default binding mode with a TwoWay binding
mode involves the SelectedItem property of ListView. The default binding mode is
OneWayToSource . When a data binding is set on the SelectedItem property to reference a

source property in a viewmodel, then that source property is set from the ListView
selection. However, in some circumstances, you might also want the ListView to be
initialized from the viewmodel.
String formatting
Article • 12/23/2022 • 3 minutes to read

Browse the sample

In a .NET Multi-platform App UI (.NET MAUI) app, it's sometimes convenient to use data
bindings to display the string representation of an object or value. For example, you
might want to use a Label to display the current value of a Slider. In this data binding,
the Slider is the source, and the target is the Text property of the Label.

String formatting in code is typically accomplished with the static String.Format method.
The formatting string includes formatting codes specific to various types of objects, and
you can include other text along with the values being formatted. For more information,
see Formatting Types in .NET for more information on string formatting.

String formatting can also be accomplished with data bindings by setting the
StringFormat property of Binding (or the StringFormat property of the Binding markup
extension) to a standard .NET formatting string with a placeholder:

XAML

<Slider x:Name="slider" />

<Label Text="{Binding Source={x:Reference slider},

Path=Value,
StringFormat='The slider value is {0:F2}'}" />

In XAML the formatting string is delimited by single-quote characters to help the XAML
parser avoid treating the curly braces as another XAML markup extension. In this
example, the formatting specification of F2 causes the value to be displayed with two
decimal places.

7 Note

Using the StringFormat property only makes sense when the target property is of
type string , and the binding mode is OneWay or TwoWay . For two-way bindings, the
StringFormat is only applicable for values passing from the source to the target.

The following example demonstrates several uses of the StringFormat property:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:sys="clr-namespace:System;assembly=netstandard"

x:Class="DataBindingDemos.StringFormattingPage"

Title="String Formatting">

<ContentPage.Resources>

<Style TargetType="Label">

<Setter Property="HorizontalTextAlignment" Value="Center" />

</Style>

<Style TargetType="BoxView">

<Setter Property="Color" Value="Blue" />

<Setter Property="HeightRequest" Value="2" />

<Setter Property="Margin" Value="0, 5" />

</Style>

</ContentPage.Resources>

<StackLayout Margin="10">

<Slider x:Name="slider" />

<Label Text="{Binding Source={x:Reference slider},

Path=Value,

StringFormat='The slider value is {0:F2}'}" />

<BoxView />

<TimePicker x:Name="timePicker" />

<Label Text="{Binding Source={x:Reference timePicker},

Path=Time,

StringFormat='The TimeSpan is {0:c}'}" />

<BoxView />

<Entry x:Name="entry" />

<Label Text="{Binding Source={x:Reference entry},

Path=Text,

StringFormat='The Entry text is &quot;


{0}&quot;'}" />

<BoxView />

<StackLayout BindingContext="{x:Static sys:DateTime.Now}">

<Label Text="{Binding}" />

<Label Text="{Binding Path=Ticks,

StringFormat='{0:N0} ticks since 1/1/1'}"


/>

<Label Text="{Binding StringFormat='The {{0:MMMM}} specifier


produces {0:MMMM}'}" />

<Label Text="{Binding StringFormat='The long date is {0:D}'}" />

</StackLayout>

<BoxView />

<StackLayout BindingContext="{x:Static sys:Math.PI}">

<Label Text="{Binding}" />

<Label Text="{Binding StringFormat='PI to 4 decimal points =


{0:F4}'}" />

<Label Text="{Binding StringFormat='PI in scientific notation =


{0:E7}'}" />

</StackLayout>

</StackLayout>

</ContentPage>

In this example, the bindings on the Slider and TimePicker show the use of format
specifications particular to double and TimeSpan data types. The StringFormat that
displays the text from the Entry view demonstrates how to specify double quotation
marks in the formatting string with the use of the &quot; HTML entity.

The next section in the XAML file is a StackLayout with a BindingContext set to an
x:Static markup extension that references the static DateTime.Now property. The first

binding has no properties:

XAML

<Label Text="{Binding}" />

This simply displays the DateTime value of the BindingContext with default formatting.
The second binding displays the Ticks property of DateTime , while the other two
bindings display the DateTime itself with specific formatting.

7 Note

If you need to display left or right curly braces in your formatting string, use a pair
of them. For example, StringFormat='{{0:MMMM}}' .

The last section sets the BindingContext to the value of Math.PI and displays it with
default formatting and two different types of numeric formatting:
ViewModels and string formatting
When you're using Label and StringFormat to display the value of a view that is also the
target of a viewmodel, you can either define the binding from the view to the Label or
from the viewmodel to the Label. In general, the second approach is best because it
verifies that the bindings between the view and viewmodel are working.

This approach is shown in the following example:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.BetterColorSelectorPage"

Title="Better Color Selector">

<ContentPage.BindingContext>

<local:HslColorViewModel Color="Sienna" />

</ContentPage.BindingContext>

<ContentPage.Resources>

<ResourceDictionary>

<Style TargetType="Slider">

<Setter Property="VerticalOptions" Value="Center" />

</Style>

<Style TargetType="Label">

<Setter Property="HorizontalTextAlignment" Value="Center" />

</Style>

</ResourceDictionary>

</ContentPage.Resources>

<StackLayout Margin="20">

<BoxView Color="{Binding Color}"

HeightRequest="100"

WidthRequest="100"

HorizontalOptions="Center" />

<StackLayout Margin="10, 0">

<Label Text="{Binding Name}" />

<Slider Value="{Binding Hue}" />

<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />

<Slider Value="{Binding Saturation}" />

<Label Text="{Binding Saturation, StringFormat='Saturation =


{0:F2}'}" />

<Slider Value="{Binding Luminosity}" />

<Label Text="{Binding Luminosity, StringFormat='Luminosity =


{0:F2}'}" />

</StackLayout>

</StackLayout>

</ContentPage>

In this example, there are three pairs of Slider and Label elements that are bound to the
same source property in the HslColorViewModel object. Each Label that accompanies a
Slider has a StringFormat property to display each Slider value:
Binding path
Article • 12/23/2022 • 3 minutes to read

Browse the sample

In .NET Multi-platform App UI (.NET MAUI), the Path property of the Binding class (or
the Path property of the Binding markup extension) can be set to a single property, to
a sub-property (a property of a property), or to a member of a collection.

For example, suppose a page contains a TimePicker:

XAML

<TimePicker x:Name="timePicker">

The Time property of TimePicker is of type TimeSpan , and it has a TotalSeconds


property. A data binding can be created that references the TotalSeconds property of
that TimeSpan value:

XAML

{Binding Source={x:Reference timePicker},

Path=Time.TotalSeconds}

The Time and TotalSeconds properties are simply connected with a period.

7 Note

The items in the Path string always refer to properties and not to the types of these
properties.

The following XAML shows multiple examples of binding to sub-properties:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:globe="clr-
namespace:System.Globalization;assembly=netstandard"

x:Class="DataBindingDemos.PathVariationsPage"

Title="Path Variations"

x:Name="page">

<ContentPage.Resources>

<Style TargetType="Label">

<Setter Property="FontSize" Value="18" />

<Setter Property="HorizontalTextAlignment" Value="Center" />

<Setter Property="VerticalOptions" Value="Center" />

</Style>

</ContentPage.Resources>

<StackLayout Margin="10, 0">

<TimePicker x:Name="timePicker" />

<Label Text="{Binding Source={x:Reference timePicker},

Path=Time.TotalSeconds,

StringFormat='{0} total seconds'}" />

<Label Text="{Binding Source={x:Reference page},


Path=Content.Children.Count,

StringFormat='There are {0} children in this


StackLayout'}" />

<Label Text="{Binding Source={x:Static


globe:CultureInfo.CurrentCulture},

Path=DateTimeFormat.DayNames[3],

StringFormat='The middle day of the week is


{0}'}" />

<Label>

<Label.Text>

<Binding Path="DateTimeFormat.DayNames[3]"

StringFormat="The middle day of the week in France


is {0}">

<Binding.Source>

<globe:CultureInfo>

<x:Arguments>

<x:String>fr-FR</x:String>

</x:Arguments>

</globe:CultureInfo>

</Binding.Source>

</Binding>

</Label.Text>

</Label>

<Label Text="{Binding Source={x:Reference page},


Path=Content.Children[1].Text.Length,

StringFormat='The second Label has {0}


characters'}" />

</StackLayout>

</ContentPage>

In the second Label, the binding source is the page itself. The Content property is of
type StackLayout, which has a Children property of type IList<View> , which has a
Count property indicating the number of children.

Paths with indexers


In the example above, the binding in the third Label references the CultureInfo class in
the System.Globalization namespace:

XAML

<Label Text="{Binding Source={x:Static globe:CultureInfo.CurrentCulture},

Path=DateTimeFormat.DayNames[3],

StringFormat='The middle day of the week is {0}'}" />

The source is set to the static CultureInfo.CurrentCulture property, which is an object


of type CultureInfo . That class defines a property named DateTimeFormat of type
DateTimeFormatInfo that contains a DayNames collection. The index selects the fourth
item.

The fourth Label does something similar but for the culture associated with France. The
Source property of the binding is set to CultureInfo object with a constructor:

XAML

<Label>

<Label.Text>

<Binding Path="DateTimeFormat.DayNames[3]"

StringFormat="The middle day of the week in France is {0}">

<Binding.Source>

<globe:CultureInfo>

<x:Arguments>

<x:String>fr-FR</x:String>

</x:Arguments>

</globe:CultureInfo>

</Binding.Source>

</Binding>

</Label.Text>

</Label>

For more information about specifying constructor arguments in XAML, see Pass
constructor arguments.

The last Label is similar to the second, except that it references one of the children of
the StackLayout:

XAML

<Label Text="{Binding Source={x:Reference page},

Path=Content.Children[1].Text.Length,

StringFormat='The first Label has {0} characters'}" />

That child is a Label, which has a Text property of type String , which has a Length
property. The first Label reports the TimeSpan set in the TimePicker, so when that text
changes, the final Label changes as well:

Debug complex paths


Complex path definitions can be difficult to construct. You need to know the type of
each sub-property or the type of items in the collection to correctly add the next sub-
property, but the types themselves do not appear in the path. One technique is to build
up the path incrementally and look at the intermediate results. For that last example,
you could start with no Path definition at all:

XAML

<Label Text="{Binding Source={x:Reference page},

StringFormat='{0}'}" />

That displays the type of the binding source, or DataBindingDemos.PathVariationsPage .


You know PathVariationsPage derives from ContentPage, so it has a Content property:

XAML

<Label Text="{Binding Source={x:Reference page},

Path=Content,

StringFormat='{0}'}" />

The type of the Content property is now revealed to be


Microsoft.Maui.Controls.StackLayout . Add the Children property to the Path and the
type is also Microsoft.Maui.Controls.StackLayout . Add an index to that and the type is
Microsoft.Maui.Controls.Label . Continue in this way.

As .NET MAUI processes the binding path, it installs a PropertyChanged handler on any
object in the path that implements the INotifyPropertyChanged interface. For example,
the final binding reacts to a change in the first Label because the Text property
changes. If a property in the binding path does not implement INotifyPropertyChanged ,
any changes to that property will be ignored. Some changes could entirely invalidate the
binding path, so you should use this technique only when the string of properties and
sub-properties never become invalid.
Binding value converters
Article • 04/03/2023 • 9 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) data bindings usually transfer data from a
source property to a target property, and in some cases from the target property to the
source property. This transfer is straightforward when the source and target properties
are of the same type, or when one type can be converted to the other type through an
implicit conversion. When that is not the case, a type conversion must take place.

In the String formatting article, you saw how you can use the StringFormat property of a
data binding to convert any type into a string. For other types of conversions, you need
to write some specialized code in a class that implements the IValueConverter interface.
Classes that implement IValueConverter are called value converters, but they are also
often referred to as binding converters or binding value converters.

Binding value converters


Suppose you want to define a data binding where the source property is of type int
but the target property is a bool . You want this data binding to produce a false value
when the integer source is equal to 0, and true otherwise. This can be achieved with a
class that implements the IValueConverter interface:

C#

public class IntToBoolConverter : IValueConverter

public object Convert(object value, Type targetType, object parameter,


CultureInfo culture)

return (int)value != 0;

public object ConvertBack(object value, Type targetType, object


parameter, CultureInfo culture)

return (bool)value ? 1 : 0;

You then set an instance of this class to the Converter property of the Binding class or
to the Converter property of the Binding markup extension. This class becomes part of
the data binding.

The Convert method is called when data moves from the source to the target in OneWay
or TwoWay bindings. The value parameter is the object or value from the data-binding
source. The method must return a value of the type of the data-binding target. The
method shown here casts the value parameter to an int and then compares it with 0
for a bool return value.

The ConvertBack method is called when data moves from the target to the source in
TwoWay or OneWayToSource bindings. ConvertBack performs the opposite conversion: It

assumes the value parameter is a bool from the target, and converts it to an int return
value for the source.

7 Note

If a data binding also includes a StringFormat setting, the value converter is


invoked before the result is formatted as a string.

The following example demonstrates how to use this value converter in a data binding:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.EnableButtonsPage"

Title="Enable Buttons">

<ContentPage.Resources>

<local:IntToBoolConverter x:Key="intToBool" />

</ContentPage.Resources>

<StackLayout Padding="10, 0">

<Entry x:Name="entry1"

Text=""

Placeholder="enter search term"

VerticalOptions="Center" />

<Button Text="Search"

HorizontalOptions="Center"

VerticalOptions="Center"

IsEnabled="{Binding Source={x:Reference entry1},

Path=Text.Length,

Converter={StaticResource intToBool}}"
/>

<Entry x:Name="entry2"

Text=""

Placeholder="enter destination"

VerticalOptions="Center" />

<Button Text="Submit"

HorizontalOptions="Center"

VerticalOptions="Center"

IsEnabled="{Binding Source={x:Reference entry2},

Path=Text.Length,

Converter={StaticResource intToBool}}"
/>

</StackLayout>

</ContentPage>

In this example, the IntToBoolConverter is instantiated in the page's resource dictionary.


It's then referenced with a StaticResource markup extension to set the Converter
property in two data bindings. It is very common to share data converters among
multiple data bindings on the page. If a value converter is used in multiple pages of
your application, you can instantiate it in the application-level resource dictionary.

This example demonstrates a common need when a Button performs an operation


based on text that the user types into an Entry view. The Text property of each Entry is
initialized to an empty string, because the Text property is null by default, and the
data binding will not work in that case. If nothing has been typed into the Entry, the
Button should be disabled. Each Button contains a data binding on its IsEnabled
property. The data-binding source is the Length property of the Text property of the
corresponding Entry. If that Length property is not 0, the value converter returns true
and the Button is enabled:

7 Note

If you know that a value converter will only be used in OneWay bindings, then the
ConvertBack method can simply return null .
The Convert method shown above assumes that the value argument is of type int and
the return value must be of type bool . Similarly, the ConvertBack method assumes that
the value argument is of type bool and the return value is int . If that is not the case, a
runtime exception will occur.

You can write value converters to be more generalized and to accept several different
types of data. The Convert and ConvertBack methods can use the as or is operators
with the value parameter, or can call GetType on that parameter to determine its type,
and then do something appropriate. The expected type of each method's return value is
given by the targetType parameter. Sometimes, value converters are used with data
bindings of different target types. In this case the value converter can use the
targetType argument to perform a conversion for the correct type.

If the conversion being performed is different for different cultures, use the culture
parameter for this purpose.

Binding converter properties


Value converter classes can have properties and generic parameters. The following value
converter converts a bool from the source to an object of type T for the target:

C#

public class BoolToObjectConverter<T> : IValueConverter

public T TrueObject { get; set; }

public T FalseObject { get; set; }

public object Convert(object value, Type targetType, object parameter,


CultureInfo culture)

return (bool)value ? TrueObject : FalseObject;

public object ConvertBack(object value, Type targetType, object


parameter, CultureInfo culture)

return ((T)value).Equals(TrueObject);

The following example demonstrates how this converter can be used to display the
value of a Switch view. Although it's common to instantiate value converters as
resources in a resource dictionary, this example demonstrates an alternative. Here, each
value converter is instantiated between Binding.Converter property-element tags. The
x:TypeArguments indicates the generic argument, and TrueObject and FalseObject are

both set to objects of that type:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.SwitchIndicatorsPage"

Title="Switch Indicators">

<ContentPage.Resources>

<Style TargetType="Label">

<Setter Property="FontSize" Value="18" />

<Setter Property="VerticalOptions" Value="Center" />

</Style>

<Style TargetType="Switch">

<Setter Property="VerticalOptions" Value="Center" />

</Style>

</ContentPage.Resources>

<StackLayout Padding="10, 0">

<StackLayout Orientation="Horizontal"

VerticalOptions="Center">

<Label Text="Subscribe?" />

<Switch x:Name="switch1" />

<Label>

<Label.Text>

<Binding Source="{x:Reference switch1}"

Path="IsToggled">

<Binding.Converter>

<local:BoolToObjectConverter
x:TypeArguments="x:String"

TrueObject="Of
course!"

FalseObject="No
way!" />

</Binding.Converter>

</Binding>

</Label.Text>

</Label>

</StackLayout>

<StackLayout Orientation="Horizontal"

VerticalOptions="Center">

<Label Text="Allow popups?" />

<Switch x:Name="switch2" />

<Label>

<Label.Text>

<Binding Source="{x:Reference switch2}"

Path="IsToggled">

<Binding.Converter>

<local:BoolToObjectConverter
x:TypeArguments="x:String"

TrueObject="Yes"

FalseObject="No" />

</Binding.Converter>

</Binding>

</Label.Text>

<Label.TextColor>

<Binding Source="{x:Reference switch2}"

Path="IsToggled">

<Binding.Converter>

<local:BoolToObjectConverter
x:TypeArguments="Color"

TrueObject="Green"

FalseObject="Red"
/>

</Binding.Converter>

</Binding>

</Label.TextColor>

</Label>

</StackLayout>

<StackLayout Orientation="Horizontal"

VerticalOptions="Center">

<Label Text="Learn more?" />

<Switch x:Name="switch3" />

<Label FontSize="18"

VerticalOptions="Center">

<Label.Style>

<Binding Source="{x:Reference switch3}"

Path="IsToggled">

<Binding.Converter>

<local:BoolToObjectConverter
x:TypeArguments="Style">

<local:BoolToObjectConverter.TrueObject>

<Style TargetType="Label">

<Setter Property="Text"
Value="Indubitably!" />
<Setter Property="FontAttributes"
Value="Italic, Bold" />
<Setter Property="TextColor"
Value="Green" />

</Style>

</local:BoolToObjectConverter.TrueObject>

<local:BoolToObjectConverter.FalseObject>

<Style TargetType="Label">

<Setter Property="Text" Value="Maybe


later" />

<Setter Property="FontAttributes"
Value="None" />

<Setter Property="TextColor"
Value="Red" />

</Style>

</local:BoolToObjectConverter.FalseObject>

</local:BoolToObjectConverter>

</Binding.Converter>

</Binding>

</Label.Style>

</Label>

</StackLayout>

</StackLayout>

</ContentPage>

In this example, in the last of the three Switch and Label pairs, the generic argument is
set to a Style, and entire Style objects are provided for the values of TrueObject and
FalseObject . These override the implicit style for Label set in the resource dictionary, so

the properties in that style are explicitly assigned to the Label. Toggling the Switch
causes the corresponding Label to reflect the change:

7 Note

It's also possible to use triggers to implement changes in the user-interface based
on other views. For more information, see Triggers.

Binding converter parameters


The Binding class defines a ConverterParameter property, and the Binding markup
extension also defines a ConverterParameter property. If this property is set, then the
value is passed to the Convert and ConvertBack methods as the parameter argument.
Even if the instance of the value converter is shared among several data bindings, the
ConverterParameter can be different to perform different conversions.

The use of the ConverterParameter property can be demonstrated with a color-selection


program. The following example shows the RgbColorViewModel , which has three
properties of type float named Red , Green , and Blue that it uses to construct a Color
value:

C#
public class RgbColorViewModel : INotifyPropertyChanged

Color color;

string name;

public event PropertyChangedEventHandler PropertyChanged;

public float Red

get { return color.Red; }

set

if (color.Red != value)

Color = new Color(value, color.Green, color.Blue);

public float Green

get { return color.Green; }

set

if (color.Green != value)

Color = new Color(color.Red, value, color.Blue);

public float Blue

get { return color.Blue; }

set

if (color.Blue != value)

Color = new Color(color.Red, color.Green, value);

public Color Color

get { return color; }

set

if (color != value)

color = value;

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Red"));

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Green"));

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Blue"));

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Color"));

Name = NamedColor.GetNearestColorName(color);

public string Name

get { return name; }

private set

if (name != value)

name = value;

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Name"));

The Red , Green , and Blue property values can range between 0 and 1. However, you
might prefer that the components be displayed as two-digit hexadecimal values. To
display these as hexadecimal values in XAML, they must be multiplied by 255, converted
to an integer, and then formatted with a specification of "X2" in the StringFormat
property. Multiplying by 255 and converting to an integer can be performed by the
value converter. To make the value converter as generalized as possible, the
multiplication factor can be specified with the ConverterParameter property, which
means that it enters the Convert and ConvertBack methods as the parameter argument:

C#

public class FloatToIntConverter : IValueConverter

public object Convert(object value, Type targetType, object parameter,


CultureInfo culture)

return (int)Math.Round((float)value * GetParameter(parameter));

public object ConvertBack(object value, Type targetType, object


parameter, CultureInfo culture)

return (int)value / GetParameter(parameter);

double GetParameter(object parameter)

if (parameter is float)

return (float)parameter;

else if (parameter is int)

return (int)parameter;

else if (parameter is string)

return float.Parse((string)parameter);

return 1;

In this example, the Convert method converts from a float to int while multiplying by
the parameter value. The ConvertBack method divides the integer value argument by
parameter and returns a float result.

The type of the parameter argument is likely to be different depending on whether the
data binding is defined in XAML or code. If the ConverterParameter property of Binding
is set in code, it's likely to be set to a numeric value:

C#

binding.ConverterParameter = 255;

The ConverterParameter property is of type Object , so the C# compiler interprets the


literal 255 as an integer, and sets the property to that value.

However, in XAML the ConverterParameter is likely to be set like this:

XAML

<Label Text="{Binding Red,

Converter={StaticResource doubleToInt},

ConverterParameter=255,

StringFormat='Red = {0:X2}'}" />

While 255 looks like a number, because ConverterParameter is of type Object , the XAML
parser treats 255 as a string. For this reason the value converter includes a separate
GetParameter method that handles cases for parameter being of type float , int , or

string .

The following XAML example instantiates FloatToIntConverter in its resource dictionary:

XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.RgbColorSelectorPage"

Title="RGB Color Selector">

<ContentPage.BindingContext>

<local:RgbColorViewModel Color="Gray" />

</ContentPage.BindingContext>

<ContentPage.Resources>

<Style TargetType="Slider">

<Setter Property="VerticalOptions" Value="Center" />

</Style>

<Style TargetType="Label">

<Setter Property="HorizontalTextAlignment" Value="Center" />

</Style>

<local:FloatToIntConverter x:Key="floatToInt" />

</ContentPage.Resources>

<StackLayout Margin="20">

<BoxView Color="{Binding Color}"

HeightRequest="100"

WidthRequest="100"

HorizontalOptions="Center" />

<StackLayout Margin="10, 0">

<Label Text="{Binding Name}" />

<Slider Value="{Binding Red}" />

<Label Text="{Binding Red,

Converter={StaticResource floatToInt},

ConverterParameter=255,

StringFormat='Red = {0:X2}'}" />


<Slider Value="{Binding Green}" />
<Label Text="{Binding Green,

Converter={StaticResource floatToInt},

ConverterParameter=255,

StringFormat='Green = {0:X2}'}" />

<Slider Value="{Binding Blue}" />

<Label>

<Label.Text>

<Binding Path="Blue"

StringFormat="Blue = {0:X2}"

Converter="{StaticResource floatToInt}">

<Binding.ConverterParameter>

<x:Single>255</x:Single>

</Binding.ConverterParameter>

</Binding>

</Label.Text>

</Label>

</StackLayout>

</StackLayout>

</ContentPage>

The values of the Red and Green properties are displayed with a Binding markup
extension. The Blue property, however, instantiates the Binding class to demonstrate
how an explicit float value can be set to ConverterParameter property:
Relative bindings
Article • 04/03/2023 • 5 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) relative bindings provide the ability to set the
binding source relative to the position of the binding target. They are created with the
RelativeSource markup extension, and set as the Source property of a binding
expression.

The RelativeSource markup extension is supported by the RelativeSourceExtension


class, which defines the following properties:

Mode , of type RelativeBindingSourceMode , describes the location of the binding


source relative to the position of the binding target.
AncestorLevel , of type int , an optional ancestor level to look for, when the Mode

property is FindAncestor . An AncestorLevel of n skips n-1 instances of the


AncestorType .

AncestorType , of type Type , the type of ancestor to look for, when the Mode
property is FindAncestor .

7 Note

The XAML parser allows the RelativeSourceExtension class to be abbreviated as


RelativeSource.

The Mode property should be set to one of the RelativeBindingSourceMode enumeration


members:

TemplatedParent indicates the element to which the template, in which the bound

element exists, is applied. For more information, see Bind to a templated parent.
Self indicates the element on which the binding is being set, allowing you to bind

one property of that element to another property on the same element. For more
information, see Bind to self.
FindAncestor indicates the ancestor in the visual tree of the bound element. This

mode should be used to bind to an ancestor control represented by the


AncestorType property. For more information, see Bind to an ancestor.

FindAncestorBindingContext indicates the BindingContext of the ancestor in the


visual tree of the bound element. This mode should be used to bind to the
BindingContext of an ancestor represented by the AncestorType property. For

more information, see Bind to an ancestor.

The Mode property is the content property of the RelativeSourceExtension class.


Therefore, for XAML markup expressions expressed with curly braces, you can eliminate
the Mode= part of the expression.

For more information about .NET MAUI markup extensions, see Consume XAML markup
extensions.

Bind to self
The Self relative binding mode is used bind a property of an element to another
property on the same element:

XAML

<BoxView Color="Red"

WidthRequest="200"

HeightRequest="{Binding Source={RelativeSource Self},


Path=WidthRequest}"

HorizontalOptions="Center" />

In this example, the BoxView sets its WidthRequest property to a fixed size, and the
HeightRequest property binds to the WidthRequest property. Therefore, both properties
are equal and so a square is drawn:

) Important

When binding a property of an element to another property on the same element,


the properties must be the same type. Alternatively, you can specify a converter on
the binding to convert the value.
A common use of this binding mode is set an object's BindingContext to a property on
itself. The following code shows an example of this:

XAML

<ContentPage ...

BindingContext="{Binding Source={RelativeSource Self},


Path=DefaultViewModel}">

<StackLayout>

<ListView ItemsSource="{Binding Employees}">

...

</ListView>

</StackLayout>

</ContentPage>

In this example, the BindingContext of the page is set to the DefaultViewModel property
of itself. This property is defined in the code-behind file for the page, and provides a
viewmodel instance. The ListView binds to the Employees property of the viewmodel.

Bind to an ancestor
The FindAncestor and FindAncestorBindingContext relative binding modes are used to
bind to parent elements, of a certain type, in the visual tree. The FindAncestor mode is
used to bind to a parent element, which derives from the Element type. The
FindAncestorBindingContext mode is used to bind to the BindingContext of a parent

element.

2 Warning

The AncestorType property must be set to a Type when using the FindAncestor
and FindAncestorBindingContext relative binding modes, otherwise a
XamlParseException is thrown.

If the Mode property isn't explicitly set, setting the AncestorType property to a type that
derives from Element will implicitly set the Mode property to FindAncestor . Similarly,
setting the AncestorType property to a type that does not derive from Element will
implicitly set the Mode property to FindAncestorBindingContext .

7 Note
Relative bindings that use the FindAncestorBindingContext mode will be reapplied
when the BindingContext of any ancestors change.

The following XAML shows an example where the Mode property will be implicitly set to
FindAncestorBindingContext :

XAML

<ContentPage ...

BindingContext="{Binding Source={RelativeSource Self},


Path=DefaultViewModel}">

<StackLayout>

<ListView ItemsSource="{Binding Employees}">

<ListView.ItemTemplate>

<DataTemplate>

<ViewCell>

<StackLayout Orientation="Horizontal">

<Label Text="{Binding Fullname}"

VerticalOptions="Center" />

<Button Text="Delete"

Command="{Binding Source={RelativeSource
AncestorType={x:Type local:PeopleViewModel}}, Path=DeleteEmployeeCommand}"

CommandParameter="{Binding}"

HorizontalOptions="End" />

</StackLayout>

</ViewCell>

</DataTemplate>

</ListView.ItemTemplate>

</ListView>

</StackLayout>

</ContentPage>

In this example, the BindingContext of the page is set to the DefaultViewModel property
of itself. This property is defined in the code-behind file for the page, and provides a
viewmodel instance. The ListView binds to the Employees property of the viewmodel.
The DataTemplate, which defines the appearance of each item in the ListView, contains a
Button. The button's Command property is bound to the DeleteEmployeeCommand in its
parent's viewmodel. Tapping a Button deletes an employee:

In addition, the optional AncestorLevel property can help disambiguate ancestor


lookup in scenarios where there is possibly more than one ancestor of that type in the
visual tree:

XAML

<Label Text="{Binding Source={RelativeSource AncestorType={x:Type Entry},


AncestorLevel=2}, Path=Text}" />

In this example, the Label.Text property binds to the Text property of the second Entry
that's encountered on the upward path, starting at the target element of the binding.

7 Note

The AncestorLevel property should be set to 1 to find the ancestor nearest to the
binding target element.

Bind to a templated parent


The TemplatedParent relative binding mode is used to bind from within a control
template to the runtime object instance to which the template is applied (known as the
templated parent). This mode is only applicable if the relative binding is within a control
template, and is similar to setting a TemplateBinding .

The following XAML shows an example of the TemplatedParent relative binding mode:

XAML

<ContentPage ...>

<ContentPage.Resources>

<ControlTemplate x:Key="CardViewControlTemplate">

<Frame BindingContext="{Binding Source={RelativeSource


TemplatedParent}}"

BackgroundColor="{Binding CardColor}"

BorderColor="{Binding BorderColor}"

...>
<Grid>

...

<Label Text="{Binding CardTitle}"

... />

<BoxView BackgroundColor="{Binding BorderColor}"

... />

<Label Text="{Binding CardDescription}"

... />

</Grid>

</Frame>

</ControlTemplate>

</ContentPage.Resources>

<StackLayout>

<controls:CardView BorderColor="DarkGray"

CardTitle="John Doe"

CardDescription="Lorem ipsum dolor sit amet,


consectetur adipiscing elit. Nulla elit dolor, convallis non interdum."

IconBackgroundColor="SlateGray"

IconImageSource="user.png"

ControlTemplate="{StaticResource
CardViewControlTemplate}" />

<controls:CardView BorderColor="DarkGray"

CardTitle="Jane Doe"

CardDescription="Phasellus eu convallis mi. In


tempus augue eu dignissim fermentum. Morbi ut lacus vitae eros lacinia."

IconBackgroundColor="SlateGray"

IconImageSource="user.png"

ControlTemplate="{StaticResource
CardViewControlTemplate}" />

<controls:CardView BorderColor="DarkGray"

CardTitle="Xamarin Monkey"

CardDescription="Aliquam sagittis, odio lacinia


fermentum dictum, mi erat scelerisque erat, quis aliquet arcu."

IconBackgroundColor="SlateGray"

IconImageSource="user.png"

ControlTemplate="{StaticResource
CardViewControlTemplate}" />

</StackLayout>

</ContentPage>

In this example, the Frame, which is the root element of the ControlTemplate, has its
BindingContext set to the runtime object instance to which the template is applied.

Therefore, the Frame and its children resolve their binding expressions against the
properties of each CardView object:
For more information about control templates, see Control templates.
Binding fallbacks
Article • 04/03/2023 • 3 minutes to read

Browse the sample

Sometimes data bindings fail, because the binding source can't be resolved, or because
the binding succeeds but returns a null value. While these scenarios can be handled
with value converters, or other additional code, data bindings can be made more robust
by defining fallback values to use if the binding process fails. In a .NET Multi-platform
App UI (.NET MAUI) app this can be accomplished by defining the FallbackValue and
TargetNullValue properties in a binding expression. Because these properties reside in

the BindingBase class, they can be used with bindings, multi-bindings, compiled
bindings, and with the Binding markup extension.

7 Note

Use of the FallbackValue and TargetNullValue properties in a binding expression


is optional.

Define a fallback value


The FallbackValue property allows a fallback value to be defined that will be used when
the binding source can't be resolved. A common scenario for setting this property is
when binding to source properties that might not exist on all objects in a bound
collection of heterogeneous types.

The following example demonstrates setting the FallbackValue property:

XAML

<Label Text="{Binding Population, FallbackValue='Population size unknown'}"

... />

The binding on the Label defines a FallbackValue value (delimited by single-quote


characters) that will be set on the target if the binding source can't be resolved.
Therefore, the value defined by the FallbackValue property will be displayed if the
Population property doesn't exist on the bound object.
Rather than defining FallbackValue property values inline, it's recommended to define
them as resources in a ResourceDictionary. The advantage of this approach is that such
values are defined once in a single location, and are more easily localizable. The
resources can then be retrieved using the StaticResource markup extension:

XAML

<Label Text="{Binding Population, FallbackValue={StaticResource


populationUnknown}}"

... />

7 Note

It's not possible to set the FallbackValue property with a binding expression.

When the FallbackValue property isn't set in a binding expression and the binding path
or part of the path isn't resolved, BindableProperty.DefaultValue is set on the target.
However, when the FallbackValue property is set and the binding path or part of the
path isn't resolved, the value of the FallbackValue value property is set on the target:

Therefore, in this example the Label displays "Population size unknown" because the
bound object lacks a Population property.

) Important

A defined value converter is not executed in a binding expression when the


FallbackValue property is set.

Define a null replacement value


The TargetNullValue property allows a replacement value to be defined that will be
used when the binding source is resolved, but the value is null . A common scenario for
setting this property is when binding to source properties that might be null in a
bound collection.
The following example demonstrates setting the TargetNullValue property:

XAML

<ListView ItemsSource="{Binding Monkeys}"

...>

<ListView.ItemTemplate>

<DataTemplate>

<ViewCell>

<Grid>

...

<Image Source="{Binding ImageUrl,


TargetNullValue='https://upload.wikimedia.org/wikipedia/commons/2/20/Point_d
_interrogation.jpg'}"

... />

...

<Label Text="{Binding Location,


TargetNullValue='Location unknown'}"

... />

</Grid>

</ViewCell>

</DataTemplate>

</ListView.ItemTemplate>

</ListView>

The bindings on the Image and Label both define TargetNullValue values (delimited by
single-quote characters) that will be applied if the binding path returns null . Therefore,
the values defined by the TargetNullValue properties will be displayed for any objects in
the collection where the ImageUrl and Location properties are not defined.

Rather than defining TargetNullValue property values inline, it's recommended to


define them as resources in a ResourceDictionary. The advantage of this approach is
that such values are defined once in a single location, and are more easily localizable.
The resources can then be retrieved using the StaticResource markup extension:

XAML

<Image Source="{Binding ImageUrl, TargetNullValue={StaticResource


fallbackImageUrl}}"

... />

<Label Text="{Binding Location, TargetNullValue={StaticResource


locationUnknown}}"

... />

7 Note

It's not possible to set the TargetNullValue property with a binding expression.
When the TargetNullValue property isn't set in a binding expression, a source value of
null will be converted if a value converter is defined, formatted if a StringFormat is
defined, and the result is then set on the target. However, when the TargetNullValue
property is set, a source value of null will be converted if a value converter is defined,
and if it's still null after the conversion, the value of the TargetNullValue property is set
on the target:

Therefore, in this example the Image and Label objects display their TargetNullValue
when their source objects are null .

) Important

String formatting is not applied in a binding expression when the TargetNullValue


property is set.
Multi-bindings
Article • 04/03/2023 • 8 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) multi-bindings provide the ability to attach a
collection of Binding objects to a single binding target property. They're created with
the MultiBinding class, which evaluates all of its Binding objects and returns a single
value through a IMultiValueConverter instance provided by your app. In addition,
MultiBinding reevaluates all of its Binding objects when any of the bound data
changes.

The MultiBinding class defines the following properties:

Bindings , of type IList<BindingBase> , which represents the collection of Binding

objects within the MultiBinding instance.


Converter , of type IMultiValueConverter , which represents the converter to use to
convert the source values to or from the target value.
ConverterParameter , of type object , which represents an optional parameter to
pass to the Converter .

The Bindings property is the content property of the MultiBinding class, and therefore
doesn't need to be explicitly set from XAML.

In addition, the MultiBinding class inherits the following properties from the
BindingBase class:

FallbackValue , of type object , which represents the value to use when the multi-

binding is unable to return a value.


Mode , of type BindingMode , which indicates the direction of the data flow of the
multi-binding.
StringFormat , of type string , which specifies how to format the multi-binding
result if it's displayed as a string.
TargetNullValue , of type object , which represents the value that's used in the

target when the value of the source is null .

A MultiBinding must use a IMultiValueConverter to produce a value for the binding


target, based on the value of the bindings in the Bindings collection. For example, a
Color might be computed from red, blue, and green values, which can be values from
the same or different binding source objects. When a value moves from the target to
the sources, the target property value is translated to a set of values that are fed back
into the bindings.

) Important

Individual bindings in the Bindings collection can have their own value converters.

The value of the Mode property determines the functionality of the MultiBinding , and is
used as the binding mode for all the bindings in the collection unless an individual
binding overrides the property. For example, if the Mode property on a MultiBinding
object is set to TwoWay , then all the bindings in the collection are considered TwoWay
unless you explicitly set a different Mode value on one of the bindings.

Define a IMultiValueConverter
The IMultiValueConverter interface enables custom logic to be applied to a
MultiBinding . To associate a converter with a MultiBinding , create a class that

implements the IMultiValueConverter interface, and then implement the Convert and
ConvertBack methods:

C#

public class AllTrueMultiConverter : IMultiValueConverter

public object Convert(object[] values, Type targetType, object


parameter, CultureInfo culture)

if (values == null || !targetType.IsAssignableFrom(typeof(bool)))

return false;

// Alternatively, return BindableProperty.UnsetValue to use the


binding FallbackValue

foreach (var value in values)

if (!(value is bool b))

return false;

// Alternatively, return BindableProperty.UnsetValue to use


the binding FallbackValue

else if (!b)

return false;

return true;

public object[] ConvertBack(object value, Type[] targetTypes, object


parameter, CultureInfo culture)

if (!(value is bool b) || targetTypes.Any(t =>


!t.IsAssignableFrom(typeof(bool))))

// Return null to indicate conversion back is not possible

return null;

if (b)

return targetTypes.Select(t => (object)true).ToArray();

else

// Can't convert back from false because of ambiguity

return null;

The Convert method converts source values to a value for the binding target. .NET
MAUI calls this method when it propagates values from source bindings to the binding
target. This method accepts four arguments:

values , of type object[] , is an array of values that the source bindings in the

MultiBinding produces.

targetType , of type Type , is the type of the binding target property.


parameter , of type object , is the converter parameter to use.

culture , of type CultureInfo , is the culture to use in the converter.

The Convert method returns an object that represents a converted value. This method
should return:

BindableProperty.UnsetValue to indicate that the converter did not produce a


value, and that the binding will use the FallbackValue .
Binding.DoNothing to instruct .NET MAUI not to perform any action. For example,
to instruct .NET MAUI not to transfer a value to the binding target, or not to use
the FallbackValue .
null to indicate that the converter can't perform the conversion, and that the
binding will use the TargetNullValue .
) Important

A MultiBinding that receives BindableProperty.UnsetValue from a Convert method


must define its FallbackValue property. Similarly, a MultiBinding that receives
null from a Convert method must define its TargetNullValue property.

The ConvertBack method converts a binding target to the source binding values. This
method accepts four arguments:

value , of type object , is the value that the binding target produces.

targetTypes , of type Type[] , is the array of types to convert to. The array length
indicates the number and types of values that are suggested for the method to
return.
parameter , of type object , is the converter parameter to use.
culture , of type CultureInfo , is the culture to use in the converter.

The ConvertBack method returns an array of values of type object[] that have been
converted from the target values back to the source values. This method should return:

BindableProperty.UnsetValue at position i to indicate that the converter is unable

to provide a value for the source binding at index i , and that no value is to be set
on it.
Binding.DoNothing at position i to indicate that no value is to be set on the
source binding at index i .
null to indicate that the converter can't perform the conversion or that it doesn't

support conversion in this direction.

Consume a IMultiValueConverter
A IMultiValueConverter is typically consumed by instantiating it in a resource dictionary,
and then referencing it using the StaticResource markup extension to set the
MultiBinding.Converter property:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.MultiBindingConverterPage"

Title="MultiBinding Converter demo">

<ContentPage.Resources>

<local:AllTrueMultiConverter x:Key="AllTrueConverter" />

<local:InverterConverter x:Key="InverterConverter" />

</ContentPage.Resources>

<CheckBox>

<CheckBox.IsChecked>

<MultiBinding Converter="{StaticResource AllTrueConverter}">

<Binding Path="Employee.IsOver16" />

<Binding Path="Employee.HasPassedTest" />

<Binding Path="Employee.IsSuspended"

Converter="{StaticResource InverterConverter}" />

</MultiBinding>

</CheckBox.IsChecked>

</CheckBox>

</ContentPage>

In this example, the MultiBinding object uses the AllTrueMultiConverter instance to set
the CheckBox.IsChecked property to true , provided that the three Binding objects
evaluate to true . Otherwise, the CheckBox.IsChecked property is set to false .

By default, the CheckBox.IsChecked property uses a TwoWay binding. Therefore, the


ConvertBack method of the AllTrueMultiConverter instance is executed when the

CheckBox is unchecked by the user, which sets the source binding values to the value of
the CheckBox.IsChecked property.

The equivalent C# code is shown here:

C#

public class MultiBindingConverterCodePage : ContentPage

public MultiBindingConverterCodePage()

BindingContext = new GroupViewModel();

CheckBox checkBox = new CheckBox();

checkBox.SetBinding(CheckBox.IsCheckedProperty, new MultiBinding

Bindings = new Collection<BindingBase>

new Binding("Employee1.IsOver16"),

new Binding("Employee1.HasPassedTest"),

new Binding("Employee1.IsSuspended", converter: new


InverterConverter())

},

Converter = new AllTrueMultiConverter()

});

Title = "MultiBinding converter demo";

Content = checkBox;

Format strings
A MultiBinding can format any multi-binding result that's displayed as a string, with the
StringFormat property. This property can be set to a standard .NET formatting string,

with placeholders, that specifies how to format the multi-binding result:

XAML

<Label>

<Label.Text>

<MultiBinding StringFormat="{}{0} {1} {2}">

<Binding Path="Employee1.Forename" />

<Binding Path="Employee1.MiddleName" />

<Binding Path="Employee1.Surname" />

</MultiBinding>

</Label.Text>

</Label>

7 Note

If the format string starts with the { character, the XAML parser will confuse it for a
markup extension. To avoid this ambiguity, prefix the format string with an empty
set of curly braces.

In this example, the StringFormat property combines the three bound values into a
single string that's displayed by the Label.

The equivalent C# code is shown here:

C#

Label label = new Label();

label.SetBinding(Label.TextProperty, new MultiBinding

Bindings = new Collection<BindingBase>

new Binding("Employee1.Forename"),

new Binding("Employee1.MiddleName"),

new Binding("Employee1.Surname")

},

StringFormat = "{0} {1} {2}"

});

) Important

The number of parameters in a composite string format can't exceed the number of
child Binding objects in the MultiBinding .

When setting the Converter and StringFormat properties, the converter is applied to
the data value first, and then the StringFormat is applied.

For more information about string formatting in .NET MAUI, see String formatting.

Provide fallback values


Data bindings can be made more robust by defining fallback values to use if the binding
process fails. Set fallback values by defining the FallbackValue and TargetNullValue
properties on a MultiBinding object.

A MultiBinding will use its FallbackValue when the Convert method of an


IMultiValueConverter instance returns BindableProperty.UnsetValue , which indicates

that the converter did not produce a value. A MultiBinding will use its TargetNullValue
when the Convert method of an IMultiValueConverter instance returns null , which
indicates that the converter can't perform the conversion.

For more information about binding fallbacks, see Binding fallbacks.

Nest MultiBinding objects


MultiBinding objects can be nested so that multiple MultiBinding objects are evaluated
to return a value through an IMultiValueConverter instance:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.NestedMultiBindingPage"

Title="Nested MultiBinding demo">

<ContentPage.Resources>

<local:AllTrueMultiConverter x:Key="AllTrueConverter" />

<local:AnyTrueMultiConverter x:Key="AnyTrueConverter" />

<local:InverterConverter x:Key="InverterConverter" />

</ContentPage.Resources>

<CheckBox>

<CheckBox.IsChecked>

<MultiBinding Converter="{StaticResource AnyTrueConverter}">

<MultiBinding Converter="{StaticResource AllTrueConverter}">

<Binding Path="Employee.IsOver16" />

<Binding Path="Employee.HasPassedTest" />

<Binding Path="Employee.IsSuspended" Converter="


{StaticResource InverterConverter}" />

</MultiBinding>

<Binding Path="Employee.IsMonarch" />

</MultiBinding>

</CheckBox.IsChecked>

</CheckBox>

</ContentPage>

In this example, the MultiBinding object uses its AnyTrueMultiConverter instance to set
the CheckBox.IsChecked property to true , provided that all of the Binding objects in the
inner MultiBinding object evaluate to true , or if the Binding object in the outer
MultiBinding object evaluates to true . Otherwise, the CheckBox.IsChecked property is
set to false .

Use a RelativeSource binding in a MultiBinding


MultiBinding objects support relative bindings, which let you set the binding source

relative to the position of the binding target:

XAML

<ContentPage ...

xmlns:local="clr-namespace:DataBindingDemos">

<ContentPage.Resources>

<local:AllTrueMultiConverter x:Key="AllTrueConverter" />

<ControlTemplate x:Key="CardViewExpanderControlTemplate">

<local:Expander BindingContext="{Binding Source={RelativeSource


TemplatedParent}}"

IsExpanded="{Binding IsExpanded, Source=


{RelativeSource TemplatedParent}}"

BackgroundColor="{Binding CardColor}"

RowDefinitions="Auto,Auto"

Padding="8">

<local:Expander.IsVisible>

<MultiBinding Converter="{StaticResource
AllTrueConverter}">

<Binding Path="IsExpanded" />

<Binding Path="IsEnabled" />

</MultiBinding>

</local:Expander.IsVisible>

<Grid>

<!-- XAML that defines Expander header goes here -->

</Grid>

<Grid>

<!-- XAML that defines Expander content goes here -->

</Grid>

</local:Expander>

</ControlTemplate>

</ContentPage.Resources>

<StackLayout>

<controls:CardViewExpander BorderColor="DarkGray"

CardTitle="John Doe"

CardDescription="Lorem ipsum dolor sit


amet, consectetur adipiscing elit. Nulla elit dolor, convallis non
interdum."

IconBackgroundColor="SlateGray"

IconImageSource="user.png"

ControlTemplate="{StaticResource
CardViewExpanderControlTemplate}"
IsEnabled="True"

IsExpanded="True" />

</StackLayout>

</ContentPage>

In this example, the TemplatedParent relative binding mode is used to bind from within
a control template to the runtime object instance to which the template is applied. The
Expander , which is the root element of the ControlTemplate, has its BindingContext set
to the runtime object instance to which the template is applied. Therefore, the Expander
and its children resolve their binding expressions, and Binding objects, against the
properties of the CardViewExpander object. The MultiBinding uses the
AllTrueMultiConverter instance to set the Expander.IsVisible property to true

provided that the two Binding objects evaluate to true . Otherwise, the
Expander.IsVisible property is set to false .

For more information about relative bindings, see Relative bindings. For more
information about control templates, see Control templates.
Commanding
Article • 02/09/2023 • 13 minutes to read

Browse the sample

In a .NET Multi-platform App UI (.NET MAUI) app that uses the Model-View-ViewModel
(MVVM) pattern, data bindings are defined between properties in the viewmodel, which
is typically a class that derives from INotifyPropertyChanged , and properties in the view,
which is typically the XAML file. Sometimes an app has needs that go beyond these
property bindings by requiring the user to initiate commands that affect something in
the viewmodel. These commands are generally signaled by button clicks or finger taps,
and traditionally they are processed in the code-behind file in a handler for the Clicked
event of the Button or the Tapped event of a TapGestureRecognizer.

The commanding interface provides an alternative approach to implementing


commands that is much better suited to the MVVM architecture. The viewmodel can
contain commands, which are methods that are executed in reaction to a specific
activity in the view such as a Button click. Data bindings are defined between these
commands and the Button.

To allow a data binding between a Button and a viewmodel, the Button defines two
properties:

Command of type System.Windows.Input.ICommand

CommandParameter of type Object

To use the command interface, you define a data binding that targets the Command
property of the Button where the source is a property in the viewmodel of type
ICommand . The viewmodel contains code associated with that ICommand property that is
executed when the button is clicked. You can set the CommandParameter property to
arbitrary data to distinguish between multiple buttons if they are all bound to the same
ICommand property in the viewmodel.

Many other views also define Command and CommandParameter properties. All these
commands can be handled within a viewmodel using an approach that doesn't depend
on the user-interface object in the view.

ICommands
The ICommand interface is defined in the System.Windows.Input namespace, and
consists of two methods and one event:

C#

public interface ICommand

public void Execute (Object parameter);

public bool CanExecute (Object parameter);


public event EventHandler CanExecuteChanged;

To use the command interface, your viewmodel should contain properties of type
ICommand :

C#

public ICommand MyCommand { private set; get; }

The viewmodel must also reference a class that implements the ICommand interface. In
the view, the Command property of a Button is bound to that property:

XAML

<Button Text="Execute command"

Command="{Binding MyCommand}" />

When the user presses the Button, the Button calls the Execute method in the ICommand
object bound to its Command property.

When the binding is first defined on the Command property of the Button, and when the
data binding changes in some way, the Button calls the CanExecute method in the
ICommand object. If CanExecute returns false , then the Button disables itself. This
indicates that the particular command is currently unavailable or invalid.

The Button also attaches a handler on the CanExecuteChanged event of ICommand . The
event is raised from within the viewmodel. When that event is raised, the Button calls
CanExecute again. The Button enables itself if CanExecute returns true and disables

itself if CanExecute returns false .

2 Warning
Do not use the IsEnabled property of Button if you're using the command
interface.

When your viewmodel defines a property of type ICommand , the viewmodel must also
contain or reference a class that implements the ICommand interface. This class must
contain or reference the Execute and CanExecute methods, and fire the
CanExecuteChanged event whenever the CanExecute method might return a different

value. You can use the Command or Command<T> class included in .NET MAUI to implement
the ICommand interface. These classes allow you to specify the bodies of the Execute and
CanExecute methods in class constructors.

 Tip

Use Command<T> when you use the CommandParameter property to distinguish


between multiple views bound to the same ICommand property, and the Command
class when that isn't a requirement.

Basic commanding
The following examples demonstrate basic commands implemented in a viewmodel.

The PersonViewModel class defines three properties named Name , Age , and Skills that
define a person:

C#

public class PersonViewModel : INotifyPropertyChanged

string name;

double age;

string skills;

public event PropertyChangedEventHandler PropertyChanged;

public string Name

set { SetProperty(ref name, value); }

get { return name; }

public double Age

set { SetProperty(ref age, value); }

get { return age; }

public string Skills

set { SetProperty(ref skills, value); }

get { return skills; }

public override string ToString()

return Name + ", age " + Age;

bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string


propertyName = null)

if (Object.Equals(storage, value))

return false;

storage = value;

OnPropertyChanged(propertyName);

return true;

protected void OnPropertyChanged([CallerMemberName] string propertyName


= null)

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));

The PersonCollectionViewModel class shown below creates new objects of type


PersonViewModel and allows the user to fill in the data. For that purpose, the class

defines IsEditing , of type bool , and PersonEdit , of type PersonViewModel , properties.


In addition, the class defines three properties of type ICommand and a property named
Persons of type IList<PersonViewModel> :

C#

public class PersonCollectionViewModel : INotifyPropertyChanged

PersonViewModel personEdit;

bool isEditing;

public event PropertyChangedEventHandler PropertyChanged;

···

public bool IsEditing

private set { SetProperty(ref isEditing, value); }

get { return isEditing; }

public PersonViewModel PersonEdit

set { SetProperty(ref personEdit, value); }

get { return personEdit; }

public ICommand NewCommand { private set; get; }

public ICommand SubmitCommand { private set; get; }

public ICommand CancelCommand { private set; get; }

public IList<PersonViewModel> Persons { get; } = new


ObservableCollection<PersonViewModel>();

bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string


propertyName = null)

if (Object.Equals(storage, value))

return false;

storage = value;

OnPropertyChanged(propertyName);

return true;

protected void OnPropertyChanged([CallerMemberName] string propertyName


= null)

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));

In this example, changes to the three ICommand properties and the Persons property do
not result in PropertyChanged events being raised. These properties are all set when the
class is first created and do not change.

The following example shows the XAML that consumes the PersonCollectionViewModel :

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.PersonEntryPage"

Title="Person Entry">

<ContentPage.BindingContext>

<local:PersonCollectionViewModel />

</ContentPage.BindingContext>

<Grid Margin="10">

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="*" />

</Grid.RowDefinitions>

<!-- New Button -->

<Button Text="New"

Grid.Row="0"

Command="{Binding NewCommand}"

HorizontalOptions="Start" />

<!-- Entry Form -->

<Grid Grid.Row="1"

IsEnabled="{Binding IsEditing}">

<Grid BindingContext="{Binding PersonEdit}">

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="*" />

</Grid.ColumnDefinitions>

<Label Text="Name: " Grid.Row="0" Grid.Column="0" />

<Entry Text="{Binding Name}"

Grid.Row="0" Grid.Column="1" />

<Label Text="Age: " Grid.Row="1" Grid.Column="0" />

<StackLayout Orientation="Horizontal"

Grid.Row="1" Grid.Column="1">

<Stepper Value="{Binding Age}"

Maximum="100" />

<Label Text="{Binding Age, StringFormat='{0} years


old'}"

VerticalOptions="Center" />

</StackLayout>

<Label Text="Skills: " Grid.Row="2" Grid.Column="0" />

<Entry Text="{Binding Skills}"

Grid.Row="2" Grid.Column="1" />

</Grid>

</Grid>

<!-- Submit and Cancel Buttons -->

<Grid Grid.Row="2">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="*" />

<ColumnDefinition Width="*" />

</Grid.ColumnDefinitions>

<Button Text="Submit"

Grid.Column="0"

Command="{Binding SubmitCommand}"

VerticalOptions="Center" />

<Button Text="Cancel"

Grid.Column="1"

Command="{Binding CancelCommand}"

VerticalOptions="Center" />

</Grid>

<!-- List of Persons -->

<ListView Grid.Row="3"

ItemsSource="{Binding Persons}" />

</Grid>

</ContentPage>

In this example, the page's BindingContext property is set to the


PersonCollectionViewModel . The Grid contains a Button with the text New with its

Command property bound to the NewCommand property in the viewmodel, an entry form

with properties bound to the IsEditing property, as well as properties of


PersonViewModel , and two more buttons bound to the SubmitCommand and

CancelCommand properties of the viewmodel. The ListView displays the collection of


persons already entered:

The following screenshot shows the Submit button enabled after an age has been set:
When the user first presses the New button, this enables the entry form but disables the
New button. The user then enters a name, age, and skills. At any time during the editing,
the user can press the Cancel button to start over. Only when a name and a valid age
have been entered is the Submit button enabled. Pressing this Submit button transfers
the person to the collection displayed by the ListView. After either the Cancel or Submit
button is pressed, the entry form is cleared and the New button is enabled again.

All the logic for the New, Submit, and Cancel buttons is handled in
PersonCollectionViewModel through definitions of the NewCommand , SubmitCommand , and
CancelCommand properties. The constructor of the PersonCollectionViewModel sets these

three properties to objects of type Command .

A constructor of the Command class allows you to pass arguments of type Action and
Func<bool> corresponding to the Execute and CanExecute methods. This action and

function can be defined as lambda functions in the Command constructor:

C#
public class PersonCollectionViewModel : INotifyPropertyChanged

···

public PersonCollectionViewModel()

NewCommand = new Command(

execute: () =>

PersonEdit = new PersonViewModel();

PersonEdit.PropertyChanged += OnPersonEditPropertyChanged;

IsEditing = true;

RefreshCanExecutes();

},

canExecute: () =>

return !IsEditing;

});

···

void OnPersonEditPropertyChanged(object sender, PropertyChangedEventArgs


args)

(SubmitCommand as Command).ChangeCanExecute();

void RefreshCanExecutes()

(NewCommand as Command).ChangeCanExecute();

(SubmitCommand as Command).ChangeCanExecute();

(CancelCommand as Command).ChangeCanExecute();

···

When the user clicks the New button, the execute function passed to the Command
constructor is executed. This creates a new PersonViewModel object, sets a handler on
that object's PropertyChanged event, sets IsEditing to true , and calls the
RefreshCanExecutes method defined after the constructor.

Besides implementing the ICommand interface, the Command class also defines a method
named ChangeCanExecute . A viewmodel should call ChangeCanExecute for an ICommand
property whenever anything happens that might change the return value of the
CanExecute method. A call to ChangeCanExecute causes the Command class to fire the

CanExecuteChanged method. The Button has attached a handler for that event and
responds by calling CanExecute again, and then enabling itself based on the return value
of that method.
When the execute method of NewCommand calls RefreshCanExecutes , the NewCommand
property gets a call to ChangeCanExecute , and the Button calls the canExecute method,
which now returns false because the IsEditing property is now true .

The PropertyChanged handler for the new PersonViewModel object calls the
ChangeCanExecute method of SubmitCommand :

C#

public class PersonCollectionViewModel : INotifyPropertyChanged

···

public PersonCollectionViewModel()

···

SubmitCommand = new Command(

execute: () =>

Persons.Add(PersonEdit);

PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;

PersonEdit = null;

IsEditing = false;

RefreshCanExecutes();

},

canExecute: () =>

return PersonEdit != null &&

PersonEdit.Name != null &&

PersonEdit.Name.Length > 1 &&

PersonEdit.Age > 0;

});

···

···

The canExecute function for SubmitCommand is called every time there's a property
changed in the PersonViewModel object being edited. It returns true only when the
Name property is at least one character long, and Age is greater than 0. At that time, the
Submit button becomes enabled.

The execute function for Submit removes the property-changed handler from the
PersonViewModel , adds the object to the Persons collection, and returns everything to its
initial state.

The execute function for the Cancel button does everything that the Submit button
does except add the object to the collection:
C#

public class PersonCollectionViewModel : INotifyPropertyChanged

···

public PersonCollectionViewModel()

···

CancelCommand = new Command(

execute: () =>

PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;

PersonEdit = null;

IsEditing = false;

RefreshCanExecutes();

},

canExecute: () =>

return IsEditing;

});

···

The canExecute method returns true at any time a PersonViewModel is being edited.

7 Note

It isn't necessary to define the execute and canExecute methods as lambda


functions. You can write them as private methods in the viewmodel and reference
them in the Command constructors. However, this approach can result in a lot of
methods that are referenced only once in the viewmodel.

Using Command parameters


It's' sometimes convenient for one or more buttons, or other user-interface objects, to
share the same ICommand property in the viewmodel. In this case, you can use the
CommandParameter property to distinguish between the buttons.

You can continue to use the Command class for these shared ICommand properties. The
class defines an alternative constructor that accepts execute and canExecute methods
with parameters of type Object . This is how the CommandParameter is passed to these
methods. However, when specifying a CommandParameter , it's easiest to use the generic
Command<T> class to specify the type of the object set to CommandParameter . The execute

and canExecute methods that you specify have parameters of that type.

The following example demonstrates a keyboard for entering decimal numbers:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.DecimalKeypadPage"

Title="Decimal Keyboard">

<ContentPage.BindingContext>

<local:DecimalKeypadViewModel />

</ContentPage.BindingContext>

<ContentPage.Resources>

<Style TargetType="Button">

<Setter Property="FontSize" Value="32" />

<Setter Property="BorderWidth" Value="1" />

<Setter Property="BorderColor" Value="Black" />

</Style>

</ContentPage.Resources>

<Grid WidthRequest="240"

HeightRequest="480"

ColumnDefinitions="80, 80, 80"

RowDefinitions="Auto, Auto, Auto, Auto, Auto, Auto"

ColumnSpacing="2"

RowSpacing="2"

HorizontalOptions="Center"

VerticalOptions="Center">

<Label Text="{Binding Entry}"

Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"

Margin="0,0,10,0"

FontSize="32"

LineBreakMode="HeadTruncation"

VerticalTextAlignment="Center"

HorizontalTextAlignment="End" />

<Button Text="CLEAR"

Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"

Command="{Binding ClearCommand}" />

<Button Text="&#x21E6;"

Grid.Row="1" Grid.Column="2"

Command="{Binding BackspaceCommand}" />

<Button Text="7"

Grid.Row="2" Grid.Column="0"

Command="{Binding DigitCommand}"

CommandParameter="7" />

<Button Text="8"

Grid.Row="2" Grid.Column="1"

Command="{Binding DigitCommand}"

CommandParameter="8" />

<Button Text="9"

Grid.Row="2" Grid.Column="2"

Command="{Binding DigitCommand}"

CommandParameter="9" />

<Button Text="4"

Grid.Row="3" Grid.Column="0"

Command="{Binding DigitCommand}"

CommandParameter="4" />

<Button Text="5"

Grid.Row="3" Grid.Column="1"

Command="{Binding DigitCommand}"

CommandParameter="5" />

<Button Text="6"

Grid.Row="3" Grid.Column="2"

Command="{Binding DigitCommand}"

CommandParameter="6" />

<Button Text="1"

Grid.Row="4" Grid.Column="0"

Command="{Binding DigitCommand}"

CommandParameter="1" />

<Button Text="2"

Grid.Row="4" Grid.Column="1"

Command="{Binding DigitCommand}"

CommandParameter="2" />

<Button Text="3"

Grid.Row="4" Grid.Column="2"

Command="{Binding DigitCommand}"

CommandParameter="3" />

<Button Text="0"

Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2"

Command="{Binding DigitCommand}"

CommandParameter="0" />

<Button Text="&#x00B7;"

Grid.Row="5" Grid.Column="2"

Command="{Binding DigitCommand}"

CommandParameter="." />

</Grid>

</ContentPage>

In this example, the page's BindingContext is a DecimalKeypadViewModel . The Entry


property of this viewmodel is bound to the Text property of a Label. All the Button
objects are bound to commands in the viewmodel: ClearCommand , BackspaceCommand ,
and DigitCommand . The 11 buttons for the 10 digits and the decimal point share a
binding to DigitCommand . The CommandParameter distinguishes between these buttons.
The value set to CommandParameter is generally the same as the text displayed by the
button except for the decimal point, which for purposes of clarity is displayed with a
middle dot character:
The DecimalKeypadViewModel defines an Entry property of type string and three
properties of type ICommand :

C#

public class DecimalKeypadViewModel : INotifyPropertyChanged

string entry = "0";

public event PropertyChangedEventHandler PropertyChanged;

···

public string Entry

private set

if (entry != value)

entry = value;

PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Entry"));

get

return entry;

public ICommand ClearCommand { private set; get; }

public ICommand BackspaceCommand { private set; get; }

public ICommand DigitCommand { private set; get; }

The button corresponding to the ClearCommand is always enabled and sets the entry
back to "0":

C#

public class DecimalKeypadViewModel : INotifyPropertyChanged

···

public DecimalKeypadViewModel()

ClearCommand = new Command(

execute: () =>

Entry = "0";

RefreshCanExecutes();

});

···

void RefreshCanExecutes()

((Command)BackspaceCommand).ChangeCanExecute();

((Command)DigitCommand).ChangeCanExecute();

···

Because the button is always enabled, it is not necessary to specify a canExecute


argument in the Command constructor.
The Backspace button is enabled only when the length of the entry is greater than 1, or
if Entry is not equal to the string "0":

C#

public class DecimalKeypadViewModel : INotifyPropertyChanged

···

public DecimalKeypadViewModel()

···

BackspaceCommand = new Command(

execute: () =>

Entry = Entry.Substring(0, Entry.Length - 1);

if (Entry == "")

Entry = "0";

RefreshCanExecutes();

},

canExecute: () =>

return Entry.Length > 1 || Entry != "0";


});

···

···

The logic for the execute function for the Backspace button ensures that the Entry is at
least a string of "0".

The DigitCommand property is bound to 11 buttons, each of which identifies itself with
the CommandParameter property. The DigitCommand is set to an instance of the
Command<T> class. When using the commanding interface with XAML, the

CommandParameter properties are usually strings, which is type of the generic argument.
The execute and canExecute functions then have arguments of type string :

C#

public class DecimalKeypadViewModel : INotifyPropertyChanged

···

public DecimalKeypadViewModel()

···

DigitCommand = new Command<string>(

execute: (string arg) =>

Entry += arg;

if (Entry.StartsWith("0") && !Entry.StartsWith("0."))

Entry = Entry.Substring(1);

RefreshCanExecutes();

},

canExecute: (string arg) =>

return !(arg == "." && Entry.Contains("."));

});

···

The execute method appends the string argument to the Entry property. However, if the
result begins with a zero (but not a zero and a decimal point) then that initial zero must
be removed using the Substring function. The canExecute method returns false only if
the argument is the decimal point (indicating that the decimal point is being pressed)
and Entry already contains a decimal point. All the execute methods call
RefreshCanExecutes , which then calls ChangeCanExecute for both DigitCommand and
ClearCommand . This ensures that the decimal point and backspace buttons are enabled or

disabled based on the current sequence of entered digits.


Compiled bindings
Article • 04/03/2023 • 7 minutes to read

Browse the sample

.NET Multi-platform App UI (.NET MAUI) data bindings have two main issues:

1. There's no compile-time validation of binding expressions. Instead, bindings are


resolved at runtime. Therefore, any invalid bindings aren't detected until runtime
when the application doesn't behave as expected or error messages appear.
2. They aren't cost efficient. Bindings are resolved at runtime using general-purpose
object inspection (reflection), and the overhead of doing this varies from platform
to platform.

Compiled bindings improve data binding performance in .NET MAUI applications by


resolving binding expressions at compile-time rather than runtime. In addition, this
compile-time validation of binding expressions enables a better developer
troubleshooting experience because invalid bindings are reported as build errors.

The process for using compiled bindings is to:

1. Ensure that XAML compilation is enabled. For more information about XAML
compilation, see XAML Compilation.
2. Set an x:DataType attribute on a VisualElement to the type of the object that the
VisualElement and its children will bind to.

7 Note

It's recommended to set the x:DataType attribute at the same level in the view
hierarchy as the BindingContext is set. However, this attribute can be re-defined at
any location in a view hierarchy.

To use compiled bindings, the x:DataType attribute must be set to a string literal, or a
type using the x:Type markup extension. At XAML compile time, any invalid binding
expressions will be reported as build errors. However, the XAML compiler will only
report a build error for the first invalid binding expression that it encounters. Any valid
binding expressions that are defined on the VisualElement or its children will be
compiled, regardless of whether the BindingContext is set in XAML or code. Compiling a
binding expression generates compiled code that will get a value from a property on the
source, and set it on the property on the target that's specified in the markup. In
addition, depending on the binding expression, the generated code may observe
changes in the value of the source property and refresh the target property, and may
push changes from the target back to the source.

) Important

Compiled bindings are disabled for any binding expressions that define the Source
property. This is because the Source property is always set using the x:Reference
markup extension, which can't be resolved at compile time.

In addition, compiled bindings are currently unsupported on multi-bindings.

Use compiled bindings


The following example demonstrates using compiled bindings between .NET MAUI
views and viewmodel properties:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.CompiledColorSelectorPage"

x:DataType="local:HslColorViewModel"

Title="Compiled Color Selector">

<ContentPage.BindingContext>

<local:HslColorViewModel Color="Sienna" />

</ContentPage.BindingContext>

...

<StackLayout>

<BoxView Color="{Binding Color}"

... />
<StackLayout Margin="10, 0">

<Label Text="{Binding Name}" />

<Slider Value="{Binding Hue}" />

<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />

<Slider Value="{Binding Saturation}" />

<Label Text="{Binding Saturation, StringFormat='Saturation =


{0:F2}'}" />

<Slider Value="{Binding Luminosity}" />

<Label Text="{Binding Luminosity, StringFormat='Luminosity =


{0:F2}'}" />

</StackLayout>

</StackLayout>

</ContentPage>

The ContentPage instantiates the HslColorViewModel and initializes the Color property
within property element tags for the BindingContext property. The ContentPage also
defines the x:DataType attribute as the viewmodel type, indicating that any binding
expressions in the ContentPage view hierarchy will be compiled. This can be verified by
changing any of the binding expressions to bind to a non-existent viewmodel property,
which will result in a build error. While this example sets the x:DataType attribute to a
string literal, it can also be set to a type with the x:Type markup extension. For more
information about the x:Type markup extension, see x:Type Markup Extension.

) Important

The x:DataType attribute can be re-defined at any point in a view hierarchy.

The BoxView, Label elements, and Slider views inherit the binding context from the
ContentPage. These views are all binding targets that reference source properties in the
viewmodel. For the BoxView.Color property, and the Label.Text property, the data
bindings are OneWay – the properties in the view are set from the properties in the
viewmodel. However, the Slider.Value property uses a TwoWay binding. This allows
each Slider to be set from the viewmodel, and also for the viewmodel to be set from
each Slider.

When the example is first run, the BoxView, Label elements, and Slider elements are all
set from the viewmodel based on the initial Color property set when the viewmodel
was instantiated. As the sliders are manipulated, the BoxView and Label elements are
updated accordingly:
For more information about this color selector, see ViewModels and property-change
notifications.

Use compiled bindings in a DataTemplate


Bindings in a DataTemplate are interpreted in the context of the object being templated.
Therefore, when using compiled bindings in a DataTemplate, the DataTemplate needs to
declare the type of its data object using the x:DataType attribute.

The following example demonstrates using compiled bindings in a DataTemplate:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:DataBindingDemos"

x:Class="DataBindingDemos.CompiledColorListPage"

Title="Compiled Color List">

<Grid>

...

<ListView x:Name="colorListView"

ItemsSource="{x:Static local:NamedColor.All}"

... >

<ListView.ItemTemplate>

<DataTemplate x:DataType="local:NamedColor">

<ViewCell>

<StackLayout Orientation="Horizontal">

<BoxView Color="{Binding Color}"

... />

<Label Text="{Binding FriendlyName}"

... />

</StackLayout>

</ViewCell>

</DataTemplate>

</ListView.ItemTemplate>

</ListView>

<!-- The BoxView doesn't use compiled bindings -->

<BoxView Color="{Binding Source={x:Reference colorListView},


Path=SelectedItem.Color}"

... />
</Grid>

</ContentPage>

The ListView.ItemsSource property is set to the static NamedColor.All property. The


NamedColor class uses .NET reflection to enumerate all the static public fields in the

Colors class, and to store them with their names in a collection that is accessible from
the static All property. Therefore, the ListView is filled with all of the NamedColor
instances. For each item in the ListView, the binding context for the item is set to a
NamedColor object. The BoxView and Label elements in the ViewCell are bound to
NamedColor properties.

The DataTemplate defines the x:DataType attribute to be the NamedColor type,


indicating that any binding expressions in the DataTemplate view hierarchy will be
compiled. This can be verified by changing any of the binding expressions to bind to a
non-existent NamedColor property, which will result in a build error. While this example
sets the x:DataType attribute to a string literal, it can also be set to a type with the
x:Type markup extension. For more information about the x:Type markup extension,
see x:Type Markup Extension.

When the example is first run, the ListView is populated with NamedColor instances.
When an item in the ListView is selected, the BoxView.Color property is set to the color
of the selected item in the ListView:
Selecting other items in the ListView updates the color of the BoxView.

Combine compiled bindings with classic


bindings
Binding expressions are only compiled for the view hierarchy that the x:DataType
attribute is defined on. Conversely, any views in a hierarchy on which the x:DataType
attribute is not defined will use classic bindings. It's therefore possible to combine
compiled bindings and classic bindings on a page. For example, in the previous section
the views within the DataTemplate use compiled bindings, while the BoxView that's set
to the color selected in the ListView does not.

Careful structuring of x:DataType attributes can therefore lead to a page using compiled
and classic bindings. Alternatively, the x:DataType attribute can be re-defined at any
point in a view hierarchy to null using the x:Null markup extension. Doing this
indicates that any binding expressions within the view hierarchy will use classic bindings.
The following example demonstrates this approach:

XAML

<StackLayout x:DataType="local:HslColorViewModel">

<StackLayout.BindingContext>

<local:HslColorViewModel Color="Sienna" />

</StackLayout.BindingContext>

<BoxView Color="{Binding Color}"

VerticalOptions="FillAndExpand" />

<StackLayout x:DataType="{x:Null}"

Margin="10, 0">

<Label Text="{Binding Name}" />

<Slider Value="{Binding Hue}" />

<Label Text="{Binding Hue, StringFormat='Hue = {0:F2}'}" />

<Slider Value="{Binding Saturation}" />

<Label Text="{Binding Saturation, StringFormat='Saturation =


{0:F2}'}" />

<Slider Value="{Binding Luminosity}" />

<Label Text="{Binding Luminosity, StringFormat='Luminosity =


{0:F2}'}" />

</StackLayout>

</StackLayout>

The root StackLayout sets the x:DataType attribute to be the HslColorViewModel type,
indicating that any binding expression in the root StackLayout view hierarchy will be
compiled. However, the inner StackLayout redefines the x:DataType attribute to null
with the x:Null markup expression. Therefore, the binding expressions within the inner
StackLayout use classic bindings. Only the BoxView, within the root StackLayout view
hierarchy, uses compiled bindings.

For more information about the x:Null markup expression, see x:Null Markup
Extension.

Performance
Compiled bindings improve data binding performance, with the performance benefit
varying:

A compiled binding that uses property-change notification (i.e. a OneWay ,


OneWayToSource , or TwoWay binding) is resolved approximately 8 times quicker than

a classic binding.
A compiled binding that doesn't use property-change notification (i.e. a OneTime
binding) is resolved approximately 20 times quicker than a classic binding.
Setting the BindingContext on a compiled binding that uses property change
notification (i.e. a OneWay , OneWayToSource , or TwoWay binding) is approximately 5
times quicker than setting the BindingContext on a classic binding.
Setting the BindingContext on a compiled binding that doesn't use property
change notification (i.e. a OneTime binding) is approximately 7 times quicker than
setting the BindingContext on a classic binding.

These performance differences can be magnified on mobile devices, dependent upon


the platform being used, the version of the operating system being used, and the device
on which the application is running.
Recognize a drag and drop gesture
Article • 02/09/2023 • 10 minutes to read

A .NET Multi-platform App UI (.NET MAUI) drag and drop gesture recognizer enables
items, and their associated data packages, to be dragged from one onscreen location to
another location using a continuous gesture. Drag and drop can take place in a single
application, or it can start in one application and end in another.

The drag source, which is the element on which the drag gesture is initiated, can provide
data to be transferred by populating a data package object. When the drag source is
released, drop occurs. The drop target, which is the element under the drag source, then
processes the data package.

) Important

On iOS a minimum platform of iOS 11 is required.

The process for enabling drag and drop in an app is as follows:

1. Enable drag on an element by adding a DragGestureRecognizer object to its


GestureRecognizers collection. For more information, see Enable drag.
2. [optional] Build a data package. .NET MAUI automatically populates the data
package for image and text controls, but for other content you'll need to construct
your own data package. For more information, see Build a data package.
3. Enable drop on an element by adding a DropGestureRecognizer object to its
GestureRecognizers collection. For more information, see Enable drop.
4. [optional] Handle the DropGestureRecognizer.DragOver event to indicate the type
of operation allowed by the drop target. For more information, see Handle the
DragOver event.
5. [optional] Process the data package to receive the dropped content. .NET MAUI
will automatically retrieve image and text data from the data package, but for
other content you'll need to process the data package. For more information, see
Process the data package.

Enable drag
In .NET MAUI, drag gesture recognition is provided by the DragGestureRecognizer class.
This class defines the following properties:
CanDrag, of type bool , which indicates whether the element the gesture
recognizer is attached to can be a drag source. The default value of this property is
true .

DragStartingCommand, of type ICommand , which is executed when a drag gesture


is first recognized.
DragStartingCommandParameter, of type object , which is the parameter that's
passed to the DragStartingCommand.
DropCompletedCommand, of type ICommand , which is executed when the drag
source is dropped.
DropCompletedCommandParameter, of type object , which is the parameter that's
passed to the DropCompletedCommand.

These properties are backed by BindableProperty objects, which means that they can be
targets of data bindings, and styled.

The DragGestureRecognizer class also defines DragStarting and DropCompleted events


that fire if the CanDrag property is true . When a DragGestureRecognizer object detects
a drag gesture, it executes the DragStartingCommand and invokes the DragStarting
event. Then, when the DragGestureRecognizer object detects the completion of a drop
gesture, it executes the DropCompletedCommand and invokes the DropCompleted
event.

The DragStartingEventArgs object that accompanies the DragStarting event defines the
following properties:

Handled, of type bool , indicates whether the event handler has handled the event
or whether .NET MAUI should continue its own processing.
Cancel, of type bool , indicates whether the event should be canceled.
Data, of type DataPackage, indicates the data package that accompanies the drag
source. This is a read-only property.

The following XAML example shows a DragGestureRecognizer attached to an Image:

XAML

<Image Source="monkeyface.png">

<Image.GestureRecognizers>

<DragGestureRecognizer />

</Image.GestureRecognizers>

</Image>

In this example, a drag gesture can be initiated on the Image.


 Tip

A drag gesture is initiated with a long-press followed by a drag.

Build a data package


.NET MAUI will automatically build a data package for you, when a drag is initiated, for
the following controls:

Text controls. Text values can be dragged from CheckBox, DatePicker, Editor, Entry,
Label, RadioButton, Switch, and TimePicker objects.
Image controls. Images can be dragged from Button, Image, and ImageButton
controls.

The following table shows the properties that are read, and any conversion that's
attempted, when a drag is initiated on a text control:

Control Property Conversion

CheckBox IsChecked bool converted to a string .

DatePicker Date DateTime converted to a string .

Editor Text

Entry Text

Label Text

RadioButton IsChecked bool converted to a string .

Switch IsToggled bool converted to a string .

TimePicker Time TimeSpan converted to a string .

For content other than text and images, you'll need to build a data package yourself.

Data packages are represented by the DataPackage class, which defines the following
properties:

Properties, of type DataPackagePropertySet , which is a collection of properties that


comprise the data contained in the DataPackage . This property is a read-only
property.
Image, of type ImageSource , which is the image contained in the DataPackage .
Text, of type string , which is the text contained in the DataPackage .
View, of type DataPackageView , which is a read-only version of the DataPackage .

The DataPackagePropertySet class represents a property bag stored as a


Dictionary<string,object> . For information about the DataPackageView class, see
Process the data package.

Store image or text data


Image or text data can be associated with a drag source by storing the data in the
DataPackage.Image or DataPackage.Text property. You can add the data in the handler

for the DragStarting event.

The following XAML example shows a DragGestureRecognizer that registers a handler


for the DragStarting event:

XAML

<Path Stroke="Black"

StrokeThickness="4">

<Path.GestureRecognizers>

<DragGestureRecognizer DragStarting="OnDragStarting" />

</Path.GestureRecognizers>

<Path.Data>

<!-- PathGeometry goes here -->

</Path.Data>

</Path>

In this example, the DragGestureRecognizer is attached to a Path object. The


DragStarting event is raised when a drag gesture is detected on the Path, which

executes the OnDragStarting event handler:

C#

void OnDragStarting(object sender, DragStartingEventArgs e)

e.Data.Text = "My text data goes here";

The DragStartingEventArgs object that accompanies the DragStarting event has a Data
property, of type DataPackage . In this example, the Text property of the DataPackage
object is set to a string . The DataPackage can then be accessed on drop, to retrieve the
string .
Store data in the property bag
Any data, including images and text, can be associated with a drag source by storing the
data in the DataPackage.Properties collection. You can add the data in the handler for
the DragStarting event.

The following XAML example shows a DragGestureRecognizer that registers a handler


for the DragStarting event:

XAML

<Rectangle Stroke="Red"

Fill="DarkBlue"

StrokeThickness="4"

HeightRequest="200"

WidthRequest="200">

<Rectangle.GestureRecognizers>

<DragGestureRecognizer DragStarting="OnDragStarting" />

</Rectangle.GestureRecognizers>

</Rectangle>

In this example, the DragGestureRecognizer is attached to a Rectangle object. The


DragStarting event is raised when a drag gesture is detected on the Rectangle, which

executes the OnDragStarting event handler:

C#

void OnDragStarting(object sender, DragStartingEventArgs e)

Shape shape = (sender as Element).Parent as Shape;

e.Data.Properties.Add("Square", new Square(shape.Width, shape.Height));

The DragStartingEventArgs object that accompanies the DragStarting event has a Data
property, of type DataPackage . The Properties collection of the DataPackage object,
which is a Dictionary<string, object> collection, can be modified to store any required
data. In this example, the Properties dictionary is modified to store a Square object
that represents the size of the Rectangle against a "Square" key.

Enable drop
In .NET MAUI, drop gesture recognition is provided by the DropGestureRecognizer class.
This class defines the following properties:
AllowDrop, of type bool , which indicates whether the element the gesture
recognizer is attached to can be a drop target. The default value of this property is
true .

DragOverCommand, of type ICommand , which is executed when the drag source is


dragged over the drop target.
DragOverCommandParameter, of type object , which is the parameter that's
passed to the DragOverCommand .
DragLeaveCommand, of type ICommand , which is executed when the drag source is
dragged off the drop target.
DragLeaveCommandParameter, of type object , which is the parameter that's
passed to the DragLeaveCommand .
DropCommand, of type ICommand , which is executed when the drag source is
dropped over the drop target.
DropCommandParameter, of type object , which is the parameter that's passed to
the DropCommand .

These properties are backed by BindableProperty objects, which means that they can be
targets of data bindings, and styled.

The DropGestureRecognizer class also defines DragOver, DragLeave, and Drop events
that fire if the AllowDrop property is true . When a DropGestureRecognizer recognizes a
drag source over the drop target, it executes the DragOverCommand and invokes the
DragOver event. Then, if the drag source is dragged off the drop target, the
DropGestureRecognizer executes the DragLeaveCommand and invokes the DragLeave
event. Finally, when the DropGestureRecognizer recognizes a drop gesture over the
drop target, it executes the DropCommand and invokes the Drop event.

The DragEventArgs class, which accompanies the DragOver and DragLeave events,
defines the following properties:

Data, of type DataPackage , which contains the data associated with the drag
source. This property is read-only.
AcceptedOperation, of type DataPackageOperation , which specifies which
operations are allowed by the drop target.

For information about the DataPackageOperation enumeration, see Handle the DragOver
event.

The DropEventArgs class that accompanies the Drop event defines the following
properties:

Data, of type DataPackageView , which is a read-only version of the data package.


Handled, of type bool , indicates whether the event handler has handled the event
or whether .NET MAUI should continue its own processing.

The following XAML example shows a DropGestureRecognizer attached to an Image:

XAML

<Image BackgroundColor="Silver"

HeightRequest="300"

WidthRequest="250">

<Image.GestureRecognizers>

<DropGestureRecognizer />

</Image.GestureRecognizers>

</Image>

In this example, when a drag source is dropped on the Image drop target, the drag
source will be copied to the drop target if the drag source is an ImageSource . .NET MAUI
automatically copies dragged images, and text, to compatible drop targets.

Handle the DragOver event


The DropGestureRecognizer.DragOver event can be optionally handled to indicate which
type of operations are allowed by the drop target. You can indicate the allowable
operations by setting the AcceptedOperation property, of type DataPackageOperation , on
the DragEventArgs object that accompanies the DragOver event.

The DataPackageOperation enumeration defines the following members:

None , indicates that no action will be performed.

Copy , indicates that the drag source content will be copied to the drop target.

) Important

When a DragEventArgs object is created, the AcceptedOperation property defaults


to DataPackageOperation.Copy .

The following XAML example shows a DropGestureRecognizer that registers a handler


for the DragOver event:

XAML

<Image BackgroundColor="Silver"

HeightRequest="300"

WidthRequest="250">

<Image.GestureRecognizers>

<DropGestureRecognizer DragOver="OnDragOver" />

</Image.GestureRecognizers>

</Image>

In this example, the DropGestureRecognizer is attached to an Image object. The


DragOver event is raised when a drag source is dragged over the drop target, but hasn't
been dropped, which executes the OnDragOver event handler:

C#

void OnDragOver(object sender, DragEventArgs e)

e.AcceptedOperation = DataPackageOperation.None;

In this example, the AcceptedOperation property of the DragEventArgs object is set to


DataPackageOperation.None . This value ensures that no action is taken when a drag

source is dropped over the drop target.

Process the data package


The Drop event is raised when a drag source is released over a drop target. .NET MAUI
automatically attempts to retrieve data from the data package when a drag source is
dropped onto the following controls:

Text controls. Text values can be dropped onto CheckBox, DatePicker, Editor, Entry,
Label, RadioButton, Switch, and TimePicker objects.
Image controls. Images can be dropped onto Button, Image, and ImageButton
controls.

The following table shows the properties that are set and any conversion that's
attempted when a text-based drag source is dropped on a text control:

Control Property Conversion

CheckBox IsChecked string is converted to a bool .

DatePicker Date string is converted to a DateTime .

Editor Text

Entry Text
Control Property Conversion

Label Text

RadioButton IsChecked string is converted to a bool .

Switch IsToggled string is converted to a bool .

TimePicker Time string is converted to a TimeSpan .

For content other than text and images, you'll need to process the data package
yourself.

The DropEventArgs class that accompanies the Drop event defines a Data property, of
type DataPackageView . This property represents a read-only version of the data package.

Retrieve image or text data


Image or text data can be retrieved from a data package in the handler for the Drop
event, using methods defined in the DataPackageView class.

The DataPackageView class includes GetImageAsync and GetTextAsync methods. The


GetImageAsync method retrieves an image from the data package that was stored in the

DataPackage.Image property and returns Task<ImageSource> . Similarly, the GetTextAsync


method retrieves text from the data package that was stored in the DataPackage.Text
property and returns Task<string> .

The following example shows a Drop event handler that retrieves text from the data
package for a Path:

C#

async void OnDrop(object sender, DropEventArgs e)

string text = await e.Data.GetTextAsync();

// Perform logic to take action based on the text value.

In this example, text data is retrieved from the data package using the GetTextAsync
method. An action based on the text value can then be taken.

Retrieve data from the property bag


Any data can be retrieved from a data package in the handler for the Drop event, by
accessing the Properties collection of the data package.

The DataPackageView class defines a Properties property, of type


DataPackagePropertySetView . The DataPackagePropertySetView class represents a read-
only property bag stored as a Dictionary<string, object> .

The following example shows a Drop event handler that retrieves data from the property
bag of a data package for a Rectangle:

C#

void OnDrop(object sender, DropEventArgs e)

Square square = (Square)e.Data.Properties["Square"];

// Perform logic to take action based on retrieved value.

In this example, the Square object is retrieved from the property bag of the data
package, by specifying the "Square" dictionary key. An action based on the retrieved
value can then be taken.
Recognize a pan gesture
Article • 02/09/2023 • 3 minutes to read

A .NET Multi-platform App UI (.NET MAUI) pan gesture recognizer detects the
movement of fingers around the screen and can be used to apply that movement to
content. A typical scenario for the pan gesture is to horizontally and vertically pan an
image, so that all of the image content can be viewed when it's being displayed in a
viewport smaller than the image dimensions. This is accomplished by moving the image
within the viewport.

In .NET MAUI, pan gesture recognition is provided by the PanGestureRecognizer class.


This class defines the TouchPoints property, of type int , which represents the number
of touch points in the gesture. The default value of this property is 1. This property is
backed by a BindableProperty object, which means that it can be the target of data
bindings, and styled.

The PanGestureRecognizer class also defines a PanUpdated event that's raised when the
detected pan gesture changes. The PanUpdatedEventArgs object that accompanies this
event defines the following properties:

GestureId, of type int , which represents the id of the gesture that raised the
event.
StatusType, of type GestureStatus , which indicates if the event has been raised for
a newly started gesture, a running gesture, a completed gesture, or a canceled
gesture.
TotalX, of type double , which indicates the total change in the X direction since the
beginning of the gesture.
TotalY, of type double , which indicates the total change in the Y direction since the
beginning of the gesture.

Create a PanGestureRecognizer
To make a View recognize a pan gesture, create a PanGestureRecognizer object, handle
the PanUpdated event, and add the new gesture recognizer to the GestureRecognizers
collection on the view. The following code example shows a PanGestureRecognizer
attached to an Image:

XAML

<Image Source="monkey.jpg">

<Image.GestureRecognizers>

<PanGestureRecognizer PanUpdated="OnPanUpdated" />

</Image.GestureRecognizers>

</Image>

The code for the OnPanUpdated event handler should be added to the code-behind file:

C#

void OnPanUpdated(object sender, PanUpdatedEventArgs e)

// Handle the pan

The equivalent C# code is:

C#

PanGestureRecognizer panGesture = new PanGestureRecognizer();

panGesture.PanUpdated += (s, e) =>

// Handle the pan

};

image.GestureRecognizers.Add(panGesture);

Create a pan container


Freeform panning is typically suited to navigating within images and maps. The
PanContainer class, which is shown in the following example, is a generalized helper

class that performs freeform panning:

C#

public class PanContainer : ContentView

double x, y;

public PanContainer()

// Set PanGestureRecognizer.TouchPoints to control the

// number of touch points needed to pan

PanGestureRecognizer panGesture = new PanGestureRecognizer();

panGesture.PanUpdated += OnPanUpdated;

GestureRecognizers.Add(panGesture);

void OnPanUpdated(object sender, PanUpdatedEventArgs e)

switch (e.StatusType)

case GestureStatus.Running:

// Translate and ensure we don't pan beyond the wrapped user


interface element bounds.

Content.TranslationX = Math.Max(Math.Min(0, x + e.TotalX), -


Math.Abs(Content.Width - DeviceDisplay.MainDisplayInfo.Width));

Content.TranslationY = Math.Max(Math.Min(0, y + e.TotalY), -


Math.Abs(Content.Height - DeviceDisplay.MainDisplayInfo.Height));

break;

case GestureStatus.Completed:

// Store the translation applied during the pan

x = Content.TranslationX;

y = Content.TranslationY;

break;

In this example, the OnPanUpdated method updates the viewable content of the wrapped
view, based on the user's pan gesture. This is achieved by using the values of the TotalX
and TotalY properties of the PanUpdatedEventArgs instance to calculate the direction
and distance of the pan. The DeviceDisplay.MainDisplayInfo.Width and
DeviceDisplay.MainDisplayInfo.Height properties provide the screen width and screen

height values of the device. The wrapped user element is then panned by setting its
TranslationX and TranslationY properties to the calculated values. When panning

content in an element that does not occupy the full screen, the height and width of the
viewport can be obtained from the element's Height and Width properties.

The PanContainer class can be wrapped around a View so that a recognized pan gesture
will pan the wrapped view. The following XAML example shows the PanContainer
wrapping an Image:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:PanGesture"

x:Class="PanGesture.MainPage">

<AbsoluteLayout>

<local:PanContainer>

<Image Source="monkey.jpg" WidthRequest="1024"


HeightRequest="768" />

</local:PanContainer>

</AbsoluteLayout>

</ContentPage>

In this example, when the Image receives a pan gesture, the displayed image will be
panned.
Recognize a pinch gesture
Article • 02/09/2023 • 3 minutes to read

A .NET Multi-platform App UI (.NET MAUI) pinch gesture recognizer is used for
performing interactive zoom. A common scenario for the pinch gesture is to perform
interactive zoom of an image at the pinch location. This is accomplished by scaling the
content of the viewport.

In .NET MAUI, pinch gesture recognition is provided by the PinchGestureRecognizer


class, which defines a PinchUpdated event that's raised when the detected pinch gesture
changes. The PinchGestureUpdatedEventArgs object that accompanies the
PinchUpdated event defines the following properties:

Scale, of type double , which indicates the relative size of the pinch gesture since
the last update was received.
ScaleOrigin, of type Point , which indicates the updated origin of the pinch's
gesture.
Status, of type GestureStatus, which indicates if the event has been raised for a
newly started gesture, a running gesture, a completed gesture, or a canceled
gesture.

Create a PinchGestureRecognizer
To make a View recognize a pinch gesture, create a PinchGestureRecognizer object,
handle the PinchUpdated event, and add the new gesture recognizer to the
GestureRecognizers collection on the view. The following code example shows a

PinchGestureRecognizer attached to an Image:

XAML

<Image Source="waterfront.jpg">

<Image.GestureRecognizers>

<PinchGestureRecognizer PinchUpdated="OnPinchUpdated" />

</Image.GestureRecognizers>

</Image>

The code for the OnPinchUpdated event handler should be added to the code-behind
file:

C#
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)

// Handle the pinch

The equivalent C# code is:

C#

PinchGestureRecognizer pinchGesture = new PinchGestureRecognizer();

pinchGesture.PinchUpdated += (s, e) =>

// Handle the pinch

};

image.GestureRecognizers.Add(pinchGesture);

Create a pinch container


The PinchToZoomContainer class, which is shown in the following example, is a
generalized helper class that can be used to interactively zoom a View:

C#

public class PinchToZoomContainer : ContentView

double currentScale = 1;

double startScale = 1;

double xOffset = 0;

double yOffset = 0;

public PinchToZoomContainer()

PinchGestureRecognizer pinchGesture = new PinchGestureRecognizer();

pinchGesture.PinchUpdated += OnPinchUpdated;

GestureRecognizers.Add(pinchGesture);

void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)

if (e.Status == GestureStatus.Started)

// Store the current scale factor applied to the wrapped user


interface element,

// and zero the components for the center point of the translate
transform.

startScale = Content.Scale;

Content.AnchorX = 0;

Content.AnchorY = 0;

if (e.Status == GestureStatus.Running)

// Calculate the scale factor to be applied.

currentScale += (e.Scale - 1) * startScale;

currentScale = Math.Max(1, currentScale);

// The ScaleOrigin is in relative coordinates to the wrapped


user interface element,

// so get the X pixel coordinate.

double renderedX = Content.X + xOffset;

double deltaX = renderedX / Width;

double deltaWidth = Width / (Content.Width * startScale);

double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;

// The ScaleOrigin is in relative coordinates to the wrapped


user interface element,

// so get the Y pixel coordinate.

double renderedY = Content.Y + yOffset;

double deltaY = renderedY / Height;

double deltaHeight = Height / (Content.Height * startScale);

double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;

// Calculate the transformed element pixel coordinates.

double targetX = xOffset - (originX * Content.Width) *


(currentScale - startScale);

double targetY = yOffset - (originY * Content.Height) *


(currentScale - startScale);

// Apply translation based on the change in origin.

Content.TranslationX = Math.Clamp(targetX, -Content.Width *


(currentScale - 1), 0);
Content.TranslationY = Math.Clamp(targetY, -Content.Height *
(currentScale - 1), 0);

// Apply scale factor


Content.Scale = currentScale;

if (e.Status == GestureStatus.Completed)

// Store the translation delta's of the wrapped user interface


element.

xOffset = Content.TranslationX;

yOffset = Content.TranslationY;

In this example, the OnPinchUpdated method updates the zoom level of the wrapped
view, based on the user's pinch gesture. This is achieved by using the values of the
Scale, ScaleOrigin and Status properties of the PinchGestureUpdatedEventArgs object to
calculate the scale factor to be applied at the origin of the pinch gesture. The wrapped
view is then zoomed at the origin of the pinch gesture by setting its TranslationX ,
TranslationY , and Scale properties to the calculated values.

The PinchToZoomContainer class can be wrapped around a View so that a recognized


pinch gesture will zoom the wrapped view. The following XAML example shows the
PinchToZoomContainer wrapping an Image:

XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"

xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"

xmlns:local="clr-namespace:PinchGesture;assembly=PinchGesture"

x:Class="PinchGesture.HomePage">

<Grid>

<local:PinchToZoomContainer>

<Image Source="waterfront.jpg" />

</local:PinchToZoomContainer>

</Grid>

</ContentPage>

In this example, when the Image receives a pinch gesture, the displayed image will be
zoomed-in or out.
Recognize a pointer gesture
Article • 02/09/2023 • 3 minutes to read

A .NET Multi-platform App UI (.NET MAUI) pointer gesture recognizer detects when the
pointer enters, exits, and moves within a view and is implemented with the
PointerGestureRecognizer class. This class defines the following properties:

PointerEnteredCommand, of type ICommand , which is the command to invoke when


the pointer enters the bounding area of the view.
PointerEnteredCommandParameter, of type object , which is the parameter that's
passed to PointerEnteredCommand.
PointerExitedCommand, of type ICommand , which is the command to invoke when
the pointer that's in the view's bounding area leaves that bounding area.
PointerExitedCommandParameter, of type object , which is the parameter that's
passed to PointerExitedCommand.
PointerMovedCommand, of type ICommand , which is the command to invoke when
the pointer moves while remaining within the bounding area of the view.
PointerMovedCommandParameter, of type object , which is the parameter that's
passed to PointerMovedCommand.

These properties are backed by BindableProperty objects, which means that they can be
targets of data bindings, and styled.

The PointerGestureRecognizer class also defines the following events:

PointerEntered, that's raised when the pointer enters the bounding area of the
view.
PointerExited, that's raised when the pointer that's in the view's bounding area
leaves that bounding area.
PointerMoved, that's raised when the pointer moves while remaining within the
bounding area of the view.

A PointerEventArgs object accompanies all three events, and defines a GetPosition


method that returns a Point? object that represents the position of the pointer when
the gesture was detected. For more information about the GetPosition method, see Get
the gesture position.

) Important

Pointer gesture recognition is only supported on iPadOS, Mac Catalyst, and


Windows.
.NET MAUI also defines a PointerOver visual state. This state can change the visual
appearance of a view when it has a mouse cursor hovering over it, but isn't pressed. For
more information, see Visual states.

Create a PointerGestureRecognizer
To make a View recognize pointer gestures, create a PointerGestureRecognizer object,
handle the required events, and add the gesture recognizer to the GestureRecognizers
collection on the view.
Alternatively, create a PointerGestureRecognizer object, and bind
the required commands to ICommand implementations, and add the gesture recognizer
to the GestureRecognizers collection on the view.

The following code example shows a PointerGestureRecognizer attached to an Image.


The PointerGestureRecognizer uses events to respond to the detection of pointer
gestures:

XAML

<Image Source="dotnet_bot.png">

<Image.GestureRecognizers>

<PointerGestureRecognizer PointerEntered="OnPointerEntered"

PointerExited="OnPointerExited"

PointerMoved="OnPointerMoved" />

</Image.GestureRecognizers>

</Image>

The code for the event handlers should be added to the code-behind file:

C#

void OnPointerEntered(object sender, PointerEventArgs e)

// Handle the pointer entered event

void OnPointerExited(object sender, PointerEventArgs e)

// Handle the pointer exited event

void OnPointerMoved(object sender, PointerEventArgs e)

// Handle the pointer moved event

The equivalent C# code is:

C#

PointerGestureRecognizer pointerGestureRecognizer = new


PointerGestureRecognizer();

pointerGestureRecognizer.PointerEntered += (s, e) =>

// Handle the pointer entered event

};

pointerGestureRecognizer.PointerExited += (s, e) =>

// Handle the pointer exited event

};

pointerGestureRecognizer.PointerMoved += (s, e) =>

// Handle the pointer moved event

};

Image image = new Image();

image.GestureRecognizers.Add(pointerGestureRecognizer);

Get the gesture position


The position at which a pointer gesture occurred can be obtained by calling the
GetPosition method on a PointerEventArgs object. The GetPosition method accepts an

Element? argument, and returns a position as a Point? object:

C#

void OnPointerExited(object sender, PointerEventArgs e)

// Position inside window

Point? windowPosition = e.GetPosition(null);

// Position relative to an Image

Point? relativeToImagePosition = e.GetPosition(image);

// Position relative to the container view

Point? relativeToContainerPosition = e.GetPosition((View)sender);

The Element? argument defines the element the position should be obtained relative to.
Supplying a null value as this argument means that the GetPosition method returns a
Point? object that defines the position of the pointer gesture inside the window.
Recognize a swipe gesture
Article • 02/09/2023 • 4 minutes to read

A .NET Multi-platform App UI (.NET MAUI) swipe gesture recognizer detects when a
finger is moved across the screen in a horizontal or vertical direction, and is often used
to initiate navigation through content.

In .NET MAUI, drag gesture recognition is provided by the SwipeGestureRecognizer


class. This class defines the following properties:

Command, of type ICommand , which is executed when a swipe gesture is


recognized.
CommandParameter, of type object , which is the parameter that's passed to the
Command .

Direction, of type SwipeDirection, which defines the direction


Threshold, of type uint , which represents the minimum swipe distance that must
be achieved for a swipe to be recognized, in device-independent units. The default
value of this property is 100, which means that any swipes that are less than 100
device-independent units will be ignored.

These properties are backed by BindableProperty objects, which means that they can be
targets of data bindings, and styled.

The SwipeGestureRecognizer also defines a Swiped event that's raised when a swipe is
recognized. The SwipedEventArgs object that accompanies the Swiped event defines the
following properties:

Direction, of type SwipeDirection, indicates the direction of the swipe gesture.


Parameter, of type object , indicates the value passed by the CommandParameter
property, if defined.

Create a SwipeGestureRecognizer
To make a View recognize a swipe gesture, create a SwipeGestureRecognizer object, set
the Direction property to a SwipeDirection enumeration value ( Left , Right , Up , or
Down ), optionally set the Threshold property, handle the Swiped event, and add the new

gesture recognizer to the GestureRecognizers collection on the view. The following


example shows a SwipeGestureRecognizer attached to a BoxView:

XAML
<BoxView Color="Te