Start Debugging

Migration von Xamarin.Forms 5.0 zu .NET MAUI 11: die vollständige Checkliste

End-to-End-Migration von Xamarin.Forms 5.0 zu .NET MAUI 11 GA auf net11.0, einschließlich csproj-Umschreibung, Umwandlung von Custom Renderern in Handler, AppShell-Verkabelung, Entfernung von DependencyService, Rückzug von MessagingCenter, Resizetizer-Assets und den Fallstricken, die einen echten Produktionscode treffen.

Xamarin.Forms ist seit 2024-05-01 ohne Support, und Microsoft hat seither keinen einzigen Fix mehr veröffentlicht. .NET MAUI 11 ist das LTS-Landeziel für jede Xamarin.Forms-5.0-App, die auf geliehener Zeit lebt. Mit MAUI 11.0 GA im November 2025 verfügt die Plattform endlich über die Renderer-Infrastruktur, die RecyclerView-Performance und die iOS 18 / Android 15 Target-Unterstützung, die den frühen MAUI-Releases fehlten. Eine fokussierte Einzelentwickler-Migration einer mittelgroßen App, zehn bis zwanzig Bildschirme mit einer Handvoll Custom Renderern, dauert zwischen einer und drei Wochen. Die schwierigen Teile sind nicht XAML und nicht das Build-System; es sind Custom Renderer, DependencyService und jeder Code, der die Plattformprojekte direkt angefasst hat. Dieser Beitrag fixiert Xamarin.Forms 5.0.0.2622 als Quelle und .NET MAUI 11.0.0 auf net11.0 als Ziel, mit net11.0-android35.0, net11.0-ios18.0 und net11.0-maccatalyst18.0 als aktive TFMs.

Ein Rollback ist nicht trivial: Sobald die .csproj auf den SDK-Stil mit Microsoft.NET.Sdk und UseMaui=true umgestellt ist, gelangt man ohne Wiederherstellung der ursprünglichen csproj und packages.config aus der Versionskontrolle nicht zu einem Xamarin.Forms Shared Project zurück. Behandeln Sie die Migration als Einbahnstraße und planen Sie Ihre Branches entsprechend.

Warum jetzt migrieren

Was bricht

BereichÄnderungSchweregrad
ProjektlayoutShared Project plus drei Head-Projekte fallen in eine SDK-style csproj zusammenhoch
Xamarin.Forms-NamespaceErsetzt durch Microsoft.Maui.Controls, Microsoft.Maui.Graphics, etc.hoch
Custom RendererExportRenderer und IVisualElementRenderer entfernt. Verwenden Sie Handlerhoch
DependencyServiceEntfernt. Verwenden Sie Microsoft.Extensions.DependencyInjectionhoch
MessagingCenterVeraltet und zur Entfernung vorgesehen. Verwenden Sie CommunityToolkit.Mvvm IMessengerhoch
Application.PropertiesEntfernt. Verwenden Sie Microsoft.Maui.Storage.Preferencesmittel
ListViewWrapper über CollectionView. Migration empfohlen, siehe die ListView-zu-CollectionView-Anleitungmittel
MasterDetailPageUmbenannt in FlyoutPage. XAML muss aktualisiert werdenniedrig
FrameSanft veraltet. Verwenden Sie Border plus StrokeShapeniedrig
OpenGLViewVollständig entferntniedrig
Android MainActivityTeilt sich in MainActivity.cs plus MainApplication.cs, beide partialmittel
iOS AppDelegateErsetzt FormsApplicationDelegate durch MauiUIApplicationDelegatemittel
App.xaml.csApplication.MainPage funktioniert in MAUI 11, aber Shell-first ist neuniedrig
TargetingMonoAndroid12.0, Xamarin.iOS10 sind weg. Nur net11.0-android35.0hoch

Der Upgrade Assistant von Microsoft wird als dotnet tool ausgeliefert und deckt einen erheblichen Teil der Namespace-Umschreibungen und der Projektdatei-Konvertierung ab. Er behandelt keine Custom Renderer und keine DependencyService-Registrierungen, und genau dort geht die eigentliche Zeit hin.

Pre-Flight-Checkliste

  1. Installieren Sie das .NET 11 SDK und die MAUI-Workloads. Prüfen Sie mit dotnet workload list. Sie wollen maui, maui-android, maui-ios und maui-maccatalyst, alle in Version 11.0.x.

    # .NET 11.0
    dotnet workload install maui
    dotnet workload list
  2. Fixieren Sie das SDK in global.json, damit der Migrationsbranch mitten im PR nicht still nach vorn rollt:

    // global.json, repo root
    {
      "sdk": {
        "version": "11.0.100",
        "rollForward": "latestFeature"
      }
    }
  3. Taggen Sie den aktuellen Xamarin.Forms-Build in der Versionskontrolle. git tag pre-maui-migration reicht. Geht die Migration in Woche zwei schief, wollen Sie einen sauberen Restore-Punkt.

  4. Snapshotten Sie die Plattform-Assets. Gehen Sie die Drawable*-Ordner, Assets.xcassets, Splash-Storyboards und die Info.plist / AndroidManifest.xml-Einträge durch. Resizetizer reorganisiert diesen ganzen Baum, und Sie wollen ein Vorher-Inventar haben, falls ein Asset verloren geht.

  5. Inventarisieren Sie die DependencyService-Registrierungen. grep -rn "DependencyService\\|Dependency(typeof" . liefert die vollständige Liste. Jeder Eintrag wird zu einem services.AddSingleton- oder services.AddTransient-Aufruf.

  6. Inventarisieren Sie die Custom Renderer. Grep nach ExportRenderer und lesen Sie jeden einzeln. Einige können ersatzlos entfernt werden (die MAUI-Defaults sind besser als die Xamarin.Forms-Defaults), andere werden zu Handlern, andere zu Behaviors.

  7. Führen Sie dotnet test auf dem Xamarin.Forms-Baum aus und speichern Sie das grüne Baseline-Ergebnis. Eine Migration, die drei Test-Regressionen einführt, ist viel leichter zu diagnostizieren als eine, die auf einer Codebasis landet, die bereits fünf flaky Tests hatte.

Migrationsschritte

  1. Lassen Sie den Upgrade Assistant zuerst auf dem Shared Project laufen. Er schreibt die csproj auf SDK-Stil um, aktualisiert die offensichtlichsten Namespaces (Xamarin.FormsMicrosoft.Maui.Controls) und markiert die Renderer- und DependencyService-Stellen, die er nicht behandeln kann.

    # .NET 11
    dotnet tool install -g upgrade-assistant
    upgrade-assistant upgrade ./src/MyApp/MyApp.csproj --target-tfm net11.0

    Verifikation: dotnet restore ist auf der neuen csproj erfolgreich, und git status zeigt die erwarteten Umschreibungen. Lassen Sie ihn noch nicht auf den Head-Projekten laufen; die löschen Sie als Nächstes.

  2. Fassen Sie die Head-Projekte in einer einzigen SDK-style csproj zusammen. Xamarin.Forms wird als ein Shared Project plus MyApp.Android, MyApp.iOS und optional MyApp.UWP Head-Projekt ausgeliefert. MAUI wird als eine csproj mit Multi-Targeting ausgeliefert. Verschieben Sie Android-spezifischen Code unter Platforms/Android/, iOS-spezifischen Code unter Platforms/iOS/, und löschen Sie die Head-csproj-Dateien. Der neue csproj-Header sieht so aus:

    <!-- src/MyApp/MyApp.csproj, .NET MAUI 11, net11.0 -->
    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFrameworks>net11.0-android35.0;net11.0-ios18.0;net11.0-maccatalyst18.0</TargetFrameworks>
        <TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net11.0-windows10.0.19041.0</TargetFrameworks>
        <OutputType>Exe</OutputType>
        <UseMaui>true</UseMaui>
        <SingleProject>true</SingleProject>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <RootNamespace>MyApp</RootNamespace>
        <ApplicationId>net.mycompany.myapp</ApplicationId>
        <ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
        <ApplicationVersion>1</ApplicationVersion>
      </PropertyGroup>
    </Project>

    Verifikation: dotnet build -t:Restore ist erfolgreich, und das Projekt lädt in Visual Studio 2026 17.14 oder Rider 2026.1 ohne “unsupported project type”-Warnungen.

  3. Schreiben Sie App.xaml.cs so um, dass es MauiProgram.CreateMauiApp verwendet. Xamarin.Forms startete in MainActivity.OnCreate über LoadApplication(new App()). MAUI gibt das an den MauiAppBuilder weiter. Der Einstiegspunkt wird zu:

    // MauiProgram.cs, .NET MAUI 11, C# 14
    using Microsoft.Extensions.Logging;
    using Microsoft.Maui.Hosting;
    
    namespace MyApp;
    
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });
    
            builder.Services.AddSingleton<IAuthService, AuthService>();
            builder.Services.AddTransient<MainPageViewModel>();
    
            return builder.Build();
        }
    }

    Verifikation: dotnet build -f net11.0-android35.0 endet mit 0, und die IDE löst MauiProgram aus jedem Page-Konstruktor auf.

  4. Konvertieren Sie jede DependencyService-Registrierung zu IServiceCollection. Mechanisch, aber zwingend; DependencyService.Get<T>() ist in MAUI 11 weg. Der Ersatz:

    // Before, Xamarin.Forms 5.0
    DependencyService.Register<IAuthService, AuthService>();
    var auth = DependencyService.Get<IAuthService>();
    
    // After, .NET MAUI 11
    // Registration moves to MauiProgram.CreateMauiApp (step 3).
    // Resolution moves to the constructor or to IPlatformApplication.Current.Services.
    public partial class MainPage : ContentPage
    {
        public MainPage(IAuthService auth) // injected
        {
            InitializeComponent();
        }
    }

    Wo Constructor Injection unpraktisch ist (ein statischer Helper, ein AppShell-Handler), ist IPlatformApplication.Current!.Services.GetRequiredService<IAuthService>() die Notausstiegsluke.

    Verifikation: grep -rn "DependencyService" src/ liefert null Treffer. CI schlägt fehl, wenn das nicht so ist.

  5. Ersetzen Sie Custom Renderer durch Handler. Dies ist der zeitintensivste Schritt. Ein Xamarin.Forms Custom Renderer, der OnElementPropertyChanged überschrieb, wird zu einem MAUI-Handler mit einem Mapper-Dictionary. Beispiel: ein Renderer, der die Unterstreichung eines Android-Entry entfernt:

    // .NET MAUI 11, C# 14
    // Platforms/Android/EntryHandlerCustomization.cs
    using Microsoft.Maui.Handlers;
    
    public static class EntryHandlerCustomization
    {
        public static void Apply()
        {
            EntryHandler.Mapper.AppendToMapping("NoUnderline", (handler, entry) =>
            {
                handler.PlatformView.BackgroundTintList =
                    Android.Content.Res.ColorStateList.ValueOf(
                        Android.Graphics.Color.Transparent);
            });
        }
    }

    Registrieren Sie die Anpassung in MauiProgram.CreateMauiApp über builder.ConfigureMauiHandlers(...) oder rufen Sie Apply() aus einem partial void in MauiProgram heraus auf, damit sie nur unter Android läuft. Behalten Sie dasselbe Muster für iOS unter Platforms/iOS/.

    Verifikation: Jede Seite, die vom alten Renderer abhing, wird in dotnet build -t:Run -f net11.0-android35.0 korrekt gerendert. Visueller Smoke-Test, nicht nur ein Build-Pass.

  6. Ziehen Sie MessagingCenter zugunsten des CommunityToolkit-Messengers zurück. MessagingCenter ist in MAUI 11 als obsolet markiert und ist für die Entfernung in MAUI 12 vorgesehen. Übernehmen Sie CommunityToolkit.Mvvm 8.4.0 oder neuer:

    # .NET MAUI 11
    dotnet add package CommunityToolkit.Mvvm --version 8.4.0

    Der Musterwechsel:

    // Before, Xamarin.Forms 5.0
    MessagingCenter.Subscribe<LoginViewModel>(this, "LoggedIn", _ => RefreshUi());
    MessagingCenter.Send(this, "LoggedIn");
    
    // After, .NET MAUI 11 with CommunityToolkit.Mvvm 8.4.0
    public sealed record LoggedInMessage;
    WeakReferenceMessenger.Default.Register<LoggedInMessage>(this, (r, m) => RefreshUi());
    WeakReferenceMessenger.Default.Send(new LoggedInMessage());

    Der WeakReferenceMessenger vermeidet die Lebenszyklus-Bugs, die MessagingCenter in langlebigen Shell-Apps lecken ließen.

  7. Verschieben Sie Assets zu Resizetizer. Löschen Sie Resources/drawable-*, Assets.xcassets und die duplizierten Launch-Screens. Legen Sie eine appicon.svg und eine splash.svg unter Resources/AppIcon/ und Resources/Splash/ ab. Die csproj kennt sie bereits über die MauiIcon- und MauiSplashScreen-Items, die der Upgrade Assistant emittiert hat. Der Build-Zeit-Resize ersetzt die gesamte Dichteleiter.

    <!-- src/MyApp/MyApp.csproj fragment -->
    <ItemGroup>
      <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
      <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
      <MauiImage Include="Resources\Images\*" />
      <MauiFont Include="Resources\Fonts\*" />
    </ItemGroup>

    Verifikation: dotnet build -f net11.0-android35.0 erzeugt bin/Debug/net11.0-android35.0/Resources/drawable-xxhdpi/appicon.png, ohne dass Sie ihn handgemacht haben.

  8. Aktualisieren Sie Android MainActivity und MainApplication. Xamarin.Forms hatte eine MainActivity, die von FormsAppCompatActivity abgeleitet war. MAUI teilt das in MainActivity plus MainApplication:

    // Platforms/Android/MainActivity.cs, .NET MAUI 11
    using Android.App;
    using Android.Content.PM;
    using Android.OS;
    
    namespace MyApp;
    
    [Activity(Theme = "@style/Maui.SplashTheme",
              MainLauncher = true,
              ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
                                     ConfigChanges.UiMode | ConfigChanges.ScreenLayout |
                                     ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
    public class MainActivity : MauiAppCompatActivity { }
    
    // Platforms/Android/MainApplication.cs, .NET MAUI 11
    [Application]
    public class MainApplication : MauiApplication
    {
        public MainApplication(IntPtr handle, Android.Runtime.JniHandleOwnership ownership)
            : base(handle, ownership) { }
    
        protected override MauiApp CreateMauiApp() => MyApp.MauiProgram.CreateMauiApp();
    }

    Unter iOS ist das Äquivalent ein AppDelegate, das von MauiUIApplicationDelegate ableitet. Das Muster ist identisch: CreateMauiApp überschreiben und das gemeinsame MauiProgram aufrufen.

  9. Konvertieren Sie XAML-Namespaces. Jedes xmlns="http://xamarin.com/schemas/2014/forms" wird zu xmlns="http://schemas.microsoft.com/dotnet/2021/maui". Der Upgrade Assistant erledigt den Großteil, aber Dateien mit gemischten Namespaces (Custom-Control-Bibliotheken, die xamarin.toolkit einbezogen) brauchen einen manuellen Durchgang. Frame funktioniert noch eine Release weiter, erzeugt aber eine Build-Warnung. Planen Sie den Ersatz durch Border plus StrokeShape="RoundRectangle 12" und ein BackgroundColor. MasterDetailPage muss sowohl in XAML als auch im Code-Behind in FlyoutPage umbenannt werden, inklusive aller x:TypeArguments.

  10. Auditieren Sie Application.Properties zu Preferences. Jeder Code, der Application.Current.Properties["key"] = value schrieb, muss zu Preferences.Set("key", value) aus Microsoft.Maui.Storage wechseln. Die Form ist ähnlich, aber das Storage-Backend unterscheidet sich, weshalb Sie beim ersten Start eine einmalige Kopie brauchen können. Machen Sie die Kopie mit einem "migrated_to_preferences"-Flag idempotent, damit sie nicht erneut läuft.

  11. Fixieren Sie jede NuGet-Abhängigkeit auf eine MAUI-kompatible Version. Führen Sie dotnet list package --vulnerable und dotnet list package --outdated nach dem Upgrade aus. Übliche Verdächtige: Xamarin.Essentials (weg, in MAUI integriert), Xamarin.Forms.Maps (ersetzt durch Microsoft.Maui.Controls.Maps), Xamarin.Forms.Visual.Material (ersetzt durch die Material-3-Styles von MAUI, siehe den Beitrag zu Material 3 in MAUI 10).

Verifikation

Nach den obigen Schritten:

Rollback-Plan

Sobald die csproj umgeschrieben ist, gibt es kein automatisiertes Rollback. Der realistische Plan ist:

  1. Behalten Sie den pre-maui-migration-Git-Tag aus der Pre-Flight-Checkliste.
  2. Halten Sie die Migration auf ihrem eigenen Branch, bis die Verifikation auf Android und iOS grün ist.
  3. Müssen Sie nach dem Merge in main zurückgehen, ist der sichere Weg git revert des Merge-Commits, gefolgt von einer sauberen Wiederherstellung des Xamarin.Forms-Baums und einem Redeploy. Es gibt keinen In-Place-”Downgrade” einer SDK-style csproj zurück zum Legacy-Shared-Project-Layout.

Toleriert Ihr Release-Fenster keine Einbahn-Migration, veröffentlichen Sie den MAUI-Build als App mit paralleler ID (net.mycompany.myapp.maui) in den Stores für einen Zyklus, erreichen eine Crash-Free-Rate über 99.5 % auf Produktionsverkehr und wechseln dann die Bundle-ID mit einem erzwungenen Update.

Stolperfallen aus der Praxis

Verwandt

Quellen

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Zurück