Dotnet Maui Net Maui 7.0
Dotnet Maui Net Maui 7.0
Overview
e OVERVIEW
Supported platforms
Troubleshooting
h WHAT'S NEW
Get started
c HOW-TO GUIDE
Installation
b GET STARTED
b GET STARTED
Learning resources
p CONCEPT
Custom renderers
Effects
XAML
p CONCEPT
Overview
Fundamentals
Compilation
Field modifiers
Generics
Markup extensions
Namespaces
Pass arguments
Fundamentals
p
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
p CONCEPT
Local databases
Deployment
p CONCEPT
Hot restart
Project configuration
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.
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.
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
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.
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:
.NET MAUI Blazor apps have the following additional platform requirements:
.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.
Visual Studio
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:
Installation
1. To create .NET MAUI apps, you'll need the latest version of Visual Studio 2022:
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.
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.
) Important
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:
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 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.
Requirements
To use the Android Device Manager, you'll need the following items:
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.
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.
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.
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.
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....
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:
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.
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.
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:
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.
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:
The following table provides a detailed explanation of the properties listed in the New
Device and Device Editor screens:
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
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.
For more information about these properties, see Hardware Profile Properties .
Debug on the Android Emulator
Article • 08/18/2022 • 2 minutes to read
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:
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 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.
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
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.
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:
To fix this error, follow the troubleshooting steps in the Hardware acceleration issues
section.
cmd
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.
cmd
cmd
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
If hardware acceleration isn't available, see Enabling Hyper-V acceleration to learn how
to enable hardware acceleration on your computer.
To correct this problem, reboot into your computer's BIOS and enable the following
options:
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:
PowerShell
cmd
FeatureName : Microsoft-Hyper-V-All
DisplayName : Hyper-V
RestartRequired : Possible
State : Disabled
CustomProperties :
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
RestartRequired : Possible
State : Disabled
CustomProperties :
PowerShell
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.
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
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 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.
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:
Alternately, you can use the following PowerShell command to disable the Hyper-V
Hypervisor:
PowerShell
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.
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:
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.
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
mountvol Z: /d
7. Restart your computer. On the boot screen, you should see a prompt similar to the
following message:
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.
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
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 status: HAXM version 6.2.1 (4) is installed and
usable.
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
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.
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.
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.
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.
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
command
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
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
zsh
This command will install the latest released version of .NET MAUI, including the
required platform SDKs.
zsh
5. In Terminal, change directory to MyMauiApp, and build and run the app:
zsh
cd MyMauiApp
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.
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
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):
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
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.
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.
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.
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:
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
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:
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.
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
Depending on your network configuration, you may need to use an interface name
other than en0 , for example, en1 or en2 .
Tip
5. Select Login to connect Visual Studio 2022 to the Mac over SSH and add it to the
list of known machines.
) 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.
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.
dotnet
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.
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
zsh
This command will install the latest released version of .NET MAUI, including the
required platform SDKs.
zsh
5. In Terminal, change directory to MyMauiApp, and build and run the app:
zsh
cd MyMauiApp
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:
Windows 10
Developer Mode is enabled in Settings app, under Update & Security > For developers.
To enable Developer Mode in Windows 10:
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 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
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.
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:
xamarinwatchos
7 Note
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.
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.
XML
<PropertyGroup>
<AndroidSupportedAbis>armeabi-v7a;arm64-
v8a;x86;x86_64</AndroidSupportedAbis>
</PropertyGroup>
XML
<PropertyGroup>
<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
7 Note
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>
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
XML
<RunAOTCompilation>true</RunAOTCompilation>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
XML
<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:
The following examples show using dotnet new to create different types of .NET for
Android projects:
.NET CLI
Once .NET for Android projects have been created, item templates can be used to add
items to the projects:
.NET CLI
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
.NET CLI
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.
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:
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
Changes to Info.plist
Some values have moved from Info.plist to the project file.
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.
Android
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-android</TargetFramework>
...
<UseMauiEssentials>true</UseMauiEssentials>
</PropertyGroup>
</Project>
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;
base.OnCreate(savedInstanceState);
Platform.Init(this, savedInstanceState);
// ...
Android
Member Purpose
AppContext A property that gets the Context object that represents the
current app context.
C#
If there's a situation where the Activity is needed, but the app hasn't fully started,
call the WaitForActivityAsync method:
C#
C#
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;
base.OnDestroy();
Platform.ActivityStateChanged -= Platform_ActivityStateChanged;
Created
Resumed
Paused
Destroyed
SaveInstanceState
Started
Stopped
Namespace Purpose
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:
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
Similarly, because the .NET Upgrade Assistant is installed as a .NET tool, it can be easily
updated by running:
.NET CLI
) 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
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
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:
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.
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 -->
<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.DualScreen Microsoft.Maui.Controls.Foldable
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.
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 following table shows the API changes between the Xamarin.Forms.Color struct and the
Microsoft.Maui.Graphics.Color class:
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
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
Layout changes
The following table lists the layout APIs that have been removed in the move from Xamarin.Forms to
.NET MAUI:
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.
C#
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#
) 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.
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.
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.Device.An Microsoft.Maui.Devices.DevicePlatform.Androi
droid d
Xamarin.Forms.Device.iOS Microsoft.Maui.Devices.DevicePlatform.iOS
Xamarin.Forms.Device.Tiz Microsoft.Maui.Devices.DevicePlatform.Tizen
en
Xamarin.Forms.Device.U Microsoft.Maui.Devices.DevicePlatform.WinUI
WP
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.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 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.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.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
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.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.O Microsoft.Maui.Appli
SAppTheme cationModel.AppThe
me
Xamarin.Forms.S Microsoft.Maui.Cont
pan.ForegroundC rols.Span.TextColor
olor
C#
using System;
using Android.App;
using Android.Runtime;
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
namespace YOUR_NAMESPACE_HERE.Droid
[Application]
using System;
using Microsoft.Maui;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
namespace YOUR_NAMESPACE_HERE.Droid
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
C#
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Maui;
using Foundation;
using UIKit;
namespace YOUR_NAMESPACE_HERE.iOS
[Register("AppDelegate")]
Then, update Info.plist so that MinimumOSVersion is 11.0, which is the minimum iOS version required by
.NET MAUI.
Therefore, add a new class named MauiProgram that contains the following code:
C#
namespace YOUR_NAMESPACE_HERE;
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>
net6.0-maccatalyst, net7.0-maccatalyst
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.
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.
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.
.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.
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:
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"/>
</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 .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.
XAML
<StackLayout>
</StackLayout>
XAML
</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.
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>
</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
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.
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;
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();
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">
FontSize="16"
TextColor="White"
Margin="24,20"
HorizontalTextAlignment="Center" />
</Grid>
</controls:PressableView>
</Grid>
</ContentPage>
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.
C#
namespace MyMauiControl.Controls
BindableProperty.Create(nameof(Text), typeof(string),
typeof(CustomEntry), null);
BindableProperty.Create(nameof(TextColor), typeof(Color),
typeof(CustomEntry), null);
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.
C#
#elif ANDROID
#elif WINDOWS
#endif
using MyMauiControl.Controls;
using Microsoft.Maui.Handlers;
namespace MyMauiControl.Handlers
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.
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#
[nameof(CustomEntry.Text)] = MapText,
[nameof(CustomEntry.TextColor)] = MapTextColor
};
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.
XML
<ItemGroup Condition="$(TargetFramework.StartsWith('net7.0-android')) !=
true">
<None Include="**\**\*.Android.cs"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>
<None Include="**\**\*.MaciOS.cs"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>
<None Include="**\*.Windows.cs"
Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>
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
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
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.
C#
#nullable enable
using AndroidX.AppCompat.Widget;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using MyMauiControl.Controls;
namespace MyMauiControl.Handlers
base.ConnectHandler(platformView);
platformView.Dispose();
base.DisconnectHandler(platformView);
handler.PlatformView.Text = view.Text;
handler.PlatformView?.SetSelection(handler.PlatformView?.Text?.Length ?? 0);
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 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;
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
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>
TextColor="Blue" />
</Grid>
</ContentPage>
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#
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.
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.
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;
#if ANDROID
#elif IOS
#elif WINDOWS
#endif
C#
builder
.UseMauiApp<App>()
.ConfigureEffects(effects =>
effects.Add<FocusRoutingEffect, FocusPlatformEffect>();
});
return builder.Build();
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.Effects>
<local:FocusRoutingEffect />
</Entry.Effects>
</Entry>
</VerticalStackLayout>
</ContentPage>
XAML
Article • 06/24/2022 • 2 minutes to read
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.
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
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.
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.
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.
C#
namespace MyMauiApp;
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.
XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.HelloXamlPage"
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
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:
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 .
C#
public MainPage()
InitializeComponent();
Text = "Navigate!",
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center
};
};
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
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.XamlPlusCodePage"
<StackLayout>
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
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 XamlPlusCodePage()
InitializeComponent();
valueLabel.Text = args.NewValue.ToString("F3");
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"
<StackLayout>
<Slider VerticalOptions="Center"
ValueChanged="OnSliderValueChanged" />
<Label x:Name="valueLabel"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
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#
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#
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#
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.
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
VerticalOptions="Center"
FontAttributes="Bold"
FontSize="18"
TextColor="Aqua" />
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:
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
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"
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</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.
XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.GridDemoPage"
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
TextColor="White"
BackgroundColor="Blue" />
<BoxView Color="Silver"
Grid.Column="1" />
<BoxView Color="Teal"
Grid.Row="1" />
Grid.Row="1" Grid.Column="1"
TextColor="Purple"
BackgroundColor="Aqua"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
Grid.Column="2" Grid.RowSpan="2"
TextColor="Yellow"
BackgroundColor="Blue"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
Grid.Row="2" Grid.ColumnSpan="2"
TextColor="Blue"
BackgroundColor="Yellow"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
Grid.Row="2" Grid.Column="2"
TextColor="Aqua"
BackgroundColor="Red"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
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"
<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")]
...
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">
</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.
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">
</OnPlatform>
</ContentPage.Padding>
...
</ContentPage>
7 Note
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.
.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"
<StackLayout>
HorizontalOptions="Center"
VerticalOptions="Center"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
HorizontalOptions="Center"
VerticalOptions="Center"
BorderWidth="3"
Rotation="-15"
TextColor="Red"
FontSize="24" />
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.
XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SharedResourcesPage"
<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
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
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
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="Red"
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">
</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"
<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">
</OnPlatform>
</ContentPage.Resources>
<StackLayout>
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
Rotation="{StaticResource rotationAngle}"
TextColor="{StaticResource textColor}"
</StackLayout>
</ContentPage>
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
7 Note
The following example demonstrates how x:Static can explicitly reference static fields
and enumeration members:
XAML
VerticalOptions="{x:Static LayoutOptions.Start}"
HorizontalTextAlignment="{x:Static TextAlignment.Center}"
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
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"
Padding="5,25,5,0">
<StackLayout>
TextColor="{x:Static local:AppConstants.BackgroundColor}"
BackgroundColor="{x:Static
local:AppConstants.ForegroundColor}"
FontAttributes="Bold"
FontSize="30"
HorizontalOptions="Center" />
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:
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.
.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"
<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" />
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
The solution to this and other problems involves the Mode property, which is set to a
member of the BindingMode enumeration:
Default
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"
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Label x:Name="label"
Text="TEXT"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Slider x:Name="scaleSlider"
BindingContext="{x:Reference label}"
Grid.Row="1" Grid.Column="0"
Maximum="10"
Grid.Row="1" Grid.Column="1"
VerticalTextAlignment="Center" />
<Slider x:Name="rotationSlider"
BindingContext="{x:Reference label}"
Grid.Row="2" Grid.Column="0"
Maximum="360"
Grid.Row="2" Grid.Column="1"
VerticalTextAlignment="Center" />
<Slider x:Name="rotationXSlider"
BindingContext="{x:Reference label}"
Grid.Row="3" Grid.Column="0"
Maximum="360"
Grid.Row="3" Grid.Column="1"
VerticalTextAlignment="Center" />
<Slider x:Name="rotationYSlider"
BindingContext="{x:Reference label}"
Grid.Row="4" Grid.Column="0"
Maximum="360"
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
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
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.
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
static NamedColor()
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof(Color))
stringBuilder.Clear();
int index = 0;
stringBuilder.Append(' ');
stringBuilder.Append(ch);
index++;
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = (Color)fieldInfo.GetValue(null)
};
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.
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"
</ContentPage>
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.ItemTemplate>
<DataTemplate>
<ViewCell>
</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"
<ContentPage.Resources>
<x:Double x:Key="boxSize">50</x:Double>
<x:Int32 x:Key="rowHeight">60</x:Int32>
</ContentPage.Resources>
RowHeight="{StaticResource rowHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
Orientation="Horizontal"
Spacing="15">
HeightRequest="{StaticResource boxSize}"
VerticalOptions="Center">
FontAttributes="Bold"
FontSize="14" />
<StackLayout Orientation="Horizontal"
Spacing="0">
Converter=
{StaticResource intConverter},
ConverterParameter=255,
StringFormat='R=
{0:X2}'}" />
Converter=
{StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G=
{0:X2}'}" />
Converter=
{StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B=
{0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
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
float multiplier;
multiplier = 1;
float divider;
divider = 1;
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.
XAML
XAML
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
) 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"
Spacing="25" Padding="30,0"
VerticalOptions="Center"
HorizontalOptions="Center">
</VerticalStackLayout>
</ContentPage>
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;
set
if (_dateTime != value)
_dateTime = value;
public ClockViewModel()
this.DateTime = DateTime.Now;
~ClockViewModel() =>
_timer.Dispose();
target updated with the new value. In the previous code example, the
OnPropertyChanged method handles raising the event while automatically determining
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>
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
</ContentPage>
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
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;
set
if (_hue != value)
set
if (_saturation != value)
set
if (_luminosity != value)
set
if (_color != value)
_color = value;
_hue = _color.GetHue();
_saturation = _color.GetSaturation();
_luminosity = _color.GetLuminosity();
OnPropertyChanged("Hue");
OnPropertyChanged("Saturation");
OnPropertyChanged("Luminosity");
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"
<ContentPage.BindingContext>
</ContentPage.BindingContext>
HeightRequest="100"
WidthRequest="100"
HorizontalOptions="Center" />
HorizontalOptions="Center" />
Margin="20,0,20,0" />
HorizontalOptions="Center" />
Margin="20,0,20,0" />
HorizontalOptions="Center" />
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.
To allow a data binding between a Button and a viewmodel, the Button defines two
properties:
7 Note
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
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
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;
private set
if (_inputString != value)
_inputString = value;
OnPropertyChanged();
DisplayText = FormatText(_inputString);
((Command)DeleteCharCommand).ChangeCanExecute();
private set
if (_displayText != value)
_displayText = value;
OnPropertyChanged();
public KeypadViewModel()
DeleteCharCommand =
new Command(
);
// Format the string based on the type of data and the length
// Do nothing
else
return formatted;
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.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
Margin="0,0,10,0" FontSize="20"
LineBreakMode="HeadTruncation"
VerticalTextAlignment="Center" HorizontalTextAlignment="End"
Grid.ColumnSpan="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:
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
[XamlCompilation (XamlCompilationOptions.Compile)]
...
In this example, XAML compilation is enabled only for the MyPage class.
7 Note
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.
C#
[XamlCompilation (XamlCompilationOptions.Skip)]
...
In this example, XAML compilation is disabled only for the MyPage class.
2 Warning
The .NET Multi-platform App UI (.NET MAUI) x:FieldModifier attribute specifies the
access level for generated fields for named XAML elements.
Private – specifies that the generated field for the XAML element is accessible
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.
XAML
) 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.
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.
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>Japanese Macaque</x:String>
</scg:List>
</CollectionView.ItemsSource>
</CollectionView>
</ContentPage>
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>
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"
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" />
ImageUrl="https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin
_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg" />
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>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</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>
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.
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>
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>
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>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</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
.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
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.
XAML
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.
One way to use x:Static is to first define a class with some constants or static variables,
such as this AppConstants class:
C#
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">
<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.FontSize>
</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
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
XAML
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
FontSize="{x:Static sys:Math.PI}"
Scale="{x:Static sys:Math.E}"
HorizontalOptions="Center" />
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">
FontSize="18"
VerticalOptions="Center"
HorizontalTextAlignment="Center" />
<Slider x:Name="slider"
Maximum="360"
VerticalOptions="Center" />
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.
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">
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding CreateCommand}"
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding CreateCommand}"
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding CreateCommand}"
</StackLayout>
</ContentPage>
C#
public TypeDemoPage()
InitializeComponent();
view.VerticalOptions = LayoutOptions.Center;
stackLayout.Add(view);
});
BindingContext = this;
Type of type Type , which indicates the type of the elements in the array. This
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"
<ListView Margin="10">
<ListView.ItemsSource>
<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.
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">
</Style>
</ContentPage.Resources>
FontFamily="{x:Null}" />
</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 :
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.
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.
implementation.
ConverterParameter of type object , that can be set to a value to pass to the
IValueConverter implementation.
7 Note
) 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
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.
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.
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 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
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.
7 Note
XAML
<ShellContent Title="Monkeys"
Icon="monkey.png"
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 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"
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  .
For information about displaying font icons by specifying the font icon data in a
FontImageSource object, see Display font icons.
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 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"
</Style>
</ContentPage.Resources>
<StackLayout Margin="20">
</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
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:
The two IMarkupExtension interfaces define only one method each, named
ProvideValue :
C#
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.
C#
return (this as
IMarkupExtension<Color>).ProvideValue(serviceProvider);
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"
<ContentPage.Resources>
<Style TargetType="BoxView">
</Style>
</ContentPage.Resources>
<StackLayout>
<BoxView>
<BoxView.Color>
</BoxView.Color>
</BoxView>
<BoxView>
<BoxView.Color>
</BoxView.Color>
</BoxView>
</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
C#
IProvideValueTarget provideValueTarget =
serviceProvider.GetService(typeof(IProvideValueTarget)) as
IProvideValueTarget;
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.
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).
Construct Description
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:FieldModifier Specifies the access level for generated fields for named XAML elements.
Construct Description
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.
clr-namespace: or using: – the CLR namespace declared within the assembly that
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.
XAML
...
</ContentPage>
XAML
...
</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>
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.
C#
namespace MyCompany.Controls
...
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.
7 Note
C#
using MyCompany.Controls;
[assembly: Preserve]
[assembly: XmlnsDefinition("http://mycompany.com/schemas/controls",
"MyCompany.Controls")]
) 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
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 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.
<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 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
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:
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:
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.
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.
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,
XAML
<StackLayout>
<StackLayout.Margin>
<OnPlatform x:TypeArguments="Thickness">
</OnPlatform>
</StackLayout.Margin>
</StackLayout>
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
C#
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#
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.
2 Warning
Loading XAML at runtime has a significant performance cost, and generally should
be avoided.
C#
...
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 method can be used to inflate any XAML, with the following example
inflating a ContentPage and then navigating to it:
C#
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#
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 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".
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.
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
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
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"
C#
In addition, the SetValue method can also be used to set the Description attached
property:
C#
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"
C#
};
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"
C#
SemanticProperties.SetDescription(image, "Like");
In addition, the SetValue method can also be used to set the Hint attached property:
C#
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.
) 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.
XAML
SemanticProperties.HeadingLevel="Level1" />
<Label Text="Installation"
SemanticProperties.HeadingLevel="Level2" />
SemanticProperties.HeadingLevel="Level3" />
SemanticProperties.HeadingLevel="Level4" />
C#
Label label1 = new Label { Text = "Get started with .NET MAUI" };
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();
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#
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.
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
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>
C#
...
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.
XAML
C#
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"
C#
AutomationProperties.SetIsInAccessibleTree(activityIndicator, true);
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
AutomationProperties.IsInAccessibleTree="true"
C#
AutomationProperties.SetIsInAccessibleTree(button, true);
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.
XAML
<Entry AutomationProperties.IsInAccessibleTree="true"
C#
AutomationProperties.SetIsInAccessibleTree(entry, true);
AutomationProperties.SetLabeledBy(entry, label);
) Important
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:
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.
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:
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.
Enable VoiceOver
VoiceOver is the primary screen reader used on iOS and macOS. On iOS, VoiceOver can
be enabled as follows:
For alternative methods of enabling VoiceOver, see Turn on and practice VoiceOver on
iPhone and Turn on and practice VoiceOver on iPad .
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.
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.
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.
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:
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.
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 App()
InitializeComponent();
// Custom logic
};
return window;
Alternatively, to consume the lifecycle overrides, create a class that derives from the Window class
C#
namespace MyMauiApp
// 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
) Important
Android
The following table lists the .NET MAUI delegates that are invoked in response to Android lifecycle
events being raised:
) Important
Each delegate has a corresponding identically named extension method, that can be called to
register a handler for the delegate.
C#
using Microsoft.Maui.LifecycleEvents;
namespace PlatformLifecycleDemo
builder
.UseMauiApp<App>()
.ConfigureLifecycleEvents(events =>
#if ANDROID
.OnStart((activity) =>
LogEvent(nameof(AndroidLifecycle.OnStart)))
.OnBackPressed((activity) =>
LogEvent(nameof(AndroidLifecycle.OnBackPressed)) && false)
.OnStop((activity) =>
LogEvent(nameof(AndroidLifecycle.OnStop))));
#endif
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:
) 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
builder
.UseMauiApp<App>()
.ConfigureLifecycleEvents(events =>
#if 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
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:
.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.
C#
using Microsoft.Maui.LifecycleEvents;
namespace PlatformLifecycleDemo
builder
.UseMauiApp<App>()
.ConfigureLifecycleEvents(events =>
#if WINDOWS
}));
#endif
System.Diagnostics.Debug.WriteLine($"Lifecycle event:
{eventName}{(type == null ? string.Empty : $" ({type})")}");
return true;
});
return builder.Build();
C#
using Microsoft.Maui.LifecycleEvents;
namespace PlatformLifecycleDemo
builder
.UseMauiApp<App>()
.ConfigureLifecycleEvents(events =>
#if WINDOWS
}));
#endif
});
return builder.Build();
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:
C#
using Microsoft.Maui.LifecycleEvents;
...
builder
.UseMauiApp<App>()
.ConfigureLifecycleEvents(events =>
#if 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
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
C#
using Microsoft.Maui.LifecycleEvents;
namespace PlatformLifecycleDemo
builder
.UseMauiApp<App>()
.ConfigureLifecycleEvents(events =>
#if WINDOWS
.OnWindowCreated(window =>
window.SizeChanged += OnSizeChanged;
}));
events.AddEvent(nameof(Microsoft.UI.Xaml.Window.SizeChanged),
() => LogEvent("Window SizeChanged"));
#endif
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
.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:
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.
C#
BindableProperty.CreateAttached("AttachBehavior", typeof(bool),
typeof(AttachedNumericValidationBehavior), false, propertyChanged:
OnAttachBehaviorChanged);
return (bool)view.GetValue(AttachBehaviorProperty);
view.SetValue(AttachBehaviorProperty, value);
if (entry == null)
return;
if (attachBehavior)
entry.TextChanged += OnEntryTextChanged;
else
entry.TextChanged -= OnEntryTextChanged;
double result;
XAML
<ContentPage ...
xmlns:local="clr-namespace:BehaviorsDemos">
<Entry Placeholder="Enter a System.Double"
local:AttachedNumericValidationBehavior.AttachBehavior="true" />
</ContentPage>
C#
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.
XAML
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.
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.
C#
base.OnAttachedTo(bindable);
// Perform setup
base.OnDetachingFrom(bindable);
// Perform clean up
// Behavior implementation
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.
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#
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
double result;
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.
XAML
<Entry.Behaviors>
<local:NumericValidationBehavior />
</Entry.Behaviors>
</Entry>
C#
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.
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.
C#
BindableProperty.CreateAttached("AttachBehavior", typeof(bool),
typeof(NumericValidationStyleBehavior), false, propertyChanged:
OnAttachBehaviorChanged);
return (bool)view.GetValue(AttachBehaviorProperty);
view.SetValue(AttachBehaviorProperty, value);
if (entry == null)
return;
if (attachBehavior)
entry.Behaviors.Add(new NumericValidationStyleBehavior());
else
if (toRemove != null)
entry.Behaviors.Remove(toRemove);
...
XAML
<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
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.
C#
if (toRemove != null)
entry.Behaviors.Remove(toRemove);
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
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:
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
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.
XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.BasicCodeBindingPage"
<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
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:
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.
methods defined by the BindableObjectExtensions class. The code-behind for the XAML
uses a simpler SetBinding extension method from the BindableObjectExtensions class:
C#
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.
XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataBindingDemos.BasicXamlBindingPage"
<Label Text="TEXT"
FontSize="80"
HorizontalOptions="Center"
VerticalOptions="Center"
BindingContext="{x:Reference Name=slider}"
<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:
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}"
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">
<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 AlternativeCodeBindingPage()
InitializeComponent();
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.
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">
<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="Center"
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
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>
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>
</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
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.
) Important
The BindingContext property value is inherited through the visual tree.
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"
<BoxView Color="#800000FF"
WidthRequest="180"
HeightRequest="40"
HorizontalOptions="Center"
VerticalOptions="Start"
</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
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.
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">
<Label x:Name="label"
Text="TEXT"
FontSize="80"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Slider x:Name="slider"
VerticalOptions="Center"
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
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:
On property of SwitchCell
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
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.
In the following example, data bindings allow you to select a color using three Slider
elements for the hue, saturation, and luminosity:
C#
Color color;
string name;
float hue;
float saturation;
float luminosity;
get
return hue;
set
if (hue != value)
get
return saturation;
set
if (saturation != value)
get
return luminosity;
set
if (luminosity != value)
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);
get
return name;
private set
if (name != value)
name = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Name"));
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.
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>
</ContentPage.BindingContext>
<ContentPage.Resources>
<Style TargetType="Slider">
</Style>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
</Grid.RowDefinitions>
Grid.Row="0" />
<StackLayout Grid.Row="1"
Margin="10, 0">
HorizontalTextAlignment="Center" />
</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.
XAML
<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
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.
XAML
<Label Text="TEXT"
FontSize="40"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
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
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
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.
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">
</Style>
<Style TargetType="BoxView">
</Style>
</ContentPage.Resources>
<StackLayout Margin="10">
Path=Value,
<BoxView />
Path=Time,
<BoxView />
Path=Text,
<BoxView />
</StackLayout>
<BoxView />
</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 " 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
XAML
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.
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"
<ContentPage.BindingContext>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Slider">
</Style>
<Style TargetType="Label">
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
HeightRequest="100"
WidthRequest="100"
HorizontalOptions="Center" />
</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
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.
XAML
<TimePicker x:Name="timePicker">
XAML
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.
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">
</Style>
</ContentPage.Resources>
Path=Time.TotalSeconds,
Path=DateTimeFormat.DayNames[3],
<Label>
<Label.Text>
<Binding Path="DateTimeFormat.DayNames[3]"
<Binding.Source>
<globe:CultureInfo>
<x:Arguments>
<x:String>fr-FR</x:String>
</x:Arguments>
</globe:CultureInfo>
</Binding.Source>
</Binding>
</Label.Text>
</Label>
</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.
XAML
Path=DateTimeFormat.DayNames[3],
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]"
<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
Path=Content.Children[1].Text.Length,
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:
XAML
StringFormat='{0}'}" />
XAML
Path=Content,
StringFormat='{0}'}" />
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
.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.
C#
return (int)value != 0;
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
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>
</ContentPage.Resources>
<Entry x:Name="entry1"
Text=""
VerticalOptions="Center" />
<Button Text="Search"
HorizontalOptions="Center"
VerticalOptions="Center"
Path=Text.Length,
Converter={StaticResource intToBool}}"
/>
<Entry x:Name="entry2"
Text=""
Placeholder="enter destination"
VerticalOptions="Center" />
<Button Text="Submit"
HorizontalOptions="Center"
VerticalOptions="Center"
Path=Text.Length,
Converter={StaticResource intToBool}}"
/>
</StackLayout>
</ContentPage>
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.
C#
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
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">
</Style>
<Style TargetType="Switch">
</Style>
</ContentPage.Resources>
<StackLayout Orientation="Horizontal"
VerticalOptions="Center">
<Label>
<Label.Text>
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>
<Label.Text>
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter
x:TypeArguments="x:String"
TrueObject="Yes"
FalseObject="No" />
</Binding.Converter>
</Binding>
</Label.Text>
<Label.TextColor>
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 FontSize="18"
VerticalOptions="Center">
<Label.Style>
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="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.
C#
public class RgbColorViewModel : INotifyPropertyChanged
Color color;
string name;
set
if (color.Red != value)
set
if (color.Green != value)
set
if (color.Blue != value)
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);
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#
if (parameter is float)
return (float)parameter;
return (int)parameter;
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;
XAML
Converter={StaticResource doubleToInt},
ConverterParameter=255,
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 .
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"
<ContentPage.BindingContext>
</ContentPage.BindingContext>
<ContentPage.Resources>
<Style TargetType="Slider">
</Style>
<Style TargetType="Label">
</Style>
</ContentPage.Resources>
<StackLayout Margin="20">
HeightRequest="100"
WidthRequest="100"
HorizontalOptions="Center" />
Converter={StaticResource floatToInt},
ConverterParameter=255,
Converter={StaticResource floatToInt},
ConverterParameter=255,
<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
.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.
AncestorType , of type Type , the type of ancestor to look for, when the Mode
property is FindAncestor .
7 Note
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
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"
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
XAML
<ContentPage ...
<StackLayout>
...
</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 ...
<StackLayout>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
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:
XAML
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.
The following XAML shows an example of the TemplatedParent relative binding mode:
XAML
<ContentPage ...>
<ContentPage.Resources>
<ControlTemplate x:Key="CardViewControlTemplate">
BackgroundColor="{Binding CardColor}"
BorderColor="{Binding BorderColor}"
...>
<Grid>
...
... />
... />
... />
</Grid>
</Frame>
</ControlTemplate>
</ContentPage.Resources>
<StackLayout>
<controls:CardView BorderColor="DarkGray"
CardTitle="John Doe"
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource
CardViewControlTemplate}" />
<controls:CardView BorderColor="DarkGray"
CardTitle="Jane Doe"
IconBackgroundColor="SlateGray"
IconImageSource="user.png"
ControlTemplate="{StaticResource
CardViewControlTemplate}" />
<controls:CardView BorderColor="DarkGray"
CardTitle="Xamarin Monkey"
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
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
XAML
... />
XAML
... />
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
XAML
...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
...
... />
...
... />
</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.
XAML
... />
... />
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
.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 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-
) 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#
return false;
return false;
else if (!b)
return false;
return true;
return null;
if (b)
else
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.
The Convert method returns an object that represents a converted value. This method
should return:
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:
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
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"
<ContentPage.Resources>
</ContentPage.Resources>
<CheckBox>
<CheckBox.IsChecked>
<Binding Path="Employee.IsSuspended"
</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 .
CheckBox is unchecked by the user, which sets the source binding values to the value of
the CheckBox.IsChecked property.
C#
public MultiBindingConverterCodePage()
new Binding("Employee1.IsOver16"),
new Binding("Employee1.HasPassedTest"),
},
});
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,
XAML
<Label>
<Label.Text>
</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.
C#
new Binding("Employee1.Forename"),
new Binding("Employee1.MiddleName"),
new Binding("Employee1.Surname")
},
});
) 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.
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.
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"
<ContentPage.Resources>
</ContentPage.Resources>
<CheckBox>
<CheckBox.IsChecked>
</MultiBinding>
</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 .
XAML
<ContentPage ...
xmlns:local="clr-namespace:DataBindingDemos">
<ContentPage.Resources>
<ControlTemplate x:Key="CardViewExpanderControlTemplate">
BackgroundColor="{Binding CardColor}"
RowDefinitions="Auto,Auto"
Padding="8">
<local:Expander.IsVisible>
<MultiBinding Converter="{StaticResource
AllTrueConverter}">
</MultiBinding>
</local:Expander.IsVisible>
<Grid>
</Grid>
<Grid>
</Grid>
</local:Expander>
</ControlTemplate>
</ContentPage.Resources>
<StackLayout>
<controls:CardViewExpander BorderColor="DarkGray"
CardTitle="John Doe"
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
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.
To allow a data binding between a Button and a viewmodel, the Button defines two
properties:
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#
To use the command interface, your viewmodel should contain properties of type
ICommand :
C#
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
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
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
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#
string name;
double age;
string skills;
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
C#
PersonViewModel personEdit;
bool isEditing;
···
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
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>
</Grid.RowDefinitions>
<Button Text="New"
Grid.Row="0"
Command="{Binding NewCommand}"
HorizontalOptions="Start" />
<Grid Grid.Row="1"
IsEnabled="{Binding IsEditing}">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal"
Grid.Row="1" Grid.Column="1">
Maximum="100" />
VerticalOptions="Center" />
</StackLayout>
</Grid>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
</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>
<ListView Grid.Row="3"
</Grid>
</ContentPage>
Command property bound to the NewCommand property in the viewmodel, an entry form
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
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
C#
public class PersonCollectionViewModel : INotifyPropertyChanged
···
public PersonCollectionViewModel()
execute: () =>
PersonEdit.PropertyChanged += OnPersonEditPropertyChanged;
IsEditing = true;
RefreshCanExecutes();
},
canExecute: () =>
return !IsEditing;
});
···
(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 PersonCollectionViewModel()
···
execute: () =>
Persons.Add(PersonEdit);
PersonEdit.PropertyChanged -= OnPersonEditPropertyChanged;
PersonEdit = null;
IsEditing = false;
RefreshCanExecutes();
},
canExecute: () =>
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 PersonCollectionViewModel()
···
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
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.
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">
</Style>
</ContentPage.Resources>
<Grid WidthRequest="240"
HeightRequest="480"
ColumnSpacing="2"
RowSpacing="2"
HorizontalOptions="Center"
VerticalOptions="Center">
Margin="0,0,10,0"
FontSize="32"
LineBreakMode="HeadTruncation"
VerticalTextAlignment="Center"
HorizontalTextAlignment="End" />
<Button Text="CLEAR"
<Button Text="⇦"
Grid.Row="1" Grid.Column="2"
<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"
Command="{Binding DigitCommand}"
CommandParameter="0" />
<Button Text="·"
Grid.Row="5" Grid.Column="2"
Command="{Binding DigitCommand}"
CommandParameter="." />
</Grid>
</ContentPage>
C#
···
private set
if (entry != value)
entry = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Entry"));
get
return entry;
The button corresponding to the ClearCommand is always enabled and sets the entry
back to "0":
C#
···
public DecimalKeypadViewModel()
execute: () =>
Entry = "0";
RefreshCanExecutes();
});
···
void RefreshCanExecutes()
((Command)BackspaceCommand).ChangeCanExecute();
((Command)DigitCommand).ChangeCanExecute();
···
C#
···
public DecimalKeypadViewModel()
···
execute: () =>
if (Entry == "")
Entry = "0";
RefreshCanExecutes();
},
canExecute: () =>
···
···
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 DecimalKeypadViewModel()
···
Entry += arg;
Entry = Entry.Substring(1);
RefreshCanExecutes();
},
});
···
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
.NET Multi-platform App UI (.NET MAUI) data bindings have two main issues:
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.
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"
<ContentPage.BindingContext>
</ContentPage.BindingContext>
...
<StackLayout>
... />
<StackLayout Margin="10, 0">
</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 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.
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"
<Grid>
...
<ListView x:Name="colorListView"
ItemsSource="{x:Static local:NamedColor.All}"
... >
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:NamedColor">
<ViewCell>
<StackLayout Orientation="Horizontal">
... />
... />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
... />
</Grid>
</ContentPage>
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.
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.
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>
</StackLayout.BindingContext>
VerticalOptions="FillAndExpand" />
<StackLayout x:DataType="{x:Null}"
Margin="10, 0">
</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 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.
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
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 .
These properties are backed by BindableProperty objects, which means that they can be
targets of data bindings, and styled.
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.
XAML
<Image Source="monkeyface.png">
<Image.GestureRecognizers>
<DragGestureRecognizer />
</Image.GestureRecognizers>
</Image>
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:
Editor Text
Entry Text
Label Text
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:
XAML
<Path Stroke="Black"
StrokeThickness="4">
<Path.GestureRecognizers>
</Path.GestureRecognizers>
<Path.Data>
</Path.Data>
</Path>
C#
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.
XAML
<Rectangle Stroke="Red"
Fill="DarkBlue"
StrokeThickness="4"
HeightRequest="200"
WidthRequest="200">
<Rectangle.GestureRecognizers>
</Rectangle.GestureRecognizers>
</Rectangle>
C#
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 .
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:
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.
Copy , indicates that the drag source content will be copied to the drop target.
) Important
XAML
<Image BackgroundColor="Silver"
HeightRequest="300"
WidthRequest="250">
<Image.GestureRecognizers>
</Image.GestureRecognizers>
</Image>
C#
e.AcceptedOperation = DataPackageOperation.None;
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:
Editor Text
Entry Text
Control Property Conversion
Label Text
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.
The following example shows a Drop event handler that retrieves text from the data
package for a Path:
C#
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.
The following example shows a Drop event handler that retrieves data from the property
bag of a data package for a Rectangle:
C#
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.
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>
</Image.GestureRecognizers>
</Image>
The code for the OnPanUpdated event handler should be added to the code-behind file:
C#
C#
};
image.GestureRecognizers.Add(panGesture);
C#
double x, y;
public PanContainer()
panGesture.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(panGesture);
switch (e.StatusType)
case GestureStatus.Running:
break;
case GestureStatus.Completed:
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>
</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.
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
XAML
<Image Source="waterfront.jpg">
<Image.GestureRecognizers>
</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)
C#
};
image.GestureRecognizers.Add(pinchGesture);
C#
double currentScale = 1;
double startScale = 1;
double xOffset = 0;
double yOffset = 0;
public PinchToZoomContainer()
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinchGesture);
if (e.Status == GestureStatus.Started)
// 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)
if (e.Status == GestureStatus.Completed)
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.
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>
</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:
These properties are backed by BindableProperty objects, which means that they can be
targets of data bindings, and styled.
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.
) Important
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.
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#
C#
};
};
};
image.GestureRecognizers.Add(pointerGestureRecognizer);
C#
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.
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:
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
XAML
<BoxView Color="Te