--- name: avalonia description: > Avalonia UI 12 for cross-platform .NET desktop apps: compiled bindings (x:DataType, on by default in 12), StyledProperty/DirectProperty, custom and templated controls, ControlThemes, DataTemplates/ViewLocator, Generic Host + DI, StorageProvider/Clipboard, Native AOT/trimming, and packaging. Load when the user mentions "Avalonia", "AXAML", "x:DataType", "compiled binding", "StyledProperty", "TemplatedControl", "ViewLocator", or "cross-platform desktop". --- # Avalonia UI (v12) Verified against Avalonia 12.1.x. Avalonia 12 requires .NET 8+ (.NET 10 recommended) and makes compiled bindings the default. Pair this skill with `desktop-mvvm` for view-model patterns. ## Core Principles 1. **Compiled bindings everywhere** — In Avalonia 12, `{Binding}` in XAML compiles by default (`AvaloniaUseCompiledBindingsByDefault=true`). Every view and `DataTemplate` needs `x:DataType`. Binding typos become build errors instead of silent blank fields, and there's no reflection, so trimming and AOT keep working. 2. **Views are mapped to view models at compile time** — Use typed `DataTemplate`s, or a dictionary-based `ViewLocator`. The template's reflection `ViewLocator` (`Type.GetType` + `Activator.CreateInstance`) breaks under trimming. 3. **`UserControl` for composition, `TemplatedControl` for reusable controls** — A lookless templated control can be restyled by consumers. A `UserControl` is just a reusable chunk of a screen. 4. **Platform services come from `TopLevel`** — Use `TopLevel.GetTopLevel(visual)` to get the file picker (`StorageProvider`), `Clipboard`, and `Launcher`. These work on Windows, macOS, Linux, and browser. Never reach for Win32/WinForms dialogs. 5. **Build and test on every target OS** — Fonts, DPI, window chrome, and file paths differ by platform. A CI matrix (windows/macos/ubuntu) catches problems before users do. ## Patterns ### Startup with Generic Host ```csharp internal static class Program { [STAThread] public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); // Must stay public static: the XAML previewer calls it public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure().UsePlatformDetect().LogToTrace(); } public sealed partial class App : Application { public override void Initialize() => AvaloniaXamlLoader.Load(this); public override void OnFrameworkInitializationCompleted() { var builder = Host.CreateApplicationBuilder(new HostApplicationBuilderSettings { ContentRootPath = AppContext.BaseDirectory }); builder.Services.AddSingleton(TimeProvider.System); builder.Services.AddSingleton(); builder.Services.AddTransient(); var host = builder.Build(); host.Start(); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { desktop.MainWindow = new MainWindow { DataContext = host.Services.GetRequiredService() }; desktop.Exit += (_, _) => host.Dispose(); } base.OnFrameworkInitializationCompleted(); } } ``` **Why:** `OnFrameworkInitializationCompleted` is the composition root, the point where the platform is ready but no window exists yet. The `IClassicDesktopStyleApplicationLifetime` check keeps the same `App` usable on mobile and browser lifetimes. If you call `UseSkia()` explicitly and see "No text shaping system configured", add `Avalonia.HarfBuzz` and `.UseHarfBuzz()`, a 12.0 change. ### Compiled Bindings with `x:DataType` ```xml