With the end of support for Xamarin approaching in May 2024, 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 upgrade .NET MAUI easier.
With .NET MAUI, Microsoft is modernizing Xamarin Forms into a better cross-platform UI framework that not only brings architectural and implementation changes, but also allows you to move to other target platforms such as Android, iOS, Mac OS (with Mac Catalyst), and Windows. Full support for Samsung's Tizen is also provided.
Is Xamarin and Xamarin Forms dying? If you think of Xamarin as just a product name, yes. Xamarin as a framework will live on as part of .NET MAUI and .NET for Android/ iOS under new names.
The short answer is no. The long answer is the MAUI team has integrated much of what Xamarin developers know and love about the framework into .NET MAUI and .NET for Android/ iOS. The compatibility mode in .NET MAUI allows you to continue using most of your Xamarin Forms custom renderers and XAML layouts. However, migration is inevitable as many small things like namespaces have changed along with the project structure.
With the following 7 steps, we were able to successfully migrate our .NET MAUI projects away from Xamarin.Forms:
You have a migration project? We can save you some time here.
Before you take the migration to .NET MAUI into your own hands, talk to us. Our team of experienced Xamarin developers is looking forward to discussing your project and helping you convert it to .NET MAUI.
Schedule an appointment now
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 project 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 platforms 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.
OnRequestPermissionsResult
method and delete the call of LoadApplication
method.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 Expert-as-a-Service.
Have a look at app 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 properties 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();
}
}
You like how we approach things?
You have made it this far, as a developer you have gained an insight into our work. Migrations are just one part of our Xamarin and .NET MAUI work. We support you in all areas of app development.
Let's talk
To use the new .NET MAUI single project structure, you must perform the following steps. It is optional, but will bring your project closer to the .NET MAUI project structure.
Platforms
with a subfolder each for Android
and iOS
.Resources
folder to the Maui project if there isn't one already, and copy all the images there.After that, you can delete your Forms.iOS and Forms.Android projects.
Microsoft provides with the .NET Upgrade Assistant a tool that performs many of the steps described here automatically. The advantage of the .NET Upgrade 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. Also the Upgrade Assistant only supports C# and Visual Basic which leaves F# (F-Sharp) behind.
To give it a try to see if it works better in your project, two steps are required:
dotnet tool update --global upgrade-assistant
.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 .NET 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.
With over 7 years of experience in the development of cross-platform apps with Xamarin and .NET MAUI, Martin is one of the old hands at Cayas Software. Customers from the agricultural, logistics and healthcare industries appreciate his calm manner and analytical approach to the implementation of their Xamarin and .NET MAUI projects. He shares some of his daily work with Xamarin and .NET MAUI in his articles.
This post is a continuation of the Hackathon topic post, where the technical implementation of voice commands in .NET MAUI is revealed, as well as the challenges the development team faced and how they successfully solved them.
As mobile app developer, we constantly have the need to exchange information between the app and the backend. In most cases, a RESTful-API is the solution. But what if a constant flow of data exchange in both directions is required? In this post we will take a look at MQTT and how to create your own simple chat app in .NET MAUI.
.NET MAUI enable us to write platform and device-factor independent apps, which makes it neccessary to adapt dynamically to the users screen size and form. In this blog post you learn how to make your XAML layouts adapt to different device orientations, using a similar syntax to OnIdiom and OnPlatform that you might already be familiar with.