Migrating MAUI Projects to .NET 10

EMDK For MAUI/Xamarin 11.1

Overview

This guide covers the steps required to update a Microsoft .NET MAUI project that uses the Zebra EMDK for Xamarin SDK from .NET 6 (or later) to .NET 10.

Starting with .NET 8, the MAUI SDK introduced changes that affect all existing MAUI projects:

  1. <UseMaui>true</UseMaui> no longer automatically includes MAUI NuGet packages.
    An explicit PackageReference for Microsoft.Maui.Controls is now required.
  2. Application.MainPage is deprecated. Application startup must use the CreateWindow() override instead.
  3. Symbol.XamarinEMDK is scoped to the Android target framework via a build condition in the .csproj, and therefore that condition must be updated alongside the framework moniker.

If not addressed, all three issues appear as build warnings (MA002, CS0618) or silently break package restore.


Requirements

  • Visual Studio 2022 v17.12 (or later)
  • .NET SDK 10.0 (or later)
  • .NET MAUI workload - If not already installed, see below.
  • Symbol.XamarinEMDK - Available on NuGet; verify the version is compatible with your EMDK on-device service.
  • Zebra Android device running Android 5.0 (API 21 or later) with EMDK service installed.

Verify current .NET version using the .NET CLI:


dotnet --version

Install the MAUI workload if missing:


dotnet workload install maui

Summary of Changes

Below is a list of the files that must change for a successful mitgration. Details and steps for making the changes follow.

Files to Update

  • .csproj <TargetFrameworks>:
    • Replace net6.0-* (or earlier) with net10.0-* to target the .NET 10 runtime.
  • .csproj new <ItemGroup>:
    • Add Microsoft.Maui.Controls package reference, which stopped being auto-included in .NET 8+ (error MA002)
  • .csproj Symbol.XamarinEMDK condition:
    • Update net6.0-android to net10.0-android so the condition matches the updated target framework moniker.
  • App.xaml.cs:
    • Replace MainPage = new AppShell() with CreateWindow() override; MainPage became obsolete in .NET 8+ (error CS0618)
  • .csproj.user (optional):
    • Update <ActiveDebugFramework> moniker for IDE debug toolbar alignment.
  • YourProject.csproj:
    • Update target framework monikers; add explicit MAUI package reference; update EMDK package condition.
  • App.xaml.cs:
    • Replace deprecated MainPage assignment with CreateWindow() override.

I. Update Target Frameworks

  1. Open your .csproj file and replace all net6.0, net7.0, net8.0 or net9.0 prefixes with net10.0 across every target framework moniker.
  2. Update any other platform monikers in the same way. Tizen is shown as an example in the commented statements below.

Example

Before:

<TargetFrameworks>net6.0-android;net6.0-ios;net6.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net6.0-windows10.0.19041.0</TargetFrameworks>
<!-- <TargetFrameworks>$(TargetFrameworks);net6.0-tizen</TargetFrameworks> -->
After:

<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
<!-- <TargetFrameworks>$(TargetFrameworks);net10.0-tizen</TargetFrameworks> -->

II. Add Explicit Microsoft.Maui.Controls Package Reference

Starting with .NET 8, <UseMaui>true</UseMaui> no longer pulls in MAUI NuGet packages automatically. Without this reference, the build produces warning MA002, potentially causing MAUI types not to resolve.

Add the following <ItemGroup> to your .csproj before the resource definitions:


<ItemGroup>
    <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
</ItemGroup>

$(MauiVersion) is an MSBuild property automatically set by the MAUI SDK to match your installed workload version. It keeps the reference in sync whenever the workload is updated.

To pin to a specific version instead, replace $(MauiVersion) with the version number reported in the MA002 warning when you first build (for example, 10.0.20).


III. Update Symbol.XamarinEMDK Package Condition

Symbol.XamarinEMDK is scoped to Android using a Condition on its <ItemGroup>. This condition must be updated to match the new target framework moniker, otherwise the package will not be restored for the Android build.

Example

Before:

<ItemGroup Condition="'$(TargetFramework)' == 'net6.0-android'">
    <PackageReference Include="Symbol.XamarinEMDK">
        <Version>8.0.0.2</Version>
    </PackageReference>
</ItemGroup>
After:

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0-android'">
    <PackageReference Include="Symbol.XamarinEMDK">
        <Version>8.0.0.2</Version>
    </PackageReference>
</ItemGroup>

The condition value is case sensitive and must exactly match the moniker used in <TargetFrameworks>. The Symbol.XamarinEMDK version number itself requires no change.


IV. Replace MainPage with CreateWindow in App.xaml.cs

Application.MainPage is marked obsolete in .NET 8+ (via warning CS0618). The correct replacement is to override CreateWindow(), which the MAUI framework calls when it needs to create the application window.

Open App.xaml.cs and modify as shown below:

Example

Before:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        MainPage = new AppShell();
    }
}
After:

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }
    protected override Window CreateWindow(IActivationState activationState)
    {
        return new Window(new AppShell());
    }
}

This change is needed because in .NET 8+, MAUI introduced multi-window support, but setting MainPage directly bypasses the windowing lifecycle and does not work correctly on all platforms. The CreateWindow() override is the supported entry point for constructing the initial page.


V. Update .csproj.user File (optional)

The .csproj.user file is developer-local option that stores IDE settings such as the last-selected debug target. It is typically excluded from source control, but if present, it might reference the old framework moniker and cause the wrong target to appear pre-selected in Visual Studio.

If you've opted to use this feature, ensure correct operation by locating the <ActiveDebugFramework> element and updating it as shown below:

Example

Before:

<ActiveDebugFramework>net6.0-android</ActiveDebugFramework>
After:

<ActiveDebugFramework>net10.0-android</ActiveDebugFramework>

NOTE: This has no effect on the build output; it affects only the Visual Studio debug toolbar default.


Build Verification

After completing all of the steps above, verify that the project is building properly using one of the methods below.

Verify Using .NET CLI:


dotnet build YourProject.csproj -f net10.0-android

Expected output:


> Build succeeded.
>     0 Warning(s)
>     0 Error(s)

Verify Using Visual Studio:

  1. Open your .sln file in Visual Studio 2022.
  2. Select Build > Rebuild Solution.
  3. Confirm the Output window shows 0 errors and 0 warnings.

Troubleshooting

MA002 — MAUI NuGet packages not included

Starting with .NET 8, setting <UseMaui>true</UseMaui> does not automatically include NuGet package references in your project.

The fix is to update your project by including: <PackageReference Include="Microsoft.Maui.Controls" ... />. See how to add the explicit Microsoft.Maui.Controls package reference described in Step II.


CS0618 — Application.MainPage is obsolete

  • Message: "‘Application.MainPage.set’ is obsolete: This property is deprecated. Initialize your application by overriding Application.CreateWindow rather than setting MainPage."
  • Fix: Replace the MainPage assignment with the CreateWindow() override described in Step IV.

CS8632 — Nullable annotation outside nullable context

  • Message: "The annotation for nullable reference types should only be used in code within a ‘#nullable’ annotations context."

  • Cause: Occurs if the IActivationState? parameter (with the ? nullable annotation) is used in a project that does not have <Nullable>enable</Nullable> enabled.

  • Fix: Remove "?" from the parameter type in CreateWindow():

    
    // Correct — no nullable annotation
    protected override Window CreateWindow(IActivationState activationState)
    

NOTE: Do not attempt to resolve by adding "<Nullable>enable</Nullable>" to the project. Doing so enables strict nullable analysis across all files and could produce a large number of new warnings in existing the EMDK sample code, which was not written with nullability in mind.


Symbol.XamarinEMDK not restored

If the Symbol.XamarinEMDK package fails to restore after a condition update, verify that the Condition value in the .csproj exactly matches the target framework moniker in <TargetFrameworks> as shown below:


<ItemGroup Condition="'$(TargetFramework)' == 'net10.0-android'">

Note that the condition is case sensitive. A mismatch such as Net10.0-Android or a leftover net6.0-android silently skips package restore for the target.