100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
.NET

MVVM in .NET MAUI

How the MVVM pattern is applied in .NET MAUI using CommunityToolkit.Mvvm, XAML data binding, Shell navigation, and dependency injection to build cross-platform mobile and desktop apps.

MVVM FrameworksIntermediate10 min readJul 10, 2026
Analogies

MVVM Building Blocks in a MAUI App

A .NET MAUI application typically pairs CommunityToolkit.Mvvm for view models (ObservableObject, [ObservableProperty], [RelayCommand]) with MAUI's own XAML data binding engine and BindableProperty system, registering both views and view models in MauiProgram.cs's MauiAppBuilder via builder.Services.AddTransient<MainPage>() and builder.Services.AddTransient<MainPageViewModel>() so the built-in Microsoft.Extensions.DependencyInjection container resolves a fresh view model per navigation. Because MAUI targets Android, iOS, macOS (Mac Catalyst), and Windows from one codebase, the view model layer is exactly where the platform-agnostic business logic lives, while platform-specific concerns are pushed down into MAUI's handlers or dependency-injected platform services accessed through interfaces.

🏏

Cricket analogy: Like a franchise's fitness and strategy staff, the same coaching philosophy, that travels with the team whether they play a home match in Mumbai or an away leg in Cape Town, a MAUI view model carries the same business logic whether the app runs on Android or iOS.

Shell Navigation and the ViewModel

MAUI Shell provides URI-based navigation via Shell.Current.GoToAsync("orderdetail?orderId=42"), and query parameters map into the view model automatically when the view model implements IQueryAttributable (via ApplyQueryAttributes) or, more conveniently, when properties are decorated with [QueryProperty("OrderId", "orderId")], letting Shell push scalar values straight into observable properties without the view ever touching navigation parameters directly. Because navigation is driven by route strings registered with Routing.RegisterRoute, deep linking and programmatic navigation from within a view model (injected with an INavigationService abstraction, or directly via Shell.Current in simpler apps) stay decoupled from any specific page instance.

🏏

Cricket analogy: Like a scorecard app's URL deep-linking straight to 'match/42/scorecard' and populating the innings data automatically, GoToAsync with query parameters navigates straight to a route and populates the view model automatically via QueryProperty.

csharp
// MauiProgram.cs
builder.Services.AddTransient<OrderDetailPage>();
builder.Services.AddTransient<OrderDetailViewModel>();
Routing.RegisterRoute("orderdetail", typeof(OrderDetailPage));

// OrderDetailViewModel.cs
[QueryProperty(nameof(OrderId), "orderId")]
public partial class OrderDetailViewModel : ObservableObject
{
    [ObservableProperty]
    private int orderId;

    [RelayCommand]
    private async Task LoadOrderAsync()
    {
        // Uses OrderId to fetch and populate order details.
    }
}

// Navigating: await Shell.Current.GoToAsync($"orderdetail?orderId={id}");

Data Binding Compiled XAML

MAUI supports x:DataType on a page or ContentView to enable compiled bindings, which resolve binding paths at compile time via a source generator instead of reflecting over property names at runtime, catching typos like {Binding Titel} as a build error and yielding measurably better binding performance, especially inside CollectionView item templates rendering hundreds of rows. Combined with BindingMode.OneWay for read-only display and BindingMode.TwoWay for editable Entry or Editor controls, and CommunityToolkit.Mvvm's generated INotifyPropertyChanged implementation, this gives a MAUI page the same declarative, testable separation of concerns as WPF while running natively on mobile.

🏏

Cricket analogy: Like DRS technology catching an incorrect umpiring call before it stands as a final decision, x:DataType compiled bindings catch a typo'd binding path as a build error before it becomes a silent runtime bug.

x:DataType compiled bindings require the binding context type to be known statically; if a page's BindingContext is set dynamically at runtime to different types (uncommon but possible), compiled bindings will not apply and MAUI silently falls back to reflection-based binding, so it is worth confirming compiled bindings are actually active via build-time warnings for large CollectionViews where performance matters most.

  • MAUI apps typically combine CommunityToolkit.Mvvm view models with MAUI's XAML binding engine and Microsoft.Extensions.DependencyInjection.
  • Views and view models are registered in MauiProgram.cs, usually as Transient for per-navigation freshness.
  • Shell.Current.GoToAsync performs URI-based navigation, with routes registered via Routing.RegisterRoute.
  • [QueryProperty] maps navigation query parameters directly into observable view-model properties.
  • x:DataType enables compiled bindings, catching binding-path typos at build time and improving CollectionView performance.
  • BindingMode.TwoWay is required for editable controls like Entry to push changes back into the view model.
  • The same view-model layer runs unmodified across Android, iOS, Mac Catalyst, and Windows targets.

Practice what you learned

Was this page helpful?

Topics covered

#NET#MVVMDesignPatternStudyNotes#MicrosoftTechnologies#MVVMInNETMAUI#MVVM#MAUI#Building#Blocks#StudyNotes#SkillVeris