Skip to content

Instantly share code, notes, and snippets.

@drawcode
Last active December 20, 2025 07:12
Show Gist options
  • Select an option

  • Save drawcode/513d0de54dbddbaa3ec7267bf1828d43 to your computer and use it in GitHub Desktop.

Select an option

Save drawcode/513d0de54dbddbaa3ec7267bf1828d43 to your computer and use it in GitHub Desktop.
MAUI Performance Checklist

.NET MAUI High-Performance & Multi-Platform Checklist

πŸš€ 1. Startup & Architecture

  • Lazy Service Resolution: Defer instantiation of heavy services to improve initial app responsiveness.
// MauiProgram.cs registration
builder.Services.AddTransient<HeavyService>();
builder.Services.AddTransient<Lazy<IHeavyService>>(p => 
    new Lazy<IHeavyService>(() => p.GetRequiredService<HeavyService>()));

// Consumption in ViewModel
public MainViewModel(Lazy<IHeavyService> lazyService) => _service = lazyService;
public void OnLoaded() => _service.Value.Initialize(); 
  • Compiled Bindings: Use x:DataType to bypass reflection and catch binding errors at compile-time.
<ContentPage xmlns:vm="clr-namespace:MyApp.ViewModels" x:DataType="vm:MainViewModel">
    <Label Text="{Binding UserName}" />
</ContentPage>
  • Custom Handlers: Use Handlers over legacy Renderers to keep the visual tree slim and reduce mapping overhead.
public partial class MyEntryHandler : Microsoft.Maui.Handlers.EntryHandler {
    protected override void ConnectHandler(Microsoft.Maui.Platform.MauiEditText platformView) {
        base.ConnectHandler(platformView);
    }
}
  • Dependency Injection Scoping: Use AddSingleton for state-heavy services and AddTransient for lightweight ViewModels to manage memory lifecycle effectively.

πŸ“± 2. UI & Rendering Optimization

  • Layout Flattening: Minimize visual tree depth. A single Grid is significantly faster than nested StackLayouts during Measure/Arrange passes.
<Grid ColumnDefinitions="100, *" RowDefinitions="Auto, Auto">
    <Image Grid.RowSpan="2" Source="icon.png" />
    <Label Grid.Column="1" Text="Header" />
    <Label Grid.Column="1" Grid.Row="1" Text="Description" />
</Grid>
  • UI Virtualization: Use CollectionView with incremental loading for large datasets to recycle native UI elements.
<CollectionView ItemsSource="{Binding Items}"
                RemainingItemsThreshold="5"
                RemainingItemsCommand="{Binding LoadMoreCommand}">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="vm:ItemVM">
            <Grid>...</Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
  • Resource Optimization: Define common styles in App.xaml to avoid redundant object instantiation per page.
  • Stop Overdraw: Use IsVisible instead of Opacity="0" to ensure the rendering engine ignores the element entirely.

πŸ’» 3. Multi-Platform & Device Tuning

  • Device Capability Detection: Gracefully degrade visuals for older hardware.
bool isLowEnd = DeviceInfo.Current.Platform == DevicePlatform.Android && DeviceInfo.Current.Version.Major < 11;
if (isLowEnd) {
    // Disable shadows or heavy animations
    MyVisualElement.Shadow = null;
}
  • Adaptive Layouts: Leverage platform-specific values for desktop vs. mobile.
<Button Text="Submit" 
        WidthRequest="{OnPlatform WinUI=250, Android=-1}"
        FontSize="{OnIdiom Phone=12, Desktop=18}" />
  • Memory Leak Prevention: Unsubscribe from events or use WeakReferenceMessenger to allow the GC to reclaim memory.
// Cleanup in OnDisappearing
protected override void OnDisappearing() {
    base.OnDisappearing();
    WeakReferenceMessenger.Default.Unregister<MyMessage>(this);
    BindingContext = null; 
}

πŸ› οΈ 4. Code Quality & Logic

  • Batch Collection Updates: Use ObservableRangeCollection to fire a single UI notification for bulk changes.
// Prevents 100+ UI refreshes (one per item)
Items.AddRange(newLargeList); 
  • Asynchronous Offloading: Never block the UI thread; use Task.Run for heavy computational logic.
public async Task LoadAsync() {
    IsBusy = true;
    var data = await Task.Run(() => _processor.HeavyCompute()); 
    Items.ReplaceRange(data);
    IsBusy = false;
}
  • ValueTask for Sync-Path Methods: Reduce heap allocations for methods that frequently return cached data.
public ValueTask<string> GetDataAsync() {
    return _cache != null ? new ValueTask<string>(_cache) : new ValueTask<string>(FetchAsync());
}
  • Context Preservation: Use .ConfigureAwait(false) in library and service layers to avoid unnecessary thread switching.

πŸ“„ Directory.Build.props

Place this in the solution root to apply performance-centric build settings globally.

<Project>
  <PropertyGroup>
    <NetMauiContextCachingEnabled>true</NetMauiContextCachingEnabled>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>

    <PropertyGroup Condition="'$(Configuration)' == 'Release'">
      <PublishTrimmed>true</PublishTrimmed>
      <TrimMode>full</TrimMode>
      <EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
      
      <RunAOTCompilation Condition="'$(TargetFramework)' == 'net8.0-android'">true</RunAOTCompilation>
      <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
      <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
      <EnableLLVM Condition="'$(TargetFramework)' == 'net8.0-android'">true</EnableLLVM>
      
      <MtouchLink>SdkOnly</MtouchLink>
      <RuntimeIdentifier Condition="'$(TargetFramework)' == 'net8.0-ios'">ios-arm64</RuntimeIdentifier>
    </PropertyGroup>
  </PropertyGroup>
</Project>

πŸ“‰ Impact Summary Table

Optimization Startup Fluidity Memory Priority
Compiled Bindings Low High Medium Critical
Grid vs Stack Medium High Low High
AOT Compilation High Low Low High
Lazy Services High Low Medium Medium
ObservableRange Low High Low Medium
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment