.NET MAUI Cheat Sheet
Overview
.NET MAUI (Multi-platform App UI) is Microsoft’s cross-platform framework for creating native mobile and desktop apps with C# and XAML. It is the evolution of Xamarin.Forms, providing a single project structure that targets Android, iOS, macOS, and Windows. MAUI enables developers to share UI code and business logic across platforms while still accessing native platform APIs when needed. The framework uses native controls on each platform, ensuring apps look and feel native to their respective operating systems.
MAUI integrates with the broader .NET ecosystem, offering access to NuGet packages, dependency injection, and modern C# features. It supports both XAML-based declarative UI and C# Markup for code-first UI development. The framework includes built-in handlers that map cross-platform controls to native platform controls, a flexible layout system, and comprehensive support for platform-specific customization through conditional compilation and platform folders.
Installation
# Install .NET 8 SDK or later
# Windows
winget install Microsoft.DotNet.SDK.8
# macOS
brew install dotnet-sdk
# Linux (Ubuntu)
sudo apt-get install dotnet-sdk-8.0
# Verify installation
dotnet --version
dotnet workload list
# Install MAUI workload
dotnet workload install maui
# Additional requirements:
# Android: Android SDK via Visual Studio or standalone
# iOS/macOS: Xcode on macOS
# Windows: Visual Studio 2022 17.8+
# Create new MAUI project
dotnet new maui -n MyMauiApp
cd MyMauiApp
# Build and run
dotnet build
dotnet build -t:Run -f net8.0-android
dotnet build -t:Run -f net8.0-ios
dotnet build -t:Run -f net8.0-maccatalyst
dotnet build -t:Run -f net8.0-windows10.0.19041.0
Project Structure
MyMauiApp/
├── App.xaml # Application resources
├── App.xaml.cs # Application entry point
├── AppShell.xaml # Navigation shell
├── MainPage.xaml # Main page UI
├── MainPage.xaml.cs # Main page code-behind
├── MauiProgram.cs # Host builder setup
├── Platforms/
│ ├── Android/ # Android-specific code
│ │ ├── AndroidManifest.xml
│ │ └── MainApplication.cs
│ ├── iOS/ # iOS-specific code
│ │ ├── AppDelegate.cs
│ │ └── Info.plist
│ ├── MacCatalyst/ # macOS-specific code
│ └── Windows/ # Windows-specific code
├── Resources/
│ ├── Fonts/
│ ├── Images/
│ ├── Raw/
│ └── Styles/
│ ├── Colors.xaml
│ └── Styles.xaml
└── MyMauiApp.csproj
XAML Basics
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyMauiApp.MainPage"
Title="Home">
<ScrollView>
<VerticalStackLayout Spacing="16" Padding="20">
<Label Text="Welcome to MAUI!"
FontSize="28"
FontAttributes="Bold"
HorizontalOptions="Center" />
<Entry x:Name="nameEntry"
Placeholder="Enter your name"
MaxLength="50" />
<Button Text="Say Hello"
Clicked="OnHelloClicked"
BackgroundColor="{StaticResource Primary}"
TextColor="White" />
<Image Source="dotnet_bot.png"
HeightRequest="200"
Aspect="AspectFit" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Common Controls
| Control | Description |
|---|---|
<Label> | Display text |
<Button> | Clickable button |
<Entry> | Single-line text input |
<Editor> | Multi-line text input |
<Image> | Display image |
<Switch> | Boolean toggle |
<Slider> | Range selector |
<Picker> | Dropdown selection |
<DatePicker> | Date selection |
<CheckBox> | Boolean checkbox |
<ProgressBar> | Progress indicator |
<ActivityIndicator> | Loading spinner |
<CollectionView> | Scrollable list of items |
<Frame> | Container with border/shadow |
<Border> | Customizable border container |
Layouts
<!-- VerticalStackLayout -->
<VerticalStackLayout Spacing="10">
<Label Text="Item 1" />
<Label Text="Item 2" />
</VerticalStackLayout>
<!-- HorizontalStackLayout -->
<HorizontalStackLayout Spacing="8">
<Image Source="icon.png" WidthRequest="40" />
<Label Text="Title" VerticalOptions="Center" />
</HorizontalStackLayout>
<!-- Grid -->
<Grid RowDefinitions="Auto,*,50"
ColumnDefinitions="*,2*"
RowSpacing="8" ColumnSpacing="8">
<Label Grid.Row="0" Grid.Column="0" Text="Header" Grid.ColumnSpan="2" />
<Label Grid.Row="1" Grid.Column="0" Text="Left" />
<Label Grid.Row="1" Grid.Column="1" Text="Right" />
<Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Text="Submit" />
</Grid>
<!-- FlexLayout -->
<FlexLayout Direction="Row" Wrap="Wrap" JustifyContent="SpaceEvenly">
<Frame WidthRequest="120" HeightRequest="120" />
<Frame WidthRequest="120" HeightRequest="120" />
<Frame WidthRequest="120" HeightRequest="120" />
</FlexLayout>
<!-- AbsoluteLayout -->
<AbsoluteLayout>
<BoxView Color="Blue"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All" />
<Label Text="Overlay"
AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1"
AbsoluteLayout.LayoutFlags="PositionProportional" />
</AbsoluteLayout>
Data Binding and MVVM
// ViewModel
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string name = string.Empty;
[ObservableProperty]
private ObservableCollection<TodoItem> items = new();
[RelayCommand]
private async Task LoadItemsAsync()
{
var data = await _service.GetItemsAsync();
Items = new ObservableCollection<TodoItem>(data);
}
[RelayCommand]
private void AddItem()
{
if (!string.IsNullOrWhiteSpace(Name))
{
Items.Add(new TodoItem { Title = Name });
Name = string.Empty;
}
}
}
<!-- XAML Binding -->
<ContentPage xmlns:vm="clr-namespace:MyApp.ViewModels">
<ContentPage.BindingContext>
<vm:MainViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout>
<Entry Text="{Binding Name}" Placeholder="New item" />
<Button Text="Add" Command="{Binding AddItemCommand}" />
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:TodoItem">
<Label Text="{Binding Title}" Padding="10" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
Navigation (Shell)
<!-- AppShell.xaml -->
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:views="clr-namespace:MyApp.Views">
<TabBar>
<ShellContent Title="Home" Icon="home.png"
ContentTemplate="{DataTemplate views:HomePage}" />
<ShellContent Title="Settings" Icon="settings.png"
ContentTemplate="{DataTemplate views:SettingsPage}" />
</TabBar>
<FlyoutItem Title="Profile" Icon="profile.png">
<ShellContent ContentTemplate="{DataTemplate views:ProfilePage}" />
</FlyoutItem>
</Shell>
// Register routes
Routing.RegisterRoute("details", typeof(DetailsPage));
Routing.RegisterRoute("edit", typeof(EditPage));
// Navigate with parameters
await Shell.Current.GoToAsync($"details?id={item.Id}");
await Shell.Current.GoToAsync(".."); // Go back
// Receive parameters
[QueryProperty(nameof(ItemId), "id")]
public partial class DetailsPage : ContentPage
{
public string ItemId { get; set; }
}
Configuration and Dependency Injection
// 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");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
// Register services
builder.Services.AddSingleton<IApiService, ApiService>();
builder.Services.AddSingleton<IDatabase, SqliteDatabase>();
// Register ViewModels
builder.Services.AddTransient<MainViewModel>();
builder.Services.AddTransient<DetailViewModel>();
// Register Pages
builder.Services.AddTransient<MainPage>();
builder.Services.AddTransient<DetailPage>();
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
}
Platform-Specific Code
// Conditional compilation
#if ANDROID
var intent = new Android.Content.Intent(Android.Content.Intent.ActionView);
#elif IOS
UIKit.UIApplication.SharedApplication.OpenUrl(new NSUrl(url));
#elif WINDOWS
await Windows.System.Launcher.LaunchUriAsync(new Uri(url));
#endif
// Partial classes for platform implementations
// Shared: Services/DeviceService.cs
public partial class DeviceService
{
public partial string GetDeviceId();
}
// Platforms/Android/Services/DeviceService.cs
public partial class DeviceService
{
public partial string GetDeviceId()
{
return Android.Provider.Settings.Secure.GetString(
Android.App.Application.Context.ContentResolver,
Android.Provider.Settings.Secure.AndroidId);
}
}
Advanced Usage
// Custom Handler
public class CustomEntryHandler : EntryHandler
{
protected override void ConnectHandler(MauiTextField platformView)
{
base.ConnectHandler(platformView);
#if ANDROID
platformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
#elif IOS
platformView.BorderStyle = UIKit.UITextBorderStyle.None;
#endif
}
}
// Register handler
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<Entry, CustomEntryHandler>();
});
// Behaviors
public class NumericValidationBehavior : Behavior<Entry>
{
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnTextChanged;
base.OnAttachedTo(entry);
}
void OnTextChanged(object sender, TextChangedEventArgs e)
{
bool isValid = double.TryParse(e.NewTextValue, out _);
((Entry)sender).TextColor = isValid ? Colors.Black : Colors.Red;
}
}
// Secure storage
await SecureStorage.SetAsync("auth_token", token);
var token = await SecureStorage.GetAsync("auth_token");
// Preferences
Preferences.Set("username", "john");
var name = Preferences.Get("username", "default");
Troubleshooting
| Issue | Solution |
|---|---|
| Android emulator not found | Install via Android SDK Manager; ensure HAXM/hypervisor enabled |
| iOS build fails | Ensure Xcode and command line tools installed; check provisioning profiles |
| Hot Reload not working | Enable in Visual Studio settings; ensure debugger attached |
| XAML IntelliSense broken | Clean solution and rebuild; restart Visual Studio |
| Images not displaying | Place in Resources/Images; use filename without extension in XAML |
| NuGet restore fails | Run dotnet restore; clear NuGet cache with dotnet nuget locals all --clear |
| Slow Android startup | Enable startup tracing; use AOT compilation for release builds |
| CollectionView not scrolling | Set HeightRequest or place inside proper layout container |
| Binding not updating UI | Implement INotifyPropertyChanged or use [ObservableProperty] |
| Platform service not found | Register in MauiProgram.cs using builder.Services |