Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Upgrading a multi-project Xamarin.Forms app to a multi-project .NET Multi-platform App UI (.NET MAUI) app can be accomplished by creating a new multi-project .NET MAUI app and then migrating the code and resources from your Xamarin.Forms apps to the multi-project .NET MAUI app. There are then additional steps to take advantage of changes in .NET MAUI.
To manually migrate a multi-project Xamarin.Forms app to a multi-project .NET MAUI library app, you must:
- Update your Xamarin.Forms app to use Xamarin.Forms 5.
- Update the app's dependencies to the latest versions.
- Ensure the app still works.
- Update namespaces.
- Address any API changes.
- Upgrade or replace incompatible dependencies with .NET 8 versions.
- Compile and test your app.
To simplify the upgrade process, you should create a new multi-project .NET MAUI app of the same name as your Xamarin.Forms app, and then copy in your code, configuration, and resources. This is the approach outlined below.
Update your Xamarin.Forms app
Before upgrading your Xamarin.Forms app to .NET MAUI, you should first update your Xamarin.Forms app to use Xamarin.Forms 5 and ensure that it still runs correctly. In addition, you should update the dependencies that your app uses to the latest versions.
This will help to simplify the rest of the migration process, as it will minimize the API differences between Xamarin.Forms and .NET MAUI, and will ensure that you are using .NET compatible versions of your dependencies if they exist.
Create a new project
In Visual Studio, create a new .NET MAUI multi-project app of the same name as your Xamarin.Forms project. The multi-project template provides a .NET MAUI app for Android, iOS, Mac Catalyst, and WinUI with multiple, separate app project.
Note
You can remove the the app projects for platforms you don't support.
Opening the project file for the class library will confirm that you have a .NET SDK-style project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFramework>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net8.0-windows10.0.19041.0</TargetFrameworks>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<UseMaui>true</UseMaui>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Opening a specific platform project should also confirm that you have similar properties set including versioning with a reference to the library project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-android</TargetFramework>
<SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseMaui>true</UseMaui>
</PropertyGroup>
<!--
...
-->
<ItemGroup>
<ProjectReference Include="..\MauiApp.1\MauiApp.1.csproj" />
</ItemGroup>
</Project>
Copy files to the .NET MAUI app
All of the cross-platform code from your Xamarin.Forms library project should be copied into your .NET MAUI library project in identically named folders and files.
Custom renderers can either be reused in a .NET MAUI app, or migrated to a .NET MAUI handler. For more information, see Reuse custom renderers in .NET MAUI and Migrate a Xamarin.Forms custom renderer to a .NET MAUI handler.
Effects can be reused in a .NET MAUI app. For more information, see Reuse effects.
If there are platform specific services that need to be migrated to .NET MAUI, use the AddTransient(IServiceCollection, Type) method to add a transient service of the specified type to the specified IServiceCollection.
Any platform code, resources, and configuration should then be copied to your multi-project .NET MAUI app.
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:
.NET MAUI projects make use of implicit global using directives. This feature enables you to remove using directives for the Xamarin.Essentials namespace, without having to replace them with the equivalent .NET MAUI namespaces.
In addition, the default XAML namespace has changed from http://xamarin.com/schemas/2014/forms in Xamarin.Forms to http://schemas.microsoft.com/dotnet/2021/maui in .NET MAUI. Therefore, you should replace all occurrences of xmlns="http://xamarin.com/schemas/2014/forms" with xmlns="http://schemas.microsoft.com/dotnet/2021/maui".
Note
You can quickly update your Xamarin.Forms namespaces to Microsoft.Maui by using Quick actions in Visual Studio, provided that you have Upgrade Assistant installed.
API changes
Some APIs have changed in the move from Xamarin.Forms to .NET MAUI. This is multiple reasons including removing duplicate functionality caused by Xamarin.Essentials becoming part of .NET MAUI, and ensuring that APIs follow .NET naming guidelines. The following sections discuss these changes.
Color changes
In Xamarin.Forms, the Xamarin.Forms.Color struct lets you construct Color objects using double values, and provides named colors, such as Xamarin.Forms.Color.AliceBlue. In .NET MAUI, this functionality has been separated into the Microsoft.Maui.Graphics.Color class, and the Microsoft.Maui.Graphics.Colors class.
The Microsoft.Maui.Graphics.Color class, in the Microsoft.Maui.Graphics namespace, lets you construct Color objects using float values, byte values, and int values. The Microsoft.Maui.Graphics.Colors class, which is also in the Microsoft.Maui.Graphics namespace, largely provides the same named colors. For example, use Colors.AliceBlue to specify the AliceBlue color.
The following table shows the API changes between the Xamarin.Forms.Color struct and the Microsoft.Maui.Graphics.Color class:
| Xamarin.Forms API | .NET MAUI API | Comment |
|---|---|---|
Xamarin.Forms.Color.R |
Microsoft.Maui.Graphics.Color.Red | |
Xamarin.Forms.Color.G |
Microsoft.Maui.Graphics.Color.Green | |
Xamarin.Forms.Color.B |
Microsoft.Maui.Graphics.Color.Blue | |
Xamarin.Forms.Color.A |
Microsoft.Maui.Graphics.Color.Alpha | |
Xamarin.Forms.Color.Hue |
Microsoft.Maui.Graphics.Color.GetHue | Xamarin.Forms property replaced with a method in .NET MAUI. |
Xamarin.Forms.Color.Saturation |
Microsoft.Maui.Graphics.Color.GetSaturation | Xamarin.Forms property replaced with a method in .NET MAUI. |
Xamarin.Forms.Color.Luminosity |
Microsoft.Maui.Graphics.Color.GetLuminosity | Xamarin.Forms property replaced with a method in .NET MAUI. |
Xamarin.Forms.Color.Default |
No .NET MAUI equivalent. Instead, Microsoft.Maui.Graphics.Color objects default to null. |
|
Xamarin.Forms.Color.Accent |
No .NET MAUI equivalent. | |
Xamarin.Forms.Color.FromHex |
Microsoft.Maui.Graphics.Color.FromArgb | Microsoft.Maui.Graphics.Color.FromHex is obsolete and will be removed in a future release. |
In addition, all of the numeric values in a Microsoft.Maui.Graphics.Color are float, rather than double as used in Xamarin.Forms.Color.
Note
Unlike Xamarin.Forms, a Microsoft.Maui.Graphics.Color doesn't have an implicit conversion to System.Drawing.Color.
Layout changes
The following table lists the layout APIs that have been removed in the move from Xamarin.Forms to .NET MAUI:
| Xamarin.Forms API | .NET MAUI API | Comments |
|---|---|---|
Xamarin.Forms.AbsoluteLayout.IAbsoluteList<T>.Add |
The Add overload that accepts three arguments isn't present in .NET MAUI. |
|
Xamarin.Forms.Grid.IGridList<T>.AddHorizontal |
No .NET MAUI equivalent. | |
Xamarin.Forms.Grid.IGridList<T>.AddVertical |
No .NET MAUI equivalent. | |
Xamarin.Forms.RelativeLayout |
Microsoft.Maui.Controls.Compatibility.RelativeLayout | In .NET MAUI, RelativeLayout only exists as a compatibility control for users migrating from Xamarin.Forms. Use Grid instead, or add the xmlns for the compatibility namespace. |
In addition, adding children to a layout in code in Xamarin.Forms is accomplished by adding the children to the layout's Children collection:
Grid grid = new Grid();
grid.Children.Add(new Label { Text = "Hello world" });
In .NET MAUI, the Children collection is for internal use by .NET MAUI and shouldn't be manipulated directly. Therefore, in code children should be added directly to the layout:
Grid grid = new Grid();
grid.Add(new Label { Text = "Hello world" });
Important
Any Add layout extension methods, such as GridExtensions.Add, are invoked on the layout rather than the layouts Children collection.
You may notice when running your upgraded .NET MAUI app that layout behavior is different. For more information, see Layout behavior changes from Xamarin.Forms.
Custom layout changes
The process for creating a custom layout in Xamarin.Forms involves creating a class that derives from Layout<View>, and overriding the VisualElement.OnMeasure and Layout.LayoutChildren methods. For more information, see Create a custom layout in Xamarin.Forms.
In .NET MAUI, the layout classes derive from the abstract Layout class. This class delegates cross-platform layout and measurement to a layout manager class. Each layout manager class implements the ILayoutManager interface, which specifies that Measure and ArrangeChildren implementations must be provided:
- The Measure implementation calls IView.Measure on each view in the layout, and returns the total size of the layout given the constraints.
- The ArrangeChildren implementation determines where each view should be placed within the bounds of the layout, and calls Arrange on each view with its appropriate bounds. The return value is the actual size of the layout.
For more information, see Custom layouts.
Device changes
Xamarin.Forms has a Xamarin.Forms.Device class that helps you to interact with the device and platform the app is running on. The equivalent class in .NET MAUI, Microsoft.Maui.Controls.Device, is deprecated and its functionality is replaced by multiple types.
The following table shows the .NET MAUI replacements for the functionality in the Xamarin.Forms.Device class:
| Xamarin.Forms API | .NET MAUI API | Comments |
|---|---|---|
Xamarin.Forms.Device.Android |
Microsoft.Maui.Devices.DevicePlatform.Android | |
Xamarin.Forms.Device.iOS |
Microsoft.Maui.Devices.DevicePlatform.iOS | |
Xamarin.Forms.Device.GTK |
No .NET MAUI equivalent. | |
Xamarin.Forms.Device.macOS |
No .NET MAUI equivalent. Instead, use Microsoft.Maui.Devices.DevicePlatform.MacCatalyst. | |
Xamarin.Forms.Device.Tizen |
Microsoft.Maui.Devices.DevicePlatform.Tizen | |
Xamarin.Forms.Device.UWP |
Microsoft.Maui.Devices.DevicePlatform.WinUI | |
Xamarin.Forms.Device.WPF |
No .NET MAUI equivalent. | |
Xamarin.Forms.Device.Flags |
No .NET MAUI equivalent. | |
Xamarin.Forms.Device.FlowDirection |
Microsoft.Maui.ApplicationModel.AppInfo.RequestedLayoutDirection | |
Xamarin.Forms.Device.Idiom |
Microsoft.Maui.Devices.DeviceInfo.Idiom | |
Xamarin.Forms.Device.IsInvokeRequired |
Microsoft.Maui.Dispatching.Dispatcher.IsDispatchRequired | |
Xamarin.Forms.Device.OS |
Microsoft.Maui.Devices.DeviceInfo.Platform | |
Xamarin.Forms.Device.RuntimePlatform |
Microsoft.Maui.Devices.DeviceInfo.Platform | |
Xamarin.Forms.Device.BeginInvokeOnMainThread |
Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread | |
Xamarin.Forms.Device.GetMainThreadSynchronizationContextAsync |
Microsoft.Maui.ApplicationModel.MainThread.GetMainThreadSynchronizationContextAsync | |
Xamarin.Forms.Device.GetNamedColor |
No .NET MAUI equivalent. | |
Xamarin.Forms.Device.GetNamedSize |
No .NET MAUI equivalent. | |
Xamarin.Forms.Device.Invalidate |
Microsoft.Maui.Controls.VisualElement.InvalidateMeasure | |
Xamarin.Forms.Device.InvokeOnMainThreadAsync |
Microsoft.Maui.ApplicationModel.MainThread.InvokeOnMainThreadAsync | |
Xamarin.Forms.Device.OnPlatform |
Microsoft.Maui.Devices.DeviceInfo.Platform | |
Xamarin.Forms.Device.OpenUri |
Microsoft.Maui.ApplicationModel.Launcher.OpenAsync | |
Xamarin.Forms.Device.SetFlags |
No .NET MAUI equivalent. | |
Xamarin.Forms.Device.SetFlowDirection |
Microsoft.Maui.Controls.Window.FlowDirection | |
Xamarin.Forms.Device.StartTimer |
Microsoft.Maui.Dispatching.DispatcherExtensions.StartTimer or Microsoft.Maui.Dispatching.Dispatcher.DispatchDelayed |
Map changes
In Xamarin.Forms, the Map control and associated types are in the Xamarin.Forms.Maps namespace. In .NET MAUI, this functionality has moved to the Microsoft.Maui.Controls.Maps and Microsoft.Maui.Maps namespaces. Some properties have been renamed and some types have been replaced with equivalent types from Xamarin.Essentials.
The following table shows the .NET MAUI replacements for the functionality in the Xamarin.Forms.Maps namespace:
| Xamarin.Forms API | .NET MAUI API | Comment |
|---|---|---|
Xamarin.Forms.Maps.Map.HasScrollEnabled |
Microsoft.Maui.Controls.Maps.Map.IsScrollEnabled | |
Xamarin.Forms.Maps.Map.HasZoomEnabled |
Microsoft.Maui.Controls.Maps.Map.IsZoomEnabled | |
Xamarin.Forms.Maps.Map.TrafficEnabled |
Microsoft.Maui.Controls.Maps.Map.IsTrafficEnabled | |
Xamarin.Forms.Maps.Map.MoveToLastRegionOnLayoutChange |
No .NET MAUI equivalent. | |
Xamarin.Forms.Maps.Pin.Id |
Microsoft.Maui.Controls.Maps.Pin.MarkerId | |
Xamarin.Forms.Maps.Pin.Position |
Microsoft.Maui.Controls.Maps.Pin.Location | |
Xamarin.Forms.Maps.MapClickedEventArgs.Position |
Microsoft.Maui.Controls.Maps.MapClickedEventArgs.Location | |
Xamarin.Forms.Maps.Position |
Microsoft.Maui.Devices.Sensors.Location | Members of type Xamarin.Forms.Maps.Position have changed to the Microsoft.Maui.Devices.Sensors.Location type. |
Xamarin.Forms.Maps.Geocoder |
Microsoft.Maui.Devices.Sensors.Geocoding | Members of type Xamarin.Forms.Maps.Geocoder have changed to the Microsoft.Maui.Devices.Sensors.Geocoding type. |
.NET MAUI has two Map types - Microsoft.Maui.Controls.Maps.Map and Microsoft.Maui.ApplicationModel.Map. Because the Microsoft.Maui.ApplicationModel namespace is one of .NET MAUI's global using directives, when using the Microsoft.Maui.Controls.Maps.Map control from code you'll have to fully qualify your Map usage or use a using alias.
In XAML, an xmlns namespace definition should be added for the Map control. While this isn't required, it prevents a collision between the Polygon and Polyline types, which exist in both the Microsoft.Maui.Controls.Maps and Microsoft.Maui.Controls.Shapes namespaces. For more information, see Display a map.
Other changes
A small number of other APIs have been consolidated in the move from Xamarin.Forms to .NET MAUI. The following table shows these changes:
| Xamarin.Forms API | .NET MAUI API | Comments |
|---|---|---|
Xamarin.Forms.Application.Properties |
Microsoft.Maui.Storage.Preferences | |
Xamarin.Forms.Button.Image |
Microsoft.Maui.Controls.Button.ImageSource | |
Xamarin.Forms.Frame.OutlineColor |
Microsoft.Maui.Controls.Frame.BorderColor | |
Xamarin.Forms.IQueryAttributable.ApplyQueryAttributes |
Microsoft.Maui.Controls.IQueryAttributable.ApplyQueryAttributes | In Xamarin.Forms, the ApplyQueryAttributes method accepts an IDictionary<string, string> argument. In .NET MAUI, the ApplyQueryAttributes method accepts an IDictionary<string, object> argument. |
Xamarin.Forms.MenuItem.Icon |
Microsoft.Maui.Controls.MenuItem.IconImageSource | Xamarin.Forms.MenuItem.Icon is the base class for Xamarin.Forms.ToolbarItem, and so ToolbarItem.Icon becomes ToolbarItem.IconImageSource. |
Xamarin.Forms.OrientationStateTrigger.Orientation |
Microsoft.Maui.Controls.OrientationStateTrigger.Orientation | In Xamarin.Forms, the OrientationStateTrigger.Orientation property is of type Xamarin.Forms.Internals.DeviceOrientation. In .NET MAUI, the OrientationStateTrigger.Orientation property is of type DisplayOrientation. |
Xamarin.Forms.OSAppTheme |
Microsoft.Maui.ApplicationModel.AppTheme | |
Xamarin.Forms.Span.ForegroundColor |
Microsoft.Maui.Controls.Span.TextColor | |
Xamarin.Forms.ToolbarItem.Name |
Microsoft.Maui.Controls.MenuItem.Text | Microsoft.Maui.Controls.MenuItem.Text is the base class for Microsoft.Maui.Controls.ToolbarItem, and so ToolbarItem.Name becomes ToolbarItem.Text. |
In addition, in Xamarin.Forms, the Page.OnAppearing override is called on Android when an app is backgrounded and then brought to the foreground. However, this override isn't called on iOS and Windows in the same scenario. In .NET MAUI, the OnAppearing() override isn't called on any platforms when an app is backgrounded and then brought to the foreground. Instead, you should listen to lifecycle events on Window to be notified when an app returns to the foreground. For more information, see .NET MAUI windows.
Native forms changes
Native forms in Xamarin.Forms has become native embedding in .NET MAUI, and uses a different initialization approach and different extension methods to convert cross-platform controls to their native types. For more information, see Native embedding.
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:
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
For more information about the GenerateAssemblyInfo property, see GenerateAssemblyInfo.
Update app dependencies
Generally, Xamarin.Forms NuGet packages are not compatible with .NET 8 unless they have been recompiled using .NET target framework monikers (TFMs). However, Android apps can use NuGet packages targeting the monoandroid and monoandroidXX.X frameworks.
You can confirm a package is .NET 8 compatible by looking at the Frameworks tab on NuGet for the package you're using, and checking that it lists one of the compatible frameworks shown in the following table:
| Compatible frameworks | Incompatible frameworks |
|---|---|
| net8.0-android, monoandroid, monoandroidXX.X | |
| net8.0-ios | monotouch, xamarinios, xamarinios10 |
| net8.0-macos | monomac, xamarinmac, xamarinmac20 |
| net8.0-tvos | xamarintvos |
| xamarinwatchos |
Note
.NET Standard libraries that have no dependencies on the incompatible frameworks listed above are still compatible with .NET 8.
If a package on NuGet indicates compatibility with any of the compatible 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 8 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 8 version of the package.
- Replace the dependency with a .NET 8 compatible alternative.
Compile and troubleshoot
Once your dependencies are resolved, you should build your project. Any errors will guide you towards next steps.
Tip
- Delete all bin and obj folders from all projects before opening and building projects in Visual Studio, particularly when changing .NET versions.
- Delete the Resource.designer.cs generated file from the Android project.
The following table provides guidance for overcoming common build or runtime issues:
| Issue | Tip |
|---|---|
Xamarin.* namespace doesn't exist. |
Update the namespace to its .NET MAUI equivalent. For more information, see Namespace changes. |
| API doesn't exist. | Update the API usage to its .NET MAUI equivalent. For more information, see API changes. |
| App won't deploy. | Ensure that the required platform project is set to deploy in Visual Studio's Configuration Manager. |
| App won't launch. | Update each platform project's entry point class, and the app entry point. For more information, see Bootstrap your migrated app. |
| CollectionView doesn't scroll. | Check the container layout and the measured size of the CollectionView. By default the control will take up as much space as the container allows. A Grid constrains children at its own size. However a StackLayout enables children to take up space beyond its bounds. |
| Pop-up is displayed under the page on iOS. | In Xamarin.Forms, all pop-ups on iOS are UIWindow instances but in .NET MAUI pop-ups are displayed by locating the current presenting ViewController and displaying the pop-up with PresentViewControllerAsync. In plugins such as Mopups, to ensure that your pop-ups are correctly displayed you should call DisplayAlert (or DisplayAlertAsync in .NET 10+), DisplayActionSheet (or DisplayActionSheetAsync in .NET 10+), or DisplayPromptAsync from the ContentPage that's used inside the Mopup popup. |
| BoxView not appearing. | The default size of a BoxView in Xamarin.Forms is 40x40. The default size of a BoxView in .NET MAUI is 0x0. Set WidthRequest and HeightRequest to 40. |
| Layout is missing padding, margin, or spacing. | Add default values to your project based on the .NET MAUI style resource. For more information, see Default value changes from Xamarin.Forms. |
| Custom layout doesn't work. | Custom layout code needs updating to work in .NET MAUI. For more information, see Custom layout changes. |
| Custom renderer doesn't work. | Renderer code needs updating to work in .NET MAUI. For more information, see Use custom renderers in .NET MAUI. |
| Effect doesn't work. | Effect code needs updating to work in .NET MAUI. For more information, see Use effects in .NET MAUI. |
| SkiaSharp code doesn't work. | SkiaSharp code needs minor updates to work in .NET MAUI. For more information, see Reuse SkiaSharp code in .NET MAUI. |
| Can't access previously created app properties data. | Migrate the app properties data to .NET MAUI preferences. For more information, see Migrate data from the Xamarin.Forms app properties dictionary to .NET MAUI preferences. |
| Can't access previously created secure storage data. | Migrate the secure storage data to .NET MAUI. For more information, see Migrate from Xamarin.Essentials secure storage to .NET MAUI secure storage. |
| Can't access previously created version tracking data. | Migrate the version tracking data to .NET MAUI. For more information, see Migrate version tracking data from a Xamarin.Forms app to a .NET MAUI app. |