With the end of support for Xamarin approaching, developers are busy migrating existing Xamarin Forms projects to .NET MAUI as its successor. So are we, of course. In this article, I'll show 7 steps we've always had to take during the transition to make your transition easier.
We have been able to successfully migrate our projects with the following 7 steps:
If you have a migration project, we can save you some time here
Before doing everything on your own, you can have a look at our services. Our team filled with experienced developers is happy to discuss your project and help you to finishing it with an excellent standard.
Have a look at our development services
Before starting there are some requirements that should be met:
Microsoft has changed the structure of the project and solution file with .NET Core and continuing. This change has not yet been adopted for Xamarin projects and is now being made up with .NET for Android and .NET for iOS as well as for .NET MAUI. To change your project file, open the Xamarin Forms csproj file and customize it according to the following example. For better understanding I have added comments to the most important entries.
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Specify which plattforms should be supported in your app -->
<TargetFrameworks>net7.0-ios;net7.0-android</TargetFrameworks>
<!-- Distinguishes .NET for Android or iOS from a MAUI project -->
<UseMaui>True</UseMaui>
<OutputType>Library</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<!-- Specify which version of a platform should be supported in your app -->
<SupportedOSPlatformVersion Condition="'$(TargetFramework)' == 'net7.0-ios'">15.4</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="'$(TargetFramework)' == 'net7.0-android'">31.0</SupportedOSPlatformVersion>
<!-- Required for C# Hot Reload -->
<UseInterpreter Condition="'$(Configuration)' == 'Debug'">True</UseInterpreter>
</PropertyGroup>
<ItemGroup>
<MauiFont Include="Resources\*" />
</ItemGroup>
<ItemGroup>
<!-- If you used CommunityToolkit -->
<PackageReference Include="CommunityToolkit.Maui" Version="1.3.0" />
<!-- Add NuGet-references of your old project file here -->
</ItemGroup>
</Project>
If you don't want to switch completely to the new "single project" structure at the beginning of the migration, you can keep the existing Android and iOS projects. However, these projects must then also be converted to the SDK style. The structure is the same as above with one minor adjustment - the OutputType value will then change from Library to Exe.
As described before, the NuGet references need to be added. The references to Xamarin.Forms and Xamarin.Essentials are no longer needed.
Now that all package references are combined in one project file, there needs to be a way to distinguish them depending on the platform. To do this, simply add a condition to the ItemGroup for each platform. Using the Google PlayService for Android as an example, this looks like this:
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0-android'">
<PackageReference Include="Xamarin.GooglePlayServices.Maps" Version="118.0.2" />
</ItemGroup>
In my experience, it is usually sufficient to change to newer versions for used NuGet packages. Only very old packages for which new versions are no longer available are problematic. In this case it usually helps to look for suitable alternatives or, if it is an open source project, to make the change yourself.
A word about Xamarin.Essentials and Xamarin.CommunityToolkit. While Xamarin.CommunityToolkit is replaced by CommunityToolkit.Maui, Xamarin.Essentials is no longer supported. Replacing Xamarin.CommunityToolkit is an important step in the migration process, as it ensures that the app can continue to use the toolkit's extensions. To use the CommunityToolkit in MAUI, the UseMauiCommunityToolkit method must be added in the MauiApp builder. More about this later in the following section.
While in Xamarin Forms we had platform specific projects, the app was launched with the platform specific code through Xamarin.Forms.Init method. In .NET MAUI, there is a single cross-platform app entry point using MauiProgram.cs. Through this static entry point, a MauiApp instance is created and returned in each supported platform. So, we need to add a new file to the .NET MAUI project (or the old Xamarin Forms project) and name it MauiProgram.cs. .NET MAUI uses the builder principle already known from .NET Core, which leads to the below structure of MauiProgram.cs:
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
return builder.Build();
}
}
In order for .NET MAUI to work on Android, three basic changes still need to be made to the old Xamarin Android project.
Some changes also need to be made in the iOS project.
You like how we approach things?
This blog is supposed to give you a little insight into our everyday business when we build an app in a full-service-environment or just provide our expertise via EaaS.
Have a look at our development services
.NET MAUI, like Xamarin Forms, also uses Xaml layouts. You can therefore continue to use your existing XAML layouts with .NET MAUI. To do this, you need to replace the XML namespaces as follows:
xmlns="http://xamarin.com/schemas/2014/forms"
becomes xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
.using Xamarin.Forms
becomes either using Microsoft.Maui
or using Microsoft.Maui.Controls
depending of the use case.using Xamarin.Forms.Xaml
becomes using Microsoft.Maui.Controls.Xaml
.For performance reasons, the Horizontal and VerticalStackLayout was introduced in MAUI. It replaces the often used StackLayout. You should therefore also invest time and optimize your layouts for this. Please have a look at the Microsoft documentation for more information.
There are also other areas where the namespace has to be adapted. Here are some known API changes you could see while migrating:
Colors
moved to Microsoft.Maui.Graphics
Shapes
moved to Microsoft.Maui.Controls
BorderColor
for Frames does not exists in MAUIIcon
for ToolbarItems becomes IconImageSource
Image
for Buttons becomes ImageSource
ForegroundColor
for Span does not exists in MAUIOnce you're done with the steps, you can launch the solution in Visual Studio or Visual Studio for Mac. It is very likely that more errors specific to your project will appear, for example from API changes due to newer NuGet package versions or namespace changes, which you will then have to fix step by step.
Typically, there are messages regarding the AssemblyInfo.cs. You can remove them because most of these poperties are now csproj properties. If you want to learn more on how to set these properties, I suggest to read equivalent-to-assemblyinfo-in-dotnet-core-csproj on stackoverflow.
Depending on the complexity and number of errors, in some cases the easiest way is to create a new MAUI project and then add layouts, services and NuGet references. Then, when your project starts, you can start copying your code piece by piece to better locate the source of the error.
We here at Cayas Software don't know of any Xamarin Forms project in which at least one custom renderer didn't need to be created. Depending on how many are in your project, it's worth converting them to the new handler structure right away. If there are too many after all, you can continue to use your Custom Renderers by updating everything Xamarin.Forms.*
related to Microsoft.Maui.*
and removing the ExportRenderer directives as they are no longer needed. After that, you still need to enable compatibility mode in MauiProgram.cs via the builder using UseMauiCompatibility()
.
// shortened
var builder = MauiApp.CreateBuilder();
.UseMauiCompatibility()
.ConfigureMauiHandlers((handlers) =>
{
handlers.AddCompatibilityRenderer(typeof("Your view"),typeof("Your renderer"));
});
However, I would recommend using the custom handler as it offers better performance and extensibility. In our article on Creating a .NET MAUI Maps Control, we show how you can create a custom handler.
In .NET MAUI, it is now much easier to perform dependency injection. MAUI already comes with a container where you can register your services and load them directly into your class using, for example, contructor injection. So you don't need the DependecyService anymore. Just register your service in the MauiProgram.cs as shown below and remove the [assembly: Dependency())]
attribute from your service class.
// shortened
var builder = MauiApp.CreateBuilder();
builder.Services.AddSingleton(typeof(BluetoothService));
To get your service into your class, you just need to pass it through the parameters and then you can use it.
public partial class BluetoothSettingsPage : ContentPage
{
public BluetoothSettingsPage(BluetoothService bluetoothService)
{
InitializeComponent();
var isBluetoothOn = bluetoothService.IsBluetoothOn();
}
}
Now you have made it this far, so we assume you gained some interest into our work. That is totally fine. Be our guest and give us some feedback or discuss a project of yours. We will be happy to hear from you.
Let's talk
To use the new MAUI single project structure, you must perform the following steps. It is optional, but will bring your project closer to the MAUI project structure.
Platforms
with a subfolder each for Android
and iOS
.After that, you can delete your Forms.iOS and Forms.Android projects.
Microsoft provides an upgrade assistant that performs many of the steps described here automatically. The advantage of the assistant is that project files, NuGet packages as well as parts of the source code are updated. At the time of writing, however, many places still need to be manually adjusted. Try for yourself if it works better in your project by running the following command in the terminal: dotnet tool update --global upgrade-assistant
.
Then run the .NET MAUI upgrade assistant with the following command: upgrade-assistant upgrade <path to sln or csproj> --non-interactive --entry-point *
.
As you can see, migrating a Xamarin Forms app to .NET MAUI is done in a few steps if you follow the mentioned points. Maybe in the near future the upgrade assistant will be able to migrate everything in an automated way and we will have to do very little manual work. Until then, this guide tries to give you a good start. Let me know how your migration went and where there was any problem. I'm sure we'll be able to help you out. You can find the source code in our public Bitbucket repository: MAUI-Migration.
Mit über 7 Jahren Erfahrung in der Entwicklung von Cross-Plattform-Apps mit Xamarin und .NET MAUI zählt Martin zu den alten Hasen bei Cayas Software. Kunden aus Agrar-, Logistik- oder der Gesundheitsbranche schätzen seine ruhige Art und das analytische Vorgehen bei der Umsetzung ihrer Xamarin und .NET MAUI-Projekte. Ein Teil aus seiner täglichen Arbeit mit Xamarin und .NET MAUI teilt er in seinen Artikeln.
Like what you read? Come work with us.
UI testing is an essential part of mobile app development to ensure that the app delivers the best possible user experience and meets the needs and expectations of its users. But how do we do that in .NET MAUI when Xamarin.UITest is not fully compatible anymore? Let's look at 3 alternatives to Xamarin.UITest.
Learn why your Xamarin Android build fails with "Error executing task Aapt: VersionCode is outside 0, 65535 interval" and how to workaround that issue.
I am currently working on porting a Xamarin Forms app to DOTNET MAUI. The app also uses maps from Apple or Google Maps to display locations. Even though there was no official support in MAUI until the release of .NET 7, I want to show you a way to display maps via custom handler.