Миграция от Xamarin.Forms към .NET MAUI: Пълно ръководство за 2026

Практическо ръководство за миграция от Xamarin.Forms към .NET MAUI 2026 с upgrade-assistant, handlers, single project и истински решения на често срещаните грешки.

Xamarin към .NET MAUI: Миграция 2026

Обновено: 28 май 2026

Миграцията от Xamarin.Forms към .NET MAUI означава прехвърляне на проекта към единичен .NET 9 SDK проект, замяна на custom renderers с handlers и пренастройване на dependency injection през MauiProgram.cs. Microsoft прекрати официалната поддръжка на Xamarin на 1 май 2024, което прави миграцията не просто желателна, а задължителна за всеки екип, който иска сигурностни ъпдейти, нови App Store изисквания и съвместимост с iOS 18/Android 15. Аз съм правил тази миграция в три production приложения (едно от тях с над 200 000 потребители) и в това ръководство ще намерите реалните проблеми, а не маркетинговата версия.

  • Xamarin.Forms официално излезе от поддръжка на 1 май 2024. App Store вече отхвърля компилации с по-стари версии на iOS таргети.
  • .NET upgrade-assistant автоматизира 60–70% от рутинната работа, но не докосва custom renderers, MessagingCenter и трети NuGet пакети.
  • .NET MAUI използва единичен проект (single project) вместо отделните iOS/Android проекти от Xamarin.Forms. Структурата на ресурсите се променя коренно.
  • Custom Renderers са deprecated; новата handler архитектура е по-бърза, но изисква пренаписване на всеки custom control.
  • Очаквайте между 2 и 8 седмици за средно приложение (50–100 екрана). Основното време отива не в кода, а в third-party NuGet пакетите.
  • За приложения с тежък custom UI преоценете дали .NET MAUI Blazor Hybrid не е по-подходящ.

Защо миграцията към .NET MAUI вече не е по избор

Xamarin.Forms получи последния си servicing update на 1 май 2024 и от този ден нататък няма да получи нито security patches, нито поправки за нови версии на iOS и Android. На практика това означава три конкретни проблема за production екипите.

Първо, App Store Connect вече показва предупреждения и от началото на 2026 отхвърля компилации, базирани на Xamarin.iOS, които не са свързани с .NET 8+ SDK. Второ, Google Play изисква targetSdkVersion 35 (Android 15) за нови releases от август 2025, а Xamarin.Android спира на API 34. Трето (и това е скритият проблем), голяма част от NuGet пакетите вече публикуват само net8.0-ios / net9.0-android таргети и не качват нови версии за monoandroid и xamarinios10.

Честно казано, аз държа продукти на .NET MAUI чак от 8.0.40 нататък. До 8.0.40 имах сериозни оплаквания. HotReload беше счупен, CollectionView memory leaks бяха ежедневие. От .NET 9 (ноември 2024) и .NET 10 preview-тата за 2026, ситуацията е друга: миграцията вече е практична, не теоретична.

Предварителна оценка на проекта

Преди да докоснете dotnet tool install, направете честен инвентар на проекта. Това е стъпката, която най-често пропускам сам и винаги съжалявам. Конкретно ме интересуват пет неща:

  1. Брой и сложност на Custom Renderers. Преброй ги. За всеки renderer ще пренапишеш от 30 до 200 реда.
  2. Effects. Effects в Xamarin.Forms нямат пряк еквивалент. Мигрират се към handler mappings или behaviors.
  3. Трети NuGet пакети. Извади packages.config или .csproj PackageReference списъка. За всеки пакет провери дали има .NET MAUI версия. Ако няма, има ли активна fork-а? Пакетите без активна поддръжка са най-голямата причина миграциите да отнемат месеци.
  4. Платформено-специфичен код. Колко неща минават през DependencyService? Тези ще пренаписваш през новия generic host DI контейнер.
  5. Шрифтове, икони и изображения. Resource структурата е изцяло нова. Всеки иконен SVG и шрифтов файл се регистрира декларативно в MauiProgram.

За малък проект (≤20 екрана, ≤5 custom renderers) очаквайте 1–2 седмици. Среден проект, около 4–6 седмици. За legacy enterprise приложения с MessagingCenter навсякъде и десетки renderers, миграцията спокойно отива в три месеца на пълен ден работа.

Стъпка 1: Как работи .NET upgrade-assistant

Microsoft поддържат CLI инструмент, който прави автоматизиран анализ и значителна част от рутинното преобразуване. Инсталация и стартиране:

dotnet tool install -g upgrade-assistant
cd MyXamarinSolution
upgrade-assistant upgrade MyApp.sln --non-interactive

Инструментът прави следното автоматично: преобразува .csproj файла от стария XML формат към SDK-style, актуализира таргет framework към net9.0-android / net9.0-ios, заменя известни namespace mappings (Xamarin.Forms към Microsoft.Maui.Controls) и добавя стандартни MauiProgram.cs и App.xaml.cs entry points.

Това, което не прави: не пренаписва custom renderers, не премахва MessagingCenter, не подменя третите NuGet пакети, не движи ресурси в новата single project структура и не докосва XAML-specific compatibility issues (например Frame, който вече се препоръчва да се замени с Border).

Очаквайте първият build след upgrade-assistant да има между 80 и 500 грешки. Това е нормално и не означава, че инструментът е счупен. Прочетете подробните официални Microsoft Learn инструкции за upgrade-assistant, защото логът след upgrade обикновено съдържа списък със todo-та, които трябва да адресирате ръчно.

Стъпка 2: Преструктуриране в single project

Това е концептуалната смяна, която обърква най-много екипи. В Xamarin.Forms имахте обичайно три проекта: MyApp (shared), MyApp.Android, MyApp.iOS. В .NET MAUI имате един проект с multi-targeting, който компилира за всички платформи едновременно.

Новата структура изглежда така:

MyApp/
├── Platforms/
│   ├── Android/
│   │   ├── MainActivity.cs
│   │   └── AndroidManifest.xml
│   ├── iOS/
│   │   ├── AppDelegate.cs
│   │   └── Info.plist
│   ├── MacCatalyst/
│   └── Windows/
├── Resources/
│   ├── Fonts/
│   ├── Images/
│   ├── Raw/
│   └── Styles/
├── Views/
├── ViewModels/
├── MauiProgram.cs
└── App.xaml

Платформените файлове отиват в Platforms/. Иконите и шрифтовете отиват в Resources/ и се регистрират декларативно. Без повече ръчни Asset Catalogs за iOS или drawable папки за Android по различни DPI:

// MauiProgram.cs
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder
        .UseMauiApp<App>()
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            fonts.AddFont("FontAwesome.ttf", "FontAwesome");
        });

    return builder.Build();
}

SVG файлите се компилират на build time към платформено-нативни формати (PNG за Android при различни DPI, PDF за iOS). Това е една от наистина добре проектираните части на MAUI. Един SVG в Resources/Images/ заменя 12–15 файла, които поддържахте на ръка в Xamarin.

Стъпка 3: От Custom Renderers към Handlers

Това е най-сериозната архитектурна промяна. Custom Renderers все още работят през compatibility shim (Microsoft.Maui.Controls.Compatibility), но препоръчвам да не разчитате на тях. Те носят measurable performance penalty и Microsoft са недвусмислени, че ще ги премахнат в бъдеща версия.

Сравнение на двата подхода:

АспектCustom Renderer (Xamarin)Handler (MAUI)
НаследяванеЦяло наследяване на ViewRendererMapper pattern (без наследяване)
ПроизводителностAllocation при всяка промянаДо 30% по-бързо рендериране
Достъп до native viewControl / Element propertiesPlatformView / VirtualView
РегистрацияAssembly attributeConfigureMauiHandlers
ТестваемостСлаба (изисква platform)По-добра (mappers са функции)

Ето пример. Стария Xamarin custom renderer за Entry, който премахва underline-а на Android:

// СТАР Xamarin начин
[assembly: ExportRenderer(typeof(Entry), typeof(NoUnderlineEntryRenderer))]
public class NoUnderlineEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);
        if (Control != null)
            Control.Background = null;
    }
}

Същото в MAUI handler стилистика, без никакво наследяване, само регистрация на mapping:

// НОВ MAUI начин — в MauiProgram.cs
#if ANDROID
Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
    "NoUnderline",
    (handler, view) =>
    {
        handler.PlatformView.BackgroundTintList =
            Android.Content.Res.ColorStateList.ValueOf(
                Android.Graphics.Color.Transparent);
    });
#endif

Mapper pattern-ът работи по идиоматичен начин: декларирате какво да се случи, когато конкретно property се промени. Няма livecycle методи за override, няма базови класове. Това опростява test-овете и намалява allocation-ите. За пълен списък с handlers и mappers вижте официалната документация за MAUI handlers.

Стъпка 4: Dependency injection през MauiProgram

В Xamarin.Forms голяма част от екипите си правеха собствен service locator (Autofac, TinyIoC, или DIY DependencyService). .NET MAUI идва с вграден Microsoft.Extensions.DependencyInjection контейнер, същият от ASP.NET Core. Това е огромно подобрение и една от причините новата платформа да е по-приятна за работа.

public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder.UseMauiApp<App>();

    // Services
    builder.Services.AddSingleton<IAuthService, AuthService>();
    builder.Services.AddSingleton<IApiClient, ApiClient>();
    builder.Services.AddTransient<ILogger, FileLogger>();

    // ViewModels
    builder.Services.AddTransient<HomeViewModel>();
    builder.Services.AddTransient<ProductDetailViewModel>();

    // Pages
    builder.Services.AddTransient<HomePage>();
    builder.Services.AddTransient<ProductDetailPage>();

    // HttpClient с Polly retry policy
    builder.Services.AddHttpClient<IProductApi, ProductApi>()
        .AddStandardResilienceHandler();

    return builder.Build();
}

Page и ViewModel resolve-ват се през constructor injection, точно както в ASP.NET Core. Това прави unit testing-а тривиален и премахва нуждата от static App.Current.Container hacks. Ако правите HTTP комуникация в приложението, прочетете и подробното ръководство за REST API с HttpClient и Refit в .NET MAUI. Там покривам как се конфигурират resilience policies.

Стъпка 5: Замяна на MessagingCenter

MessagingCenter е deprecated в .NET MAUI и аз бих го нарекъл по-точно: винаги е бил архитектурен дълг. Слабо типизиран, лесно се изтичат subscriptions и създава скрити dependencies между компоненти. Препоръчителната замяна е WeakReferenceMessenger от CommunityToolkit.Mvvm.

// Инсталиране
// dotnet add package CommunityToolkit.Mvvm

// Дефиниция на съобщение (record е достатъчен)
public record CartUpdatedMessage(int ItemCount);

// Изпращане
WeakReferenceMessenger.Default.Send(new CartUpdatedMessage(5));

// Получаване (обикновено в ViewModel конструктор)
WeakReferenceMessenger.Default.Register<CartUpdatedMessage>(this, (r, m) =>
{
    BadgeCount = m.ItemCount;
});

Ключовата дума Weak е важна. Съобщенията се държат с weak references и subscription-ите се чистят автоматично при GC. Това решава най-честия leak в Xamarin приложения, където хората забравяха Unsubscribe.

Често срещани грешки при миграция и как се решават

Грешка: "Resource not found" за изображения

В Xamarin.Forms изображенията се добавяха като EmbeddedResource или в Asset Catalog. В MAUI трябва да са в Resources/Images/ с Build Action MauiImage. След преместване, изчистете bin/ и obj/ папките. Incremental builds често пазят counts към старите пътища.

Грешка: SQLite база данни не се намира на iOS

Pre-MAUI обичайно ползвахте Environment.GetFolderPath(SpecialFolder.LocalApplicationData). На iOS под MAUI това понякога връща празен низ при cold start. Замяна:

var dbPath = Path.Combine(FileSystem.AppDataDirectory, "app.db3");

Грешка: CollectionView не показва нищо

Често срещам това, когато ItemTemplate е дефиниран с x:DataType, но bindings са към properties, които не са public. Compiled bindings в MAUI 9 са по-строги от Xamarin. Всички binding source members трябва да са public.

Грешка: Splash screen не се променя

Splash screen-ът сега се дефинира в .csproj като MauiSplashScreen и трябва да е SVG. Старите LaunchScreen.storyboard файлове се игнорират. Тествайте на чисто install. Android особено агресивно кешира стария splash.

Тестване, достъпност и публикуване

След като приложението компилира, не считайте миграцията за приключила. Има три неща, които винаги проверявам, преди да push-на към main:

1. Memory profiling. Стартирайте dotnet-trace или Visual Studio Memory Profiler върху основните екрани с навигация напред-назад 20 пъти. CollectionView в ранните версии на MAUI имаше leaks; в .NET 9 повечето са решени, но при custom adapters още се появяват.

2. Достъпност. Преминаването към handlers често чупи SemanticProperties bindings. TalkBack и VoiceOver престават да четат поле, което преди са четяли. Минете всеки екран с включен screen reader. Имам отделно ръководство за достъпност в .NET MAUI и WCAG 2.2 съвместимост.

3. Build size. След миграция Android APK обикновено става с 4–8 MB по-голям заради новите runtime зависимости. Включете $(PublishTrimmed) и $(RunAOTCompilation) в Release конфигурацията. Премахват 20–30% от размера.

За публикуване в App Store и Google Play от 2026 нататък няма съществени промени спрямо последните Xamarin.iOS releases. Apple вече изисква Xcode 16+ за submissions, което означава, че build агентът ви (Azure DevOps, GitHub Actions) трябва да върви на macOS 14+ image. Прегледайте официалните .NET MAUI release notes на GitHub за последните compatibility матрици.

Често задавани въпроси

Колко време отнема миграция от Xamarin.Forms към .NET MAUI?

За малък проект (под 20 екрана), 1 до 2 седмици. За среден production проект с 50–100 екрана и няколко custom renderers, 4 до 8 седмици. Основното време не отива в кода, а в проверка и замяна на трети NuGet пакети, които не са били актуализирани за .NET MAUI.

Мога ли да продължа да използвам Xamarin.Forms след май 2024?

Технически, да, приложението ви ще работи. Практически, не. Няма да получавате security patches, App Store ще започне да отхвърля компилации, а нови версии на iOS и Android няма да имат тествана поддръжка. Това е техническо дълго и нарастващ риск.

Работи ли .NET upgrade-assistant за всеки Xamarin проект?

Инструментът покрива стандартните Xamarin.Forms 5.0 проекти. Ако имате Xamarin.Forms 4.x или Xamarin Classic (без Forms), първо трябва да минете през ъпдейт до 5.0. Custom build системи и Cake/FAKE скриптове изискват ръчна корекция.

Трябва ли да пренапиша всички Custom Renderers веднага?

Не. Microsoft.Maui.Controls.Compatibility ви позволява да оставите renderers за известно време. Но новите функции и performance improvements са свързани с handlers, така че разпределете пренаписването в спринтове. Аз правя по 2–3 renderer-а на седмица в продължение на месец.

Кое е по-добре, .NET MAUI или MAUI Blazor Hybrid?

Зависи от UI сложността. Стандартен MAUI е по-бърз за приложения с native feel. Blazor Hybrid е по-удобен, ако екипът ви има web опит или приложението има тежки form-heavy екрани, които и без това приличат на web интерфейс. Не са взаимно изключващи се, могат да съществуват в едно приложение.

Каква версия на .NET да таргетирам през 2026?

.NET 9 е LTS-кандидат и е стабилен за production през 2026. .NET 10 preview-та са обещаващи, но за commercial приложения изчакайте GA release. Минимална таргет версия за нови проекти: net9.0-android и net9.0-ios18.0.

Marcus Chen
За Автора Marcus Chen

Senior mobile architect with a decade of cross-platform experience. Spent the last five years going deep on .NET MAUI in production.