.NET MAUI w 2026: Samouczek programowania cross-platform i pytania rekrutacyjne

Samouczek .NET MAUI obejmujący programowanie cross-platform z .NET 10, handlery, MVVM, HybridWebView i kluczowe pytania rekrutacyjne na 2026 rok.

.NET MAUI cross-platform development tutorial 2026

.NET MAUI (Multi-platform App UI) osiągnął pełną dojrzałość produkcyjną wraz z wydaniem .NET 10. Jako wersja z długoterminowym wsparciem (LTS, obsługiwana do listopada 2028 roku), .NET MAUI 10 jest dystrybuowany jako workload oraz pakiety NuGet, dostarczając ulepszoną jakość, wydajność oraz nowe API takie jak rozszerzenia HybridWebView i SafeAreaEdges. Ten artykuł przedstawia praktyczny przewodnik po tworzeniu aplikacji cross-platform, omawia architekturę frameworka oraz kluczowe pytania pojawiające się na rozmowach kwalifikacyjnych w 2026 roku.

.NET MAUI 10 LTS

.NET 10 to wydanie z długoterminowym wsparciem (do listopada 2028). MAUI 10 kładzie nacisk na stabilność i wydajność zamiast nowych kontrolek UI, co czyni go najbardziej stabilnym wydaniem MAUI. Jest teraz dystrybuowany jako workload .NET oraz pakiety NuGet, umożliwiając przypinanie wersji per projekt.

Konfiguracja projektu .NET MAUI 10 od podstaw

Najszybszą drogą do uruchomienia aplikacji MAUI jest wykorzystanie .NET CLI. .NET 10 wprowadza zaktualizowany szablon projektu, który zawiera domyślne ustawienia .NET Aspire, zapewniając telemetrię i odkrywanie usług.

bash
# Install the MAUI workload (if not already present)
dotnet workload install maui

# Create a new MAUI app
dotnet new maui -n CrossPlatformDemo
cd CrossPlatformDemo

# Run on Android emulator
dotnet build -t:Run -f net10.0-android

Struktura pojedynczego projektu konsoliduje kod specyficzny dla platform w katalogu Platforms/, współdzieląc resztę. Plik MauiProgram.cs pełni rolę composition root, gdzie rejestruje się serwisy, czcionki i handlery.

MauiProgram.cscsharp
using Microsoft.Extensions.Logging;

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

        // Register services for dependency injection
        builder.Services.AddSingleton<IApiService, ApiService>();
        builder.Services.AddTransient<MainViewModel>();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

Wstrzykiwanie zależności w MAUI stosuje ten sam wzorzec co ASP.NET Core. Serwisy Singleton utrzymują się przez cały czas życia aplikacji, serwisy Transient są tworzone przy każdym żądaniu, natomiast serwisy Scoped wymagają ostrożności, ponieważ MAUI nie posiada wbudowanej koncepcji zakresu jak żądania HTTP.

Handlery: architektura renderowania cross-platform

MAUI zastąpiło renderery Xamarin.Forms architekturą handlerów. Handlery mapują każdy kontroler cross-platform na jego natywny odpowiednik poprzez cienką warstwę abstrakcji. Kluczowa różnica: handlery są bezstanowe i oddzielone od widoku wirtualnego, co czyni je szybszymi i łatwiejszymi do dostosowania.

CustomEntryHandler.cscsharp
using Microsoft.Maui.Handlers;

public class CustomEntryHandler : EntryHandler
{
    protected override void ConnectHandler(MauiAppCompatEditText platformView)
    {
        base.ConnectHandler(platformView);
        // Remove the default underline on Android
        platformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
    }
}

// Register in MauiProgram.cs
builder.ConfigureMauiHandlers(handlers =>
{
    handlers.AddHandler<Entry, CustomEntryHandler>();
});

W .NET 10 kontrolki Entry i Editor na Androidzie przeszły z AppCompatEditText na MauiAppCompatEditText, dodając natywne wsparcie dla zdarzenia SelectionChanged. Ulepszone handlery CollectionView i CarouselView wprowadzone w .NET 9 są teraz domyślne na iOS i Mac Catalyst, rozwiązując długotrwałe problemy ze stabilnością.

Android 16 Edge-to-Edge: przełomowa zmiana i wymagana poprawka

Android 16 usuwa mechanizm rezygnacji z wyświetlania edge-to-edge. Aplikacje targetujące API 36 muszą prawidłowo obsługiwać wcięcia pasków systemowych, w przeciwnym razie zawartość renderuje się niepoprawnie. .NET 10 wprowadził cichą przełomową zmianę: w .NET 9 ContentPage domyślnie respektował paski systemowe (zachowanie Container), ale .NET 10 domyślnie stosuje edge-to-edge (None).

Przełomowa zmiana w .NET 10

Aplikacje migrujące z .NET 9 do .NET 10 mogą zauważyć, że zawartość renderuje się pod paskiem statusu lub paskiem nawigacji na Androidzie 15+. Rozwiązanie wymaga jawnego ustawienia SafeAreaEdges w celu przywrócenia poprzedniego zachowania.

Rozwiązanie polega na dodaniu globalnego niejawnego stylu do App.xaml:

xml
<!-- App.xaml -->
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CrossPlatformDemo.App">
    <Application.Resources>
        <ResourceDictionary>
            <Style TargetType="ContentPage" ApplyToDerivedTypes="True">
                <Setter Property="SafeAreaEdges" Value="Container" />
            </Style>
        </ResourceDictionary>
    </Application.Resources>
</Application>

Enum SafeAreaEdges zapewnia granularną kontrolę:

  • None: zawartość edge-to-edge bez paddingu safe area
  • SoftInput: zawsze dodaj padding dla klawiatury
  • Container: płynie pod klawiaturą, unika pasków i wycięcia
  • Default: domyślne zachowanie platformy
  • All: respektuje wszystkie wcięcia safe area

To podejście wykorzystuje Window Insets API w runtime, co sprawia, że jest kompatybilne z Androidem 16, gdzie rezygnacje na poziomie manifestu już nie działają.

MVVM z CommunityToolkit.Mvvm: eliminacja kodu boilerplate

Generator kodu źródłowego CommunityToolkit.Mvvm eliminuje około 80% ceremonii MVVM. Koniec z ręczną implementacją INotifyPropertyChanged, koniec z wrapperami komend: atrybuty sterują generowaniem kodu.

MainViewModel.cscsharp
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class MainViewModel : ObservableObject
{
    private readonly IApiService _apiService;

    public MainViewModel(IApiService apiService)
    {
        _apiService = apiService;
    }

    // Source generator creates the 'Title' property with change notification
    [ObservableProperty]
    private string _title = string.Empty;

    // Source generator creates the 'IsLoading' property
    [ObservableProperty]
    private bool _isLoading;

    // Source generator creates an async ICommand
    [RelayCommand]
    private async Task LoadDataAsync()
    {
        IsLoading = true;
        try
        {
            Title = await _apiService.FetchTitleAsync();
        }
        finally
        {
            IsLoading = false;
        }
    }
}

XAML wiąże się bezpośrednio z wygenerowanymi właściwościami i komendami:

xml
<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vm="clr-namespace:CrossPlatformDemo.ViewModels"
             x:DataType="vm:MainViewModel">
    <VerticalStackLayout Padding="20" Spacing="16">
        <Label Text="{Binding Title}"
               FontSize="24"
               HorizontalOptions="Center" />
        <Button Text="Load Data"
                Command="{Binding LoadDataCommand}"
                IsEnabled="{Binding IsLoading, Converter={StaticResource InverseBoolConverter}}" />
        <ActivityIndicator IsRunning="{Binding IsLoading}"
                           IsVisible="{Binding IsLoading}" />
    </VerticalStackLayout>
</ContentPage>

Atrybut x:DataType włącza kompilowane bindowania, które są szybsze od bindowań opartych na refleksji i generują błędy kompilacji, gdy ścieżka bindowania jest nieprawidłowa.

Gotowy na rozmowy o .NET?

Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.

Generator XAML Source w .NET 10

.NET MAUI 10 wprowadza generator XAML source, który kompiluje XAML w czasie budowania zamiast parsować go w runtime. Poprawia to wydajność uruchamiania i wyłapuje błędy XAML podczas kompilacji.

Aby włączyć generowanie XAML source, należy dodać właściwość do pliku projektu:

xml
<PropertyGroup>
  <MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>

.NET 10 Preview 5 wprowadził również niejawne przestrzenie nazw XML, które eliminują szablonowe deklaracje xmlns. Plik GlobalXmlns.cs może agregować wiele przestrzeni nazw:

GlobalXmlns.cscsharp
[assembly: XmlnsDefinition(
    "http://schemas.microsoft.com/dotnet/maui/global",
    "MyApp.Views")]
[assembly: XmlnsDefinition(
    "http://schemas.microsoft.com/dotnet/maui/global",
    "MyApp.Controls")]

Z tą konfiguracją pliki XAML stają się czystsze:

xml
<!-- Przed -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:controls="clr-namespace:MyApp.Controls"
        x:Class="MyApp.MainPage">
    <controls:TagView />
</ContentPage>

<!-- Po (z globalnym xmlns) -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/maui/global"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.MainPage">
    <TagView />
</ContentPage>

HybridWebView w .NET 10: łączenie natywnego z webowym

HybridWebView umożliwia osadzanie treści webowych wewnątrz aplikacji MAUI przy zachowaniu dwukierunkowej komunikacji między C# a JavaScript. .NET 10 dodaje trzy możliwości: wywołania JavaScript typu fire-and-forget, zdarzenia inicjalizacji dla konfiguracji specyficznej dla platformy oraz przechwytywanie żądań webowych.

MainPage.xaml.cscsharp
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();

        // Initialization event for platform-specific tweaks
        hybridWebView.WebViewInitialized += (sender, args) =>
        {
            System.Diagnostics.Debug.WriteLine("WebView ready");
        };
    }

    // Call JavaScript from C#
    private async void OnCallJsClicked(object sender, EventArgs e)
    {
        var result = await hybridWebView.InvokeJavaScriptAsync<string>(
            "getFormData",
            HybridSampleContext.Default.String
        );
        await DisplayAlertAsync("Result", result, "OK");
    }

    // Fire-and-forget: no return type needed (.NET 10)
    private async void OnResetClicked(object sender, EventArgs e)
    {
        await hybridWebView.InvokeJavaScriptAsync("resetForm");
    }
}

Wyjątki JavaScript rzucane podczas InvokeJavaScriptAsync są teraz automatycznie przekazywane do .NET jako wyjątki, eliminując ciche awarie. Przechwytywanie żądań webowych pozwala modyfikować nagłówki lub dostarczać lokalne odpowiedzi:

csharp
webView.WebResourceRequested += (s, e) =>
{
    if (e.Uri.ToString().Contains("api/secure"))
    {
        e.Handled = true;
        e.SetResponse(200, "OK", "application/json", GetCustomStream());
    }
};

Przestarzałe API i ścieżka migracji

.NET MAUI 10 oznacza kilka API jako przestarzałe. Aplikacje używające tych elementów powinny migrować do zalecanych alternatyw.

ListView i powiązane komórki: ListView, EntryCell, ImageCell, SwitchCell, TextCell i ViewCell są przestarzałe. Należy używać CollectionView, który ma lepszą wydajność i stabilność z nowymi handlerami iOS/Mac Catalyst.

MessagingCenter: Oznaczony jako wewnętrzny w .NET 10. Należy zastąpić WeakReferenceMessenger z CommunityToolkit.Mvvm.

Metody animacji: FadeTo, RotateTo, ScaleTo i podobne metody są przestarzałe. Należy używać asynchronicznych odpowiedników: FadeToAsync, RotateToAsync, ScaleToAsync.

TableView: Przestarzały na rzecz CollectionView.

DisplayAlert/DisplayActionSheet: Zastąpione przez DisplayAlertAsync i DisplayActionSheetAsync.

Migracja z Xamarin.Forms do .NET MAUI

Wsparcie dla Xamarin.Forms zakończyło się w maju 2024 roku. Migracja do MAUI wymaga zmian strukturalnych wykraczających poza prostą zamianę przestrzeni nazw. Oto lista kontrolna migracji oparta na rzeczywistych konwersjach projektów:

Koniec wsparcia Xamarin

Xamarin.Forms nie jest wspierany od maja 2024 roku. Aplikacje nadal działające na Xamarin niosą ze sobą ryzyko bezpieczeństwa i kompatybilności. .NET MAUI 10 (LTS, wsparcie do listopada 2028) jest wyznaczonym celem migracji.

  1. Struktura projektu: Konwersja z projektów specyficznych dla platform do modelu pojedynczego projektu MAUI. Przeniesienie współdzielonego kodu do katalogu głównego, kodu platformowego do Platforms/.
  2. Przestrzenie nazw: Zamiana Xamarin.Forms na Microsoft.Maui.Controls i Xamarin.Essentials na Microsoft.Maui.Essentials (teraz wbudowane w MAUI).
  3. Renderery na handlery: Niestandardowe renderery muszą zostać przepisane jako handlery. API handlerów jest prostsze, ale logika mapowania różni się.
  4. Inicjalizacja: Zastąpienie inicjalizacji App.xaml.cs wzorcem builder w MauiProgram.cs.
  5. Pakiety NuGet: Wiele pakietów z ery Xamarin ma odpowiedniki MAUI. Należy zweryfikować kompatybilność przed aktualizacją.
  6. Wstrzykiwanie zależności: MAUI natywnie używa Microsoft.Extensions.DependencyInjection. Należy zastąpić wszystkie zewnętrzne kontenery DI lub wywołania DependencyService.

.NET Upgrade Assistant automatyzuje części kroków 1-2, ale handlery (krok 3) i dostosowania logiki biznesowej wymagają ręcznej pracy.

Najważniejsze pytania rekrutacyjne .NET MAUI na 2026 rok

Poniższe pytania odzwierciedlają to, o co pytają zespoły rekrutacyjne w 2026 roku, bazując na obecnym ekosystemie .NET 10. Więcej przygotowania do rozmów z C# i .NET można znaleźć w kompletnym przewodniku po rozmowach .NET.

Czym architektura handlerów MAUI różni się od rendererów Xamarin.Forms?

Renderery w Xamarin.Forms były ściśle powiązane zarówno z kontrolką cross-platform, jak i widokiem natywnym, tworząc dwukierunkową zależność. Handlery w MAUI to bezstanowe mappery: otrzymują powiadomienia o zmianach właściwości i stosują je do widoku natywnego poprzez słownik mapperów. To oddzielenie sprawia, że handlery są łatwiejsze do testowania, rozszerzania i ponownego użycia. Słowniki PropertyMapper i CommandMapper zastępują wzorzec nadpisywania OnElementPropertyChanged, czyniąc dostosowywanie jawnym zamiast ukrytym w instrukcjach switch.

Jakie są pułapki związane z czasem życia DI specyficzne dla MAUI?

MAUI obsługuje czasy życia Singleton, Transient i Scoped, ale Scoped zachowuje się inaczej niż w ASP.NET Core. Nie ma naturalnej granicy zakresu (jak żądanie HTTP). Serwis Scoped zarejestrowany w MAUI działa jak Singleton, chyba że ręcznie tworzone są niestandardowe zakresy. Typowe błędy: rejestrowanie ViewModelu jako Singleton, gdy przechowuje stan specyficzny dla strony (nieaktualne dane przy nawigacji), lub rejestrowanie połączenia z bazą danych jako Transient (wyczerpanie puli połączeń). Zasada: ViewModele to Transient, serwisy to Singleton, a Scoped jest unikany, chyba że cykl życia zakresu jest zarządzany jawnie.

Wskazówka rekrutacyjna

Odpowiadając na pytania o DI, należy wykazać zrozumienie, czym cykl życia MAUI różni się od modelu request-scoped ASP.NET Core. Rekruterzy szukają świadomości wycieków pamięci i problemów z przestarzałym stanem specyficznych dla długo działających aplikacji mobilnych.

Czym kompilowane bindowanie różni się od bindowania opartego na refleksji w MAUI?

Bindowania oparte na refleksji rozwiązują ścieżki właściwości w runtime przy użyciu System.Reflection, co jest wolne i generuje błędy runtime przy literówkach. Kompilowane bindowania, włączane za pomocą x:DataType, rozwiązują ścieżki bindowań w czasie kompilacji. Kompilator generuje bezpośredni kod dostępu do właściwości, pomijając refleksję. Poprawia to czas uruchomienia, redukuje alokacje pamięci i wychwytuje błędy bindowań podczas budowania. W .NET 10 nowy generator XAML source dodatkowo optymalizuje to, kompilując XAML w czasie budowania zamiast parsować go w runtime.

Jakie strategie istnieją do współdzielenia kodu między aplikacją MAUI a backendem ASP.NET Core?

Zalecane podejście wykorzystuje współdzieloną bibliotekę klas zawierającą DTO, logikę walidacji i reguły biznesowe. Zarówno aplikacja MAUI, jak i backend ASP.NET Core referencjonują tę bibliotekę. .NET 10 wzmacnia ten wzorzec integracją .NET Aspire dla MAUI, zapewniającą odkrywanie usług i telemetrię między projektami mobilnymi i backendowymi. Współdzielone kontrakty używające generatorów System.Text.Json zapewniają spójność serializacji. Kluczowe ograniczenie: współdzielona biblioteka musi targetować net10.0 (nie TFM specyficzne dla platformy), aby pozostać przenośna.

Czym HybridWebView różni się od BlazorWebView w MAUI?

BlazorWebView hostuje pełną aplikację Blazor wewnątrz aplikacji MAUI. Komponenty Razor renderują się w osadzonym WebView, ale runtime .NET wykonuje się natywnie (nie przez WebAssembly). HybridWebView jest lżejszy: ładuje statyczną zawartość HTML/CSS/JS i zapewnia interop C#-JavaScript bez narzutu frameworka Blazor. Wybór zależy od przypadku użycia. BlazorWebView pasuje do zespołów z istniejącymi komponentami Blazor, które chcą ponownego użycia kodu. HybridWebView pasuje do scenariuszy, w których istniejąca zawartość webowa (dashboardy, mapy, edytory) wymaga natywnej integracji bez pełnego frameworka.

Ćwicz więcej pytań rekrutacyjnych .NET na SharpSkill, aby utrwalić te koncepcje interaktywnymi ćwiczeniami.

Zacznij ćwiczyć!

Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.

Źródła

Co powinni pamiętać programiści .NET MAUI 10

  • .NET MAUI 10 to wydanie LTS (wsparcie do listopada 2028) skoncentrowane na stabilności i wydajności, co czyni go niezawodnym wyborem dla produkcyjnych aplikacji cross-platform
  • Android 16 wymusza edge-to-edge: należy dodać SafeAreaEdges="Container" jako globalny styl w App.xaml, aby przywrócić zachowanie .NET 9
  • Generator XAML source (MauiXamlInflator=SourceGen) kompiluje XAML w czasie budowania, poprawiając wydajność uruchamiania i wcześniej wyłapując błędy
  • ListView, TableView i MessagingCenter są przestarzałe: należy migrować do CollectionView i WeakReferenceMessenger
  • Architektura handlerów zastępuje renderery Xamarin bezstanowymi mapperami, poprawiając testowalność poprzez PropertyMapper i CommandMapper
  • Generatory kodu źródłowego CommunityToolkit.Mvvm usuwają większość kodu boilerplate MVVM: atrybuty [ObservableProperty] i [RelayCommand] zastępują ręczne implementacje
  • HybridWebView w .NET 10 dodaje wywołania JavaScript fire-and-forget, zdarzenia inicjalizacji i przechwytywanie żądań dla integracji natywno-webowej
  • Przygotowanie do rozmów kwalifikacyjnych powinno koncentrować się na architekturze handler vs. renderer, pułapkach DI w długo działających aplikacjach, kompilowanych bindowaniach oraz kompromisach HybridWebView vs. BlazorWebView
Wyzwanie dnia

Znajdziesz błąd w .NET?

Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Anthony Fillion-Maillet

Autor:

Anthony Fillion-Maillet

Założyciel SharpSkill

Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.

Zaktualizowano 14 września 2026

Tagi

#dotnet
#maui
#cross-platform
#xamarin
#mobile

Udostępnij

Powiązane artykuły