.NET MAUI trong 2026: Hướng dẫn Phát triển Cross-Platform và Câu hỏi Phỏng vấn
Hướng dẫn .NET MAUI bao gồm phát triển cross-platform với .NET 10, handler, MVVM, HybridWebView và câu hỏi phỏng vấn quan trọng năm 2026.

.NET MAUI (Multi-platform App UI) đã trưởng thành thành một framework cross-platform sẵn sàng cho production với .NET 10. Là phiên bản Long-Term Support được hỗ trợ đến tháng 11 năm 2028, .NET MAUI 10 được phân phối dưới dạng workload và gói NuGet, mang đến cải thiện về chất lượng, hiệu năng và các API mới như nâng cấp HybridWebView và SafeAreaEdges. Bài hướng dẫn này trình bày cách xây dựng ứng dụng cross-platform, kiến trúc đằng sau MAUI, và các câu hỏi phỏng vấn mà nhà tuyển dụng thường đặt ra trong năm 2026.
.NET 10 là bản phát hành Long-Term Support (được hỗ trợ đến tháng 11 năm 2028). MAUI 10 tập trung vào chất lượng và hiệu năng thay vì các control UI mới, biến nó thành bản phát hành MAUI ổn định nhất từ trước đến nay. Framework hiện được cung cấp dưới dạng workload .NET và gói NuGet, cho phép ghim phiên bản theo từng dự án.
Thiết lập Dự án .NET MAUI 10 từ Đầu
Cách nhanh nhất để có một ứng dụng MAUI hoạt động bắt đầu với .NET CLI. .NET 10 giới thiệu template dự án cập nhật bao gồm service defaults .NET Aspire, kết nối telemetry và service discovery ngay từ đầu.
# 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-androidCấu trúc single-project hợp nhất mã platform-specific vào thư mục Platforms/ trong khi chia sẻ phần còn lại. File MauiProgram.cs đóng vai trò composition root, nơi đăng ký service, font và handler.
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();
}
}Dependency injection trong MAUI tuân theo cùng mô hình với ASP.NET Core. Service Singleton tồn tại suốt vòng đời ứng dụng, service Transient được tạo mới mỗi lần yêu cầu, và service Scoped cần thận trọng vì MAUI không có khái niệm scope tích hợp như HTTP request.
Handler: Kiến trúc Đằng sau Rendering Cross-Platform
MAUI thay thế renderer của Xamarin.Forms bằng kiến trúc handler. Handler ánh xạ mỗi control cross-platform đến đối tác native thông qua một lớp trừu tượng mỏng. Điểm khác biệt chính: handler không có trạng thái (stateless) và tách biệt khỏi virtual view, giúp chúng nhanh hơn và dễ tùy chỉnh hơn.
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>();
});Trong .NET 10, các control Entry và Editor trên Android chuyển từ AppCompatEditText sang MauiAppCompatEditText, thêm hỗ trợ native cho event SelectionChanged. Các handler CollectionView và CarouselView được cải tiến từ .NET 9 giờ đã là mặc định trên iOS và Mac Catalyst, giải quyết các vấn đề ổn định tồn tại lâu.
Android 16 Edge-to-Edge: Breaking Change và Cách Khắc phục
Android 16 loại bỏ cơ chế opt-out cho hiển thị edge-to-edge. Các ứng dụng nhắm đến API 36 phải xử lý system bar insets đúng cách, nếu không nội dung sẽ render không chính xác. .NET 10 đã đưa vào một breaking change ngầm: trong .NET 9, ContentPage tôn trọng system bars theo mặc định (hành vi Container), nhưng .NET 10 mặc định là edge-to-edge (None).
Các ứng dụng di chuyển từ .NET 9 sang .NET 10 có thể thấy nội dung render dưới status bar hoặc navigation bar trên Android 15+. Cách khắc phục yêu cầu đặt SafeAreaEdges một cách tường minh để khôi phục hành vi trước đó.
Cách khắc phục bao gồm thêm global implicit style vào App.xaml:
<!-- 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 cung cấp điều khiển chi tiết:
None: nội dung edge-to-edge không có padding safe areaSoftInput: luôn padding cho bàn phímContainer: chảy dưới bàn phím, giữ ngoài bars và notchDefault: hành vi mặc định của platformAll: tuân thủ tất cả safe area insets
Cách tiếp cận này sử dụng Window Insets API tại runtime, làm cho nó tương thích với Android 16 nơi opt-out cấp manifest không còn hoạt động.
MVVM với CommunityToolkit.Mvvm: Loại bỏ Boilerplate
Source generator CommunityToolkit.Mvvm loại bỏ khoảng 80% mã lặp MVVM. Không cần triển khai INotifyPropertyChanged thủ công, không cần command wrapper: các attribute điều khiển code generation.
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 binding trực tiếp đến các property và command được sinh ra:
<!-- 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>Attribute x:DataType kích hoạt compiled bindings, nhanh hơn binding dựa trên reflection và tạo ra lỗi compile-time khi binding path sai.
Sẵn sàng chinh phục phỏng vấn .NET?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
XAML Source Generator trong .NET 10
.NET MAUI 10 giới thiệu XAML source generator biên dịch XAML tại build time thay vì phân tích tại runtime. Điều này cải thiện hiệu năng startup và bắt lỗi XAML trong quá trình biên dịch.
Để bật XAML source generation, thêm property vào file dự án:
<PropertyGroup>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>.NET 10 Preview 5 cũng giới thiệu implicit XML namespaces loại bỏ các khai báo xmlns boilerplate. File GlobalXmlns.cs có thể tổng hợp nhiều namespaces:
[assembly: XmlnsDefinition(
"http://schemas.microsoft.com/dotnet/maui/global",
"MyApp.Views")]
[assembly: XmlnsDefinition(
"http://schemas.microsoft.com/dotnet/maui/global",
"MyApp.Controls")]Với thiết lập này, các file XAML trở nên gọn gàng hơn:
<!-- Before -->
<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>
<!-- After (with global 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 trong .NET 10: Kết nối Native và Web
HybridWebView cho phép nhúng nội dung web bên trong ứng dụng MAUI đồng thời duy trì giao tiếp hai chiều giữa C# và JavaScript. .NET 10 bổ sung ba khả năng: gọi JavaScript fire-and-forget, event khởi tạo cho cấu hình platform-specific, và chặn web request.
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");
}
}Các exception JavaScript phát sinh trong InvokeJavaScriptAsync giờ đây tự động được chuyển tiếp đến .NET dưới dạng exception, loại bỏ các lỗi im lặng. Chặn web request cho phép sửa đổi header hoặc cung cấp response cục bộ:
webView.WebResourceRequested += (s, e) =>
{
if (e.Uri.ToString().Contains("api/secure"))
{
e.Handled = true;
e.SetResponse(200, "OK", "application/json", GetCustomStream());
}
};API Deprecated và Đường dẫn Di chuyển
.NET MAUI 10 deprecate một số API. Các ứng dụng sử dụng chúng nên di chuyển sang các lựa chọn thay thế được khuyến nghị.
ListView và các cell liên quan: ListView, EntryCell, ImageCell, SwitchCell, TextCell, và ViewCell đã deprecated. Sử dụng CollectionView thay thế, có hiệu năng và độ ổn định tốt hơn với các handler iOS/Mac Catalyst mới.
MessagingCenter: Được chuyển thành internal trong .NET 10. Thay thế bằng WeakReferenceMessenger từ CommunityToolkit.Mvvm.
Các phương thức animation: FadeTo, RotateTo, ScaleTo, và các phương thức tương tự đã deprecated. Sử dụng các phiên bản async: FadeToAsync, RotateToAsync, ScaleToAsync.
TableView: Deprecated, thay thế bằng CollectionView.
DisplayAlert/DisplayActionSheet: Thay thế bằng DisplayAlertAsync và DisplayActionSheetAsync.
Di chuyển từ Xamarin.Forms sang .NET MAUI
Xamarin.Forms đã kết thúc hỗ trợ vào tháng 5 năm 2024. Việc di chuyển sang MAUI bao gồm các thay đổi cấu trúc vượt xa việc thay namespace đơn thuần. Dưới đây là checklist di chuyển dựa trên kinh nghiệm chuyển đổi dự án thực tế:
Xamarin.Forms đã không còn được hỗ trợ kể từ tháng 5 năm 2024. Các ứng dụng vẫn chạy trên Xamarin có rủi ro bảo mật và tương thích. .NET MAUI 10 (LTS, hỗ trợ đến tháng 11 năm 2028) là mục tiêu di chuyển được chỉ định.
- Cấu trúc dự án: Chuyển đổi từ các dự án platform-specific sang mô hình single-project MAUI. Di chuyển mã shared về root, mã platform vào
Platforms/. - Namespace: Thay thế
Xamarin.FormsbằngMicrosoft.Maui.ControlsvàXamarin.EssentialsbằngMicrosoft.Maui.Essentials(giờ đã tích hợp trong MAUI). - Renderer sang Handler: Custom renderer phải được viết lại thành handler. API handler đơn giản hơn nhưng logic ánh xạ khác biệt.
- Startup: Thay thế khởi tạo
App.xaml.csbằngMauiProgram.cssử dụng builder pattern. - Gói NuGet: Nhiều gói thời Xamarin có tương đương MAUI. Xác minh tương thích trước khi nâng cấp.
- Dependency injection: MAUI sử dụng
Microsoft.Extensions.DependencyInjectionnative. Thay thế container DI bên thứ ba hoặc lệnh gọiDependencyService.
.NET Upgrade Assistant tự động hóa một phần bước 1-2, nhưng handler (bước 3) và điều chỉnh business logic đòi hỏi công việc thủ công.
Câu hỏi Phỏng vấn .NET MAUI Hàng đầu cho 2026
Những câu hỏi này phản ánh những gì các đội tuyển dụng hỏi trong năm 2026, dựa trên hệ sinh thái .NET 10 hiện tại. Để chuẩn bị phỏng vấn C# và .NET thêm, xem hướng dẫn phỏng vấn .NET đầy đủ.
Kiến trúc handler MAUI khác với renderer Xamarin.Forms như thế nào?
Renderer trong Xamarin.Forms có coupling chặt với cả control cross-platform và native view, tạo ra sự phụ thuộc hai chiều. Handler trong MAUI là mapper stateless: chúng nhận thông báo thay đổi property và áp dụng chúng vào native view thông qua dictionary mapper. Sự tách biệt này giúp handler dễ test, mở rộng và tái sử dụng hơn. Dictionary PropertyMapper và CommandMapper thay thế mẫu override OnElementPropertyChanged, làm cho việc tùy chỉnh trở nên tường minh thay vì ẩn trong các câu lệnh switch.
Những cạm bẫy DI lifetime nào đặc thù cho MAUI?
MAUI hỗ trợ lifetime Singleton, Transient và Scoped, nhưng Scoped hoạt động khác so với ASP.NET Core. Không có ranh giới scope tự nhiên (như HTTP request). Service Scoped được đăng ký trong MAUI hoạt động như Singleton trừ khi scope tùy chỉnh được tạo thủ công. Lỗi phổ biến: đăng ký ViewModel là Singleton khi nó giữ state đặc thù trang (dữ liệu cũ khi điều hướng), hoặc đăng ký kết nối database là Transient (cạn kiệt connection pool). Quy tắc chung: ViewModel là Transient, service là Singleton, và Scoped được tránh trừ khi lifecycle scope được quản lý tường minh.
Khi trả lời câu hỏi DI, hãy thể hiện sự hiểu biết về cách lifecycle MAUI khác với mô hình request-scoped của ASP.NET Core. Người phỏng vấn tìm kiếm nhận thức về memory leak và vấn đề state cũ đặc thù cho ứng dụng mobile chạy lâu dài.
Compiled binding khác với binding dựa trên reflection trong MAUI như thế nào?
Binding dựa trên reflection giải quyết property path tại runtime sử dụng System.Reflection, chậm và tạo lỗi runtime cho các lỗi đánh máy. Compiled binding, được kích hoạt qua x:DataType, giải quyết binding path tại compile time. Compiler sinh mã truy cập property trực tiếp, bỏ qua reflection hoàn toàn. Điều này cải thiện thời gian startup, giảm phân bổ bộ nhớ, và bắt lỗi binding khi build. Trong .NET 10, source generator XAML mới tối ưu thêm bằng cách biên dịch XAML tại build time thay vì phân tích tại runtime.
Có những chiến lược nào để chia sẻ mã giữa ứng dụng MAUI và backend ASP.NET Core?
Cách tiếp cận được khuyến nghị sử dụng shared class library chứa DTO, logic validation và business rules. Cả ứng dụng MAUI và backend ASP.NET Core đều tham chiếu library này. .NET 10 củng cố mẫu này với tích hợp .NET Aspire cho MAUI, cung cấp service discovery và telemetry xuyên suốt các dự án mobile và backend. Shared contract sử dụng source generator System.Text.Json đảm bảo tính nhất quán serialization. Ràng buộc chính: shared library phải nhắm đến net10.0 (không phải TFM platform-specific) để duy trì tính portable.
HybridWebView khác với BlazorWebView trong MAUI như thế nào?
BlazorWebView host toàn bộ ứng dụng Blazor bên trong ứng dụng MAUI. Các component Razor render vào WebView được nhúng, nhưng runtime .NET thực thi native (không qua WebAssembly). HybridWebView nhẹ hơn: nó tải nội dung HTML/CSS/JS tĩnh và cung cấp interop C#-sang-JavaScript mà không có overhead của framework Blazor. Lựa chọn phụ thuộc vào use case. BlazorWebView phù hợp cho các đội có component Blazor sẵn và muốn tái sử dụng mã. HybridWebView phù hợp cho các tình huống mà nội dung web hiện có (dashboard, bản đồ, trình soạn thảo) cần tích hợp native mà không cần framework đầy đủ.
Luyện tập thêm câu hỏi phỏng vấn .NET trên SharpSkill để củng cố các khái niệm này với bài tập tương tác.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Nguồn tham khảo
- What's new in .NET MAUI for .NET 10 — tài liệu chính thức về tất cả tính năng .NET 10 MAUI
- .NET MAUI GitHub Releases — ghi chú phát hành service cho các phiên bản 10.0.x
- Edge-to-Edge Is Now Mandatory in Android 16 — hướng dẫn di chuyển cho yêu cầu Android 16
- MAUI GitHub Discussion #34171 — phản hồi cộng đồng về độ ổn định .NET MAUI 10
Điều Lập trình viên .NET MAUI 10 Cần Ghi nhớ
- .NET MAUI 10 là bản phát hành LTS (hỗ trợ đến tháng 11 năm 2028) tập trung vào ổn định và hiệu năng, là lựa chọn đáng tin cậy cho ứng dụng cross-platform production
- Android 16 bắt buộc edge-to-edge: thêm
SafeAreaEdges="Container"làm global style trong App.xaml để khôi phục hành vi .NET 9 - XAML source generator (
MauiXamlInflator=SourceGen) biên dịch XAML tại build time, cải thiện hiệu năng startup và bắt lỗi sớm hơn - ListView, TableView, và MessagingCenter đã deprecated: di chuyển sang CollectionView và WeakReferenceMessenger
- Kiến trúc handler thay thế renderer Xamarin bằng mapper stateless, cải thiện khả năng test thông qua
PropertyMappervàCommandMapper - Source generator CommunityToolkit.Mvvm loại bỏ hầu hết boilerplate MVVM: attribute
[ObservableProperty]và[RelayCommand]thay thế triển khai thủ công - HybridWebView trong .NET 10 bổ sung gọi JavaScript fire-and-forget, event khởi tạo và chặn request cho tích hợp native-web
- Chuẩn bị phỏng vấn nên tập trung vào kiến trúc handler vs. renderer, cạm bẫy DI lifetime trong ứng dụng chạy lâu, compiled binding, và đánh đổi HybridWebView vs. BlazorWebView
Bạn có tìm ra lỗi trong .NET không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 14 tháng 9, 2026
Thẻ
Chia sẻ
Bài viết liên quan

Clean Architecture .NET 2026: CQRS, MediatR và Câu Hỏi Phỏng Vấn Developer
Hướng dẫn toàn diện về Clean Architecture trong .NET với CQRS và MediatR 14. Tìm hiểu các mẫu kiến trúc phân lớp, pipeline behaviors và chuẩn bị phỏng vấn senior developer.

Câu Hỏi Phỏng Vấn C# và .NET: Hướng Dẫn Đầy Đủ 2026
17 câu hỏi phỏng vấn C# và .NET thường gặp nhất. LINQ, async/await, dependency injection, Entity Framework và các phương pháp tốt nhất với đáp án chi tiết.

Top 25 Câu Hỏi Phỏng Vấn ASP.NET Core: Middleware, DI và Minimal APIs
Tổng hợp 25 câu hỏi phỏng vấn ASP.NET Core về middleware pipeline, dependency injection, minimal APIs kèm giải đáp chi tiết và ví dụ code thực tế.