Skip to content
Development
Skill

/avalonia

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,

From plugin
dotnet-claude-kit
81752 skills11 agents2 hooks1 MCP

Claude Code

$ npx -y skills add codewithmukesh/dotnet-claude-kit --skill avalonia --agent claude-code

SKILL.md

avalonia.SKILL.md
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

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<App>().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<MainWindowViewModel>();
        builder.Services.AddTransient<OrdersViewModel>();
        var host = builder.Build();
        host.Start();

        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.MainWindow = new MainWindow
            {
                DataContext = host.Services.GetRequiredService<MainWindowViewModel>()
            };
            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`

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:vm="using:Contoso.Desktop.ViewModels"
             x:Class="Contoso.Desktop.Views.OrdersView"
             x:DataType="vm:OrdersViewModel">
    <DockPanel>
        <TextBox DockPanel.Dock="Top" Text="{Binding CustomerName}" PlaceholderText="Customer" />
        <Button DockPanel.Dock="Top" Content="Save" Command="{Binding SaveCommand}" />
        <ListBox ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder}">
            <ListBox.ItemTemplate>
                <DataTemplate x:DataType="vm:OrderRow">
                    <TextBlock Text="{Binding Number}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </DockPanel>
</UserControl>

**Why:** the XAML compiler resolves `CustomerName` against `OrdersViewModel` at build time. Each `DataTemplate` changes the data context, so it declares its own `x:DataType`. In 12, `TextBox.Watermark` was renamed `PlaceholderText`. Use `{ReflectionBinding}` only for a genuinely dynamic path, and treat each one as a trimming liability.

Mapping View Models to Views (Trim-Safe)

<!-- App.axaml: typed templates, resolved at compile time -->
<Application.DataTemplates>
    <DataTemplate DataType="vm:OrdersViewModel"><views:OrdersView /></DataTemplate>
    <DataTemplate DataType="vm:SettingsViewModel"><views:SettingsView /></DataTemplate>
</Application.DataTemplates>
// If you prefer a ViewLocator: explicit factories, no reflection
public sealed class ViewLocator : IDataTemplate
{
    private static readonly Dictionary<Type, Func<Control>> Views = new()
    {
        [typeof(OrdersViewModel)] = () => new OrdersView(),
        [typeof(SettingsViewModel)] = () => new SettingsView(),
    };

    public Control? Build(object? param) =>
        param is not null && Views.TryGetValue(param.GetType(), out var factory)
            ? factory()
            : new TextBlock { Text = $"No view for {param?.GetType().Name}" };

    public bool Match(object? data) => data is ObservableObject;
}

**Why:** `<ContentControl Content="{Binding Current}" />` (see navigation in `desktop-mvvm`) then renders the right vi

Read more
@codewithmukesh
Ships withdotnet-claude-kit

Make Claude Code a .NET 10 Expert

81752 skills11 agents1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
817
Star history of codewithmukesh/dotnet-claude-kit on GitHub, a chart by star-history.com

Other skills on dotnet-claude-kit.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills12d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about avalonia.

Which coding agents can use avalonia?
avalonia installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.