.NET MAUI 10: Tính Năng Mới, CoreCLR Thử Nghiệm Và Hướng Dẫn Nâng Cấp Chi Tiết

Tổng hợp chi tiết tính năng mới trong .NET MAUI 10: XAML Source Generator, global namespaces, tích hợp .NET Aspire, SafeAreaEdges, CoreCLR thử nghiệm trên Android, hỗ trợ Android 16 và iOS 26, cùng hướng dẫn nâng cấp từ .NET MAUI 9.

Giới thiệu

.NET MAUI 10 vừa chính thức ra mắt và thật sự, đây là bản cập nhật mà nhiều người trong cộng đồng đã chờ đợi từ lâu. Thay vì chạy theo những tính năng UI hào nhoáng, Microsoft lần này tập trung vào điều mà lập trình viên thực sự cần — chất lượng sản phẩm. Từ XAML Source Generator giúp build nhanh hơn đáng kể, global XML namespaces giúp dọn sạch đống boilerplate khó chịu, cho đến tích hợp .NET Aspire để có observability đầy đủ.

Nói thật, khi mình lần đầu thử nghiệm bản preview, ấn tượng đầu tiên là tốc độ build cải thiện rõ rệt. Và đó mới chỉ là bề mặt.

Trong bài viết này, chúng ta sẽ đi sâu vào từng tính năng mới của .NET MAUI 10 — bao gồm các cải tiến giao diện, API nền tảng, hỗ trợ Android 16 và iOS 26, cùng những breaking changes mà bạn cần nắm rõ trước khi nâng cấp dự án.

XAML Source Generator — Biên dịch XAML ngay tại thời điểm build

Đây có lẽ là cải tiến có tác động lớn nhất trong phiên bản này. Trước đây, XAML được parse tại runtime — nghĩa là bạn phải chạy app mới biết có lỗi hay không. Khá phiền phải không? Giờ đây với XAML Source Generator, XAML được biên dịch thành mã strongly-typed ngay trong quá trình build.

Lợi ích chính

  • Phát hiện lỗi sớm: Lỗi XAML được báo ngay lúc biên dịch, không còn chuyện app crash bất ngờ vì một typo trong XAML
  • Khởi động nhanh hơn: Không cần parse XAML tại runtime nữa
  • Code có thể kiểm tra: Mã được sinh ra hoàn toàn có thể xem và debug trực tiếp
  • Tooling cải thiện: IntelliSense và refactoring hoạt động chính xác hơn

Cách kích hoạt

Để bật XAML Source Generator, bạn chỉ cần thêm một dòng vào file .csproj:

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

Sau khi bật, mỗi file XAML sẽ tự động sinh ra một file C# tương ứng chứa strongly-typed references đến tất cả các phần tử UI. Điều này không chỉ nhanh hơn mà còn khiến việc debug dễ chịu hơn rất nhiều.

Global và Implicit XML Namespaces — Tạm biệt boilerplate XAML

Nếu bạn từng bực mình vì phải khai báo hàng loạt namespace ở đầu mỗi file XAML, thì đây là tính năng dành cho bạn. Implicit và global XML namespaces giúp giảm đáng kể lượng code thừa.

Trước đây (.NET 8/9)

Mỗi file XAML phải khai báo đầy đủ tất cả namespace:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:models="clr-namespace:MyApp.Models"
             xmlns:controls="clr-namespace:MyApp.Controls"
             xmlns:converters="clr-namespace:MyApp.Converters"
             x:Class="MyApp.MainPage">
    <controls:TagView x:DataType="models:Tag" />
    <Label Text="{Binding Name,
           Converter={converters:StringToUpperConverter}}" />
</ContentPage>

Với .NET MAUI 10

Khai báo một lần duy nhất trong file GlobalXmlns.cs, xong:

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

Và XAML trở nên gọn gàng hơn hẳn:

<ContentPage x:Class="MyApp.MainPage">
    <TagView x:DataType="Tag" />
    <Label Text="{Binding Name,
           Converter={StringToUpperConverter}}" />
</ContentPage>

Sự khác biệt khá rõ ràng — XAML sạch hơn, dễ đọc hơn, và bạn ít có khả năng mắc lỗi khi copy-paste namespace giữa các file.

Tích hợp .NET Aspire — Observability cho ứng dụng di động

Đây là một bổ sung khá thú vị. .NET MAUI 10 cho phép tích hợp với .NET Aspire, mang telemetry và service discovery vào ứng dụng di động — điều mà trước giờ chỉ dành cho server-side.

Thiết lập cơ bản

Bạn dùng project template mới và thêm Aspire service defaults vào MauiProgram.cs:

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

        // Thêm Aspire service defaults
        builder.AddServiceDefaults();

        return builder.Build();
    }
}

Những gì được cấu hình tự động

  • OpenTelemetry Metrics: Thu thập metrics hiệu suất tự động, không cần cấu hình thêm
  • Distributed Tracing: Theo dõi request qua nhiều service
  • Service Discovery: Tự động tìm và kết nối đến backend
  • HttpClient Configuration: HttpClient được cấu hình sẵn với service discovery

Với các dự án enterprise mà app mobile cần giao tiếp với nhiều microservices, tính năng này thật sự rất hữu ích.

Hệ thống Diagnostics và Metrics tích hợp sẵn

.NET MAUI 10 bổ sung hệ thống diagnostics toàn diện, đặc biệt tập trung vào hiệu suất layout — vốn là một trong những "thủ phạm" phổ biến nhất gây giật lag trong ứng dụng di động.

Các metrics được theo dõi

// ActivitySource cho .NET MAUI
// Tên: "Microsoft.Maui"

// Các metrics tích hợp sẵn:
// maui.layout.measure_count   - Số lần đo lường (measure)
// maui.layout.measure_duration - Thời gian đo lường (nanoseconds)
// maui.layout.arrange_count   - Số lần sắp xếp (arrange)
// maui.layout.arrange_duration - Thời gian sắp xếp (nanoseconds)

Với hệ thống này, bạn có thể:

  • Phát hiện các layout "ngốn" tài nguyên
  • Theo dõi hiệu suất layout real-time
  • Tích hợp với APM tools như Datadog, New Relic, hoặc Azure Monitor
  • Nhanh chóng tìm ra nguyên nhân khi app bị chậm

Cải tiến điều khiển giao diện (Control Enhancements)

Animation API — Chuyển sang Async hoàn toàn

Cuối cùng thì Animation API cũng được hiện đại hóa với async/await đầy đủ:

// Trước đây (đã deprecated)
await myButton.FadeTo(0, 250);
await myButton.ScaleTo(1.5, 500);
await myButton.RotateTo(360, 1000);

// .NET MAUI 10 — API mới
await myButton.FadeToAsync(0, 250);
await myButton.ScaleToAsync(1.5, 500);
await myButton.RotateToAsync(360, 1000);

// Kết hợp nhiều animation chạy đồng thời
await Task.WhenAll(
    myButton.FadeToAsync(1, 300),
    myButton.ScaleToAsync(1.2, 300),
    myButton.RotateToAsync(45, 300)
);

Chuyển sang async giúp tránh blocking UI thread và dễ dàng kết hợp nhiều animation cùng lúc hơn.

Picker — Open/Close API và Nullable Support

// Mở và đóng Picker theo chương trình
await picker.OpenAsync();
await picker.CloseAsync();

// DatePicker và TimePicker giờ hỗ trợ nullable
// Date là DateTime? thay vì DateTime
// Time là TimeSpan? thay vì TimeSpan
<DatePicker Date="{Binding SelectedDate}" />
// SelectedDate có thể là null — rất tiện cho form
// mà ngày tháng không bắt buộc

HybridWebView — Tương tác JavaScript nâng cao

HybridWebView nhận được khá nhiều cải tiến đáng chú ý trong phiên bản này:

// Gọi JavaScript kiểu fire-and-forget
await webView.InvokeJavaScriptAsync("updateUI", newData);

// Chặn và xử lý web request tùy chỉnh
webView.WebResourceRequested += (sender, e) =>
{
    if (e.Uri.ToString().Contains("api/secure"))
    {
        e.Handled = true;
        e.SetResponse(
            statusCode: 200,
            reasonPhrase: "OK",
            contentType: "application/json",
            content: GetSecureDataStream()
        );
    }
};

// Sự kiện vòng đời mới
webView.WebViewInitializing += (s, e) =>
{
    // Cấu hình trước khi WebView được tạo
};

webView.WebViewInitialized += (s, e) =>
{
    // Thao tác sau khi WebView đã sẵn sàng
};

SearchBar và Switch — Tùy chỉnh giao diện

<!-- Tùy chỉnh màu icon tìm kiếm -->
<SearchBar SearchIconColor="Blue"
           ReturnType="Search"
           Placeholder="Tìm kiếm..." />

<!-- Switch với màu trạng thái off -->
<Switch OffColor="#CCCCCC"
        OnColor="#4CAF50"
        ThumbColor="White" />

<!-- RefreshView giờ phân biệt rõ giữa refresh và enable -->
<RefreshView IsRefreshEnabled="false" IsEnabled="true">
    <CollectionView ItemsSource="{Binding Items}" />
</RefreshView>

SafeAreaEdges — Kiểm soát Safe Area chi tiết hơn bao giờ hết

Cái này mình rất thích. Hệ thống SafeAreaEdges mới cho phép bạn kiểm soát chính xác cách nội dung tương tác với notch, thanh trạng thái, bàn phím ảo — thay vì chỉ có "có" hoặc "không" như trước.

<!-- Tràn dưới bàn phím nhưng tránh notch -->
<ContentPage SafeAreaEdges="Container">
    <Grid>
        <!-- Nội dung chính -->
    </Grid>
</ContentPage>

<!-- Edge-to-edge scrolling -->
<ScrollView SafeAreaEdges="None">
    <StackLayout>
        <!-- Nội dung cuộn tràn viền -->
    </StackLayout>
</ScrollView>

<!-- Chỉ tránh bàn phím ảo -->
<Grid SafeAreaEdges="SoftInput">
    <Editor Placeholder="Nhập nội dung..." />
</Grid>

<!-- Tuân theo tất cả vùng an toàn -->
<StackLayout SafeAreaEdges="All">
    <!-- Nội dung an toàn hoàn toàn -->
</StackLayout>

Các giá trị SafeAreaEdges

  • None — Edge-to-edge hoàn toàn, nội dung tràn viền
  • SoftInput — Chỉ tránh bàn phím ảo
  • Container — Tràn dưới bàn phím nhưng tránh notch và status bar
  • Default — Hành vi mặc định của nền tảng
  • All — Tuân theo tất cả vùng an toàn

Cải tiến nền tảng Android

Hỗ trợ Android 16 (API 36)

.NET MAUI 10 nhắm đến Android 16 (API 36) làm target mặc định, đi kèm hỗ trợ JDK 21. Mức API tối thiểu khuyến nghị cũng được nâng lên 24 (Android 7.0 Nougat) — một quyết định hợp lý vì các thiết bị cũ hơn đã chiếm tỷ lệ rất nhỏ.

Chạy ứng dụng với dotnet run

Cuối cùng thì bạn cũng có thể dùng dotnet run để chạy trực tiếp app Android. Nghe đơn giản nhưng đây là điều mà nhiều developer đã yêu cầu từ lâu:

# Chạy trên thiết bị vật lý
dotnet run -p:AdbTarget=-d

# Chạy trên emulator
dotnet run -p:AdbTarget=-e

# Chạy trên emulator cụ thể
dotnet run -p:AdbTarget="-s emulator-5554"

Hiệu suất build cải thiện đáng kể

  • Marshal methods bật mặc định: Startup performance tốt hơn ngay lập tức
  • System.IO.Compression cho .apk/.aab: Đóng gói nhanh hơn
  • Design-time builds nhanh hơn: Bỏ việc gọi aapt2 trong design-time, giảm từ ~2 giây xuống còn ~600ms (cải thiện hơn 3 lần!)

CoreCLR thử nghiệm trên Android

Đây là tính năng mà cá nhân mình cho rằng có thể thay đổi cuộc chơi. Bạn giờ có thể sử dụng CoreCLR thay cho Mono trên Android, hứa hẹn hiệu suất runtime vượt trội:

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier(
    '$(TargetFramework)')) == 'android'">
    <UseMonoRuntime>false</UseMonoRuntime>
</PropertyGroup>

Nhiều dev trong cộng đồng đánh giá đây là bước đi có thể mang lại "sự tái sinh" cho MAUI trên Android. Tất nhiên, hiện tại vẫn là experimental, nhưng triển vọng rất đáng mong đợi.

Cải tiến nền tảng iOS và Mac Catalyst

Phiên bản hỗ trợ

  • iOS: 18.2+ (hỗ trợ Xcode 26 Beta 4)
  • tvOS: 18.2+
  • Mac Catalyst: 18.2+
  • macOS: 15.2+

Trimmer bật mặc định

Trong .NET MAUI 10, IL Trimmer được bật mặc định cho cấu hình arm64 trên iOS. Kích thước app giảm đáng kể nhờ loại bỏ mã không sử dụng. Tuy nhiên, nếu dự án của bạn dùng nhiều reflection, bạn có thể cần tắt cảnh báo trimmer:

<PropertyGroup>
    <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
</PropertyGroup>

Build Binding Projects trên Windows

Tin vui cho anh em dùng Windows: giờ đây bạn có thể build iOS binding projects trực tiếp trên Windows mà không cần kết nối đến máy Mac. Điều này đơn giản hóa workflow đáng kể, đặc biệt cho các team không phải ai cũng có Mac.

CollectionView và CarouselView mặc định mới

Các handler cải tiến cho CollectionView và CarouselView — vốn là optional trong .NET 9 — giờ trở thành handler mặc định trên iOS và Mac Catalyst. Tin tốt là chúng ổn định hơn và nhanh hơn phiên bản cũ.

Cải tiến MediaPicker và các API nền tảng

MediaPicker

// Tự động xoay ảnh theo EXIF orientation
// Giữ nguyên EXIF metadata

// Chọn nhiều file với nén tự động
var results = await MediaPicker.PickMultipleAsync(
    new MediaPickerOptions
    {
        MaximumWidth = 1024,
        MaximumHeight = 768
    });

foreach (var photo in results)
{
    var stream = await photo.OpenReadAsync();
    // Xử lý từng ảnh đã được nén
}

Các API nền tảng khác

// Geolocation — Kiểm tra trạng thái trước khi gọi
if (Geolocation.IsEnabled)
{
    var location = await Geolocation.GetLocationAsync();
}

// TextToSpeech — Điều chỉnh tốc độ đọc
await TextToSpeech.SpeakAsync("Xin chào!", new SpeechOptions
{
    Rate = 1.5f  // Nhanh hơn bình thường
});

// WebAuthenticator — Hỗ trợ CancellationToken
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var result = await WebAuthenticator.AuthenticateAsync(
    callbackUrl, authUrl, cts.Token);

// Vibration và HapticFeedback — Check trước khi dùng
if (Vibration.IsSupported)
{
    Vibration.Vibrate(TimeSpan.FromMilliseconds(500));
}

if (HapticFeedback.IsSupported)
{
    HapticFeedback.Perform(HapticFeedbackType.Click);
}

Các API và Controls bị Deprecated

Một số API và controls cũ đã chính thức bị đánh dấu deprecated. Đây là bước cần thiết (dù hơi đau) để framework tiến lên phía trước.

Controls bị deprecated

  • ListView → Dùng CollectionView thay thế
  • TableView → Dùng CollectionView thay thế
  • EntryCell, ImageCell, SwitchCell, TextCell → Dùng custom DataTemplate trong CollectionView
  • ClickGestureRecognizer → Dùng TapGestureRecognizer
  • Accelerator → Dùng KeyboardAccelerator
  • Compatibility.Layout → Chuyển sang layout mới

API bị deprecated

// Pop-up methods — Chuyển sang async
// Cũ (deprecated):
await DisplayAlert("Thông báo", "Nội dung", "OK");
await DisplayActionSheet("Chọn", "Hủy", null, "Tùy chọn 1", "Tùy chọn 2");

// Mới:
await DisplayAlertAsync("Thông báo", "Nội dung", "OK");
await DisplayActionSheetAsync("Chọn", "Hủy", null, "Tùy chọn 1", "Tùy chọn 2");

// Page.IsBusy — Dùng ActivityIndicator
// Cũ:
IsBusy = true;

// Mới:
<ActivityIndicator IsRunning="{Binding IsLoading}" />

// MessagingCenter — Dùng CommunityToolkit.Mvvm
// Cũ:
MessagingCenter.Send(this, "DataUpdated", newData);

// Mới:
WeakReferenceMessenger.Default.Send(new DataUpdatedMessage(newData));

FontImageExtension

<!-- Cũ (deprecated): -->
<Button ImageSource="{FontImage Glyph=,
        Color=Black, FontFamily=FontAwesome, Size=18}" />

<!-- Mới: -->
<Button>
    <Button.ImageSource>
        <FontImageSource Glyph=""
                         Color="Black"
                         FontFamily="FontAwesome"
                         Size="18" />
    </Button.ImageSource>
</Button>

Hướng dẫn nâng cấp từ .NET MAUI 9 lên .NET MAUI 10

Nếu bạn đang có dự án chạy trên .NET MAUI 9, đây là các bước nâng cấp mà mình khuyến nghị. Không quá phức tạp, nhưng cần cẩn thận một chút.

Bước 1: Cập nhật Target Framework

<!-- Cũ -->
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>

<!-- Mới -->
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>

Bước 2: Cập nhật NuGet packages

# Cập nhật workload và restore packages
dotnet workload update
dotnet restore

Bước 3: Xử lý Deprecation Warnings

  • Thay ListView bằng CollectionView
  • Chuyển MessagingCenter sang WeakReferenceMessenger
  • Đổi animation API từ FadeTo sang FadeToAsync
  • Thay DisplayAlert bằng DisplayAlertAsync

Bước 4: Tận dụng tính năng mới

  • Bật XAML Source Generator để tăng tốc build
  • Thiết lập global XML namespaces để dọn sạch XAML
  • Tích hợp .NET Aspire nếu app cần observability
  • Thử SafeAreaEdges cho trải nghiệm edge-to-edge

Bước 5: Kiểm tra Trimmer Warnings

Vì trimmer mặc định bật trên iOS, bạn cần kiểm tra kỹ các cảnh báo trimming — đặc biệt nếu dự án sử dụng nhiều reflection hoặc dynamic type loading:

<!-- Tắt tạm để debug nếu cần -->
<PropertyGroup>
    <PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>

<!-- Hoặc chỉ tắt cảnh báo -->
<PropertyGroup>
    <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
</PropertyGroup>

So sánh .NET MAUI 10 với các phiên bản trước

Tính năng .NET MAUI 8 .NET MAUI 9 .NET MAUI 10
XAML Source Generator Không Không
Global XML Namespaces Không Không
.NET Aspire Integration Không Không
Layout Diagnostics Không Hạn chế Toàn diện
SafeAreaEdges Cơ bản Cơ bản Chi tiết (5 chế độ)
CollectionView Handler (iOS) Tùy chọn Mặc định
CoreCLR trên Android Không Không Thử nghiệm
Animation API Đồng bộ Đồng bộ Async
Nullable DatePicker/TimePicker Không Không
iOS Trimmer mặc định Không Không

Triển vọng và đánh giá

.NET MAUI 10 cho thấy Microsoft đang đi đúng hướng. Thay vì chạy theo tính năng UI mới cho "có tiêu đề", đội ngũ phát triển ưu tiên những thứ developer thực sự cần: build nhanh hơn, code sạch hơn, observability tốt hơn, và sửa những bug dai dẳng.

Với CoreCLR experimental trên Android, hỗ trợ Android 16 và iOS 26, cùng tích hợp .NET Aspire — MAUI 10 đang dần chứng minh mình là một framework đa nền tảng nghiêm túc, có khả năng cạnh tranh với Flutter và React Native trong phân khúc enterprise.

Có một điểm quan trọng cần lưu ý: các phiên bản MAUI chỉ nhận được 6 tháng hỗ trợ sau khi phiên bản mới phát hành — ngắn hơn so với chính sách hỗ trợ tiêu chuẩn của .NET. Vì vậy, hãy lên kế hoạch nâng cấp sớm để đảm bảo dự án luôn nhận được bản vá bảo mật.

Kết luận

.NET MAUI 10 không phải bản cập nhật "đình đám" với hàng loạt tính năng mới bóng bẩy — nhưng thành thật mà nói, đây chính xác là những gì framework cần lúc này. Tập trung vào chất lượng, hiệu suất, và developer experience là quyết định đúng đắn, và Microsoft đã giải quyết được nhiều pain points mà cộng đồng phản hồi suốt các phiên bản trước.

Nếu bạn đang dùng .NET MAUI, đây là thời điểm tốt để nâng cấp. XAML Source Generator, global namespaces, và hệ thống diagnostics sẽ giúp bạn viết code sạch hơn, build nhanh hơn, và debug hiệu quả hơn.

Lời khuyên cuối: hãy thử trên một dự án nhỏ trước khi nâng cấp dự án chính. Chú ý đặc biệt đến các API deprecated và hành vi mới của trimmer trên iOS. Chúc bạn code vui với .NET MAUI 10!

Về Tác Giả Editorial Team

Our team of expert writers and editors.