The first project with .NET MAUI

The first project with .NET MAUI

14 March 2021 3:18:29 PM

MAUI/Xamarin

Share:

Hello!

Today we will migrate an existing Xamarin.Forms Application to .NET MAUI! As a victim I chose my KanbanBoard app.

Installation

First of all, we need to install the latest .NET SDK: https://github.com/dotnet/installer

You will also need to install builds of the iOS and Android workloads:

Android:

iOS:

Make sure you have the latest Xcode (12.4 at the moment of writing).

Mac Catalyst:

Solution Configuration

  1. Because .NET MAUI is not published to Nuget yet, we need to set up additional package sources.

1.1. Create NuGet.config file new the solution file

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <!-- ensure only the sources defined below are used -->
    <add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
    <add key="xamarin" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/xamarin-impl/nuget/v3/index.json" />
    <add key="public"  value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <config>
    <add key="globalPackagesFolder" value="packages" />
  </config>
</configuration>

1.2. Create global.json file near the solution file

{
    "sdk": {
        "version": "6.0.100-preview.2.21114.3",
        "rollForward": "disable",
        "allowPrerelease": true
    }
}
  1. Edit solution file
  • Remove Platform-specific projects;
  • Remove unused Solution Configuration Platforms

The base solution configuration is done.

It's project time

.NET MAUI introduces the Single project concept, SDK-project style, and much more. Let's first migrate our platforms to the “Shared” project.

  1. For each platform create a folder in the “Shared” project:
  • Android
  • iOS
  • MacCatalyst

1.1 Android

  • Copy AndroidManifest.xml, MainActivity.cs, MainApplication.cs, all your services, Resources folder to the Android folder.

1.2 iOS, macOS

  • Copy Main.cs, Info.plist, Entitlements.plist, AppDelegate.cs, LaunchScreen.storyboard, and all your services to the iOS/MacCatalyst folder
  1. Delete Old platforms projects folders.

  2. Now we need to modify and change these files

  • From AndroidManifest.xml remove package name, version code, version name. We will set these settings later in csproj file.
  • Replace MainActivity.cs content with:
namespace YourNamespace
{
	using Android.App;
	using Microsoft.Maui;

	[Activity(Theme = "@style/MainTheme", MainLauncher = true)]
	public class MainActivity : MauiAppCompatActivity
	{
	}
}
  • Replace MainApplication.cs content with:
namespace YourNamespace
{
	using Android.App;
	using Microsoft.Maui;

	[Activity(Theme = "@style/MainTheme", MainLauncher = true)]
	public class MainActivity : MauiAppCompatActivity
	{
	}
}
  • Replace AppDelegate.cs content with:
using Foundation;
using Microsoft.Maui;

namespace YourNamespace
{
	[Register(nameof(AppDelegate))]
	public class AppDelegate : MauiUIApplicationDelegate<Application>
	{
	}
}
  1. Replace all Xamarin.Forms with Microsoft.Maui
  2. Delete App.xaml and App.xaml.cs
  3. Create Application.cs with content:
namespace KanbanBoard
{
	using System.Linq;
	using Microsoft.Extensions.DependencyInjection;
	using Microsoft.Maui;
	using Microsoft.Maui.Hosting;

	public class Application : MauiApp
	{
		public override IAppHostBuilder CreateBuilder() => 
			base.CreateBuilder()
				.ConfigureServices((ctx, services) =>
				{
					services.AddTransient<MainPage>();
					services.AddTransient<IPath, DbPath>();
					services.AddTransient<IWindow, MainWindow>();
				})
				.ConfigureFonts((hostingContext, fonts) =>
				{
					fonts.AddFont("fasolid.otf", "FASolid");
				});

		public override IWindow CreateWindow(IActivationState state)
		{
			Microsoft.Maui.Controls.Compatibility.Forms.Init(state);
			return Services.GetService<IWindow>();
		}
	}
}
  1. For each content page replace the content with:
using System;
using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;

namespace YourNamespace
{
	[XamlCompilation(XamlCompilationOptions.Compile)]
	public partial class MainPage : ContentPage, IPage
	{
		public MainPage()
		{
			InitializeComponent();
		}

		public IView View
		{
			get => (IView)Content;
			set => Content = (View)value;
		}
	}
}
  1. Create MainWindow.cs with content:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui;

namespace YourNamespace
{
	public class MainWindow : IWindow
	{
		public IPage Page { get; set; }
		public IMauiContext MauiContext { get; set; }

		public MainWindow()
		{
			Page = App.Current.Services.GetService<MainPage>();
		}
	}
}
  1. Finally update csproj file with:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net6.0-android;net6.0-ios</TargetFrameworks>
    <TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">$(TargetFrameworks);net6.0-maccatalyst</TargetFrameworks>
    <OutputType>Exe</OutputType>
    <SingleProject>true</SingleProject>
    <ApplicationId>com.vladislavantonyuk.kanbanboard</ApplicationId>
    <ApplicationTitle>KanbanBoard</ApplicationTitle>
    <ApplicationVersion>1.0</ApplicationVersion>
    <AndroidVersionCode>1</AndroidVersionCode>
    <RuntimeIdentifier Condition="'$(TargetFramework)' == 'net6.0-ios'">ios-x64</RuntimeIdentifier>
    <RuntimeIdentifier Condition="'$(TargetFramework)' == 'net6.0-maccatalyst'">maccatalyst-x64</RuntimeIdentifier>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Maui" Version="6.0.100-preview.2.122" />
    <PackageReference Include="Refractored.MvvmHelpers" Version="1.6.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0-preview.1.21102.2" />
  </ItemGroup>
  <ItemGroup>
    <SharedImage Include="appicon.svg" ForegroundFile="appiconfg.svg" IsAppIcon="true" />
    <SharedFont Include="Resources\Fonts\fasolid.otf" />
  </ItemGroup>
</Project>

Pay attention to the last ItemGroup. .NET MAUI is integrated with Resizetizer NT, so SharedImage and SharedFont will automatically prepare resources for all your applications.

Build and Run

dotnet build KanbanBoard -t:Run -f net6.0-android - for Android
dotnet build KanbanBoard -t:Run -f net6.0-ios - for iOS
dotnet build KanbanBoard -t:Run -f net6.0-maccatalyst  - for macOS

Sometimes it may be an issue: error MSB4057: The target "Run" does not exist in the project.. To solve it add --no-restore.

Issues

1. Android deployment (Solved)

I was not able to deploy the application to the device until specified the RuntimeIdentifiers for Android. Add this line to your csproj file:

<RuntimeIdentifiers Condition="'$(TargetFramework)' == 'net6.0-android'">android-arm;android-arm64;android-x86;android-x64</RuntimeIdentifiers>

2. Handler not found (In progress)

.NET MAUI is still in the early stage and unfortunately, I receive the runtime issue: Unhandled Exception: System.Exception: Handler not found for view Microsoft.Maui.Controls.IndicatorView.

But commenting on some XAML code I made it running: Kanban MAUI

Related:

Creating Kanban Board using Xamarin Forms 5

Creating Kanban Board using Xamarin Forms 5

This article describes how to create Kanban Board using Xamarin Forms 5 only. Drag & Drop Cards, Set Column WIP, Store data in Local Db.

How to show SnackBar and Toast using Xamarin Community Toolkit

How to show SnackBar and Toast using Xamarin Community Toolkit

Demonstrate how to configure SnackBar and Toast using Xamarin Community Toolkit

An error has occurred. This application may no longer respond until reloaded. Reload 🗙