codewithmukesh/dotnet-claude-kit

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, N…

Quelltext ansehen

Verwendung

  1. Installationsbefehl kopieren und das Quellpaket vor der Installation in der eigenen Agentenumgebung prüfen.
  2. Benötigte Werkzeuge, Zugangsdaten und Hosts im Original prüfen.
  3. Eingabe und gewünschtes Ergebnis beschreiben und die Ausgabe mit den Quellen abgleichen.
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill avalonia

Vor dem Start

  • Die Installation umfasst weder Modellabo noch API-Kontingent oder Dateizugriffsrechte.
  • Sterne und Installationen zeigen Aktivität, keine unabhängige Qualitäts- oder Sicherheitsprüfung.
Originales Skill-Dokument

Aus dem Quell-Repository gerendert; Überschriften, Beispiele, Code, Tabellen, Links und Bilder bleiben erhalten.

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 DataTemplates, 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<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

xml
<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)

xml
<!-- 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>
csharp
// 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 view. The template's name-convention locator compiles but fails at runtime once trimmed, because the trimmer removes "unused" view types.

StyledProperty vs DirectProperty

csharp
public sealed class Badge : TemplatedControl
{
    // StyledProperty: settable from styles/themes, supports inheritance and animation
    public static readonly StyledProperty<string?> TextProperty =
        AvaloniaProperty.Register<Badge, string?>(nameof(Text));

    public static readonly StyledProperty<IBrush?> AccentProperty =
        AvaloniaProperty.Register<Badge, IBrush?>(nameof(Accent));

    // DirectProperty: plain backing field, cheap, NOT settable from styles — use for read-only state
    public static readonly DirectProperty<Badge, bool> IsEmptyProperty =
        AvaloniaProperty.RegisterDirect<Badge, bool>(nameof(IsEmpty), o => o.IsEmpty);

    private bool isEmpty = true;

    public string? Text
    {
        get => GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }

    public IBrush? Accent
    {
        get => GetValue(AccentProperty);
        set => SetValue(AccentProperty, value);
    }

    public bool IsEmpty
    {
        get => isEmpty;
        private set => SetAndRaise(IsEmptyProperty, ref isEmpty, value);
    }

    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
    {
        base.OnPropertyChanged(change);
        if (change.Property == TextProperty)
        {
            IsEmpty = string.IsNullOrEmpty(Text);
            PseudoClasses.Set(":empty", IsEmpty);
        }
    }
}

Why: styled properties go through Avalonia's value-priority system (local value > style > theme > inherited > default), which costs a little but enables theming. Direct properties skip that system. Pseudo-classes such as :empty let themes style a state without extra properties.

Templated Control with a ControlTheme

xml
<!-- Themes/Badge.axaml, merged into App.axaml resources -->
<ControlTheme x:Key="{x:Type local:Badge}" TargetType="local:Badge">
    <Setter Property="Accent" Value="{DynamicResource SystemAccentColor}" />
    <Setter Property="Template">
        <ControlTemplate>
            <Border Name="PART_Border" Background="{TemplateBinding Accent}" CornerRadius="8" Padding="6,2">
                <TextBlock Text="{TemplateBinding Text}" />
            </Border>
        </ControlTemplate>
    </Setter>
    <Style Selector="^:empty /template/ Border#PART_Border">
        <Setter Property="IsVisible" Value="False" />
    </Style>
</ControlTheme>
csharp
// Same Badge class as above, shown here for the template part only
[TemplatePart("PART_Border", typeof(Border))]
public sealed class Badge : TemplatedControl
{
    protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
    {
        base.OnApplyTemplate(e);
        var border = e.NameScope.Find<Border>("PART_Border"); // may be null if a consumer re-templated
    }
}

Why: a ControlTheme keyed by type is the control's default look, and consumers replace it without subclassing. TemplateBinding is the cheap one-way link from template to control. Always tolerate a missing PART_ because consumers are free to re-template.

Cross-Platform Services via TopLevel

csharp
public sealed class AvaloniaDialogService(Func<TopLevel> topLevel) : IFileDialogService
{
    public async Task<Stream?> OpenCsvAsync()
    {
        var files = await topLevel().StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
        {
            Title = "Import orders",
            AllowMultiple = false,
            FileTypeFilter = [new FilePickerFileType("CSV") { Patterns = ["*.csv"] }]
        });
        return files is [var file] ? await file.OpenReadAsync() : null;
    }
}

Why: IStorageProvider works on every platform, including sandboxed macOS and the browser, where raw paths aren't available. The old OpenFileDialog/SaveFileDialog classes were removed in 12. Get the TopLevel with TopLevel.GetTopLevel(control), since casting the visual root to TopLevel no longer works in 12.

Native AOT and Trimming

xml
<PropertyGroup>
  <PublishAot>true</PublishAot>          <!-- app project only -->
  <IsAotCompatible>true</IsAotCompatible> <!-- every project, enables the analyzers -->
</PropertyGroup>

Checklist:

  • Compiled bindings only (12 default); audit any {ReflectionBinding} and x:CompileBindings="False"
  • Trim-safe view mapping (typed DataTemplates or the dictionary ViewLocator)
  • CommunityToolkit.Mvvm generators (no reflection), source-generated JSON (native-aot skill)
  • Don't load XAML at runtime (AvaloniaRuntimeXamlLoader); add TrimmerRootAssembly only for assemblies you create controls from dynamically

Why: Avalonia supports Native AOT, which gives fast cold start and no runtime install, a real advantage over WPF. But one reflection binding or locator becomes a runtime crash that only shows up in the published build. Always smoke-test the AOT output.

Packaging

bash
dotnet publish src/Contoso.Desktop -c Release -r win-x64
dotnet publish src/Contoso.Desktop -c Release -r osx-arm64
dotnet publish src/Contoso.Desktop -c Release -r linux-x64

Why: publish per RID on that OS (Native AOT needs the native toolchain of the target OS). macOS needs an .app bundle (Info.plist + icon) plus signing and notarization. On Windows, sign the exe. For installers and auto-update across all three OSes, use a dedicated tool such as Velopack rather than hand-rolled update code.

Anti-patterns

Bindings Without x:DataType

xml
<!-- BAD — 12 compiles bindings by default: no x:DataType = build error; ReflectionBinding hides typos -->
<TextBlock Text="{ReflectionBinding CustmerName}" />

<!-- GOOD — typed, validated at build time -->
<UserControl x:DataType="vm:OrdersViewModel">
    <TextBlock Text="{Binding CustomerName}" />
</UserControl>

Bindings in C# Using the Binding Class

csharp
// BAD — in Avalonia 12, Binding always means ReflectionBinding
textBlock.Bind(TextBlock.TextProperty, new Binding("CustomerName"));

// GOOD — explicit compiled binding
textBlock.Bind(TextBlock.TextProperty,
    CompiledBinding.Create((OrdersViewModel vm) => vm.CustomerName));

Platform-Specific APIs in Shared UI Code

csharp
// BAD — Windows-only, crashes on macOS/Linux
System.Diagnostics.Process.Start("explorer.exe", folder);

// GOOD — launcher from TopLevel (desktop OSes; sandboxed targets need IStorageFolder overloads)
await TopLevel.GetTopLevel(this)!.Launcher.LaunchDirectoryInfoAsync(new DirectoryInfo(folder));

Touching Controls from Background Threads

csharp
// BAD — throws: call from invalid thread
_ = Task.Run(() => statusText.Text = "Done");

// GOOD — bind to a view model property and await the work; or dispatch explicitly
await Dispatcher.UIThread.InvokeAsync(() => Status = "Done");

In Avalonia 12 each thread can have its own dispatcher. App code with a single UI thread can use Dispatcher.UIThread, but reusable control libraries should use the control's Dispatcher property.

Decision Guide

ScenarioRecommendation
New cross-platform desktop appAvalonia 12 + CommunityToolkit.Mvvm + Generic Host
Binding a viewx:DataType on the root and on every DataTemplate
Truly dynamic binding path{ReflectionBinding}, isolated and not in AOT builds
VM → View mappingTyped DataTemplates in App.axaml (or dictionary ViewLocator)
Reusable, restylable controlTemplatedControl + ControlTheme + StyledProperty
One-off screen sectionUserControl
Computed / read-only control stateDirectProperty + pseudo-class
File open/saveTopLevel.StorageProvider
Fastest startup, no runtime installNative AOT publish per RID
Installers + auto-updateVelopack (or platform-native packaging)
Migrating from 11.xRead the official "Breaking changes in Avalonia 12" page first: bindings, clipboard, TopLevel, and the removed dialogs all changed
Developer toolsAttachDeveloperTools() from AvaloniaUI.DiagnosticsSupport (replaces AttachDevTools)
aus demselben Repository

Weitere Skills

Alle Skills
codewithmukesh
Community

console-cli

Console apps and CLI tools on .NET 10: System.CommandLine 2.x (RootCommand, Option , SetAction, InvokeAsync), Generic Host + DI in console apps, exit codes, stdout/stderr discipline, Ctrl+C cancellation, interactive vs piped use, dotnet tool packaging, and dotnet run app.cs file-based apps. Load when the user mentions "console app", "CLI", "command line", "System.CommandLine", "dotnet tool", "exit code", "stdin", "args parsing", or "file-based app".

Installationen / 1h
3
GitHub Stars
804
Aktualisiert
4. Nov.
codewithmukesh
Community

desktop-mvvm

MVVM for .NET desktop apps (WPF, Avalonia, WinForms, MAUI) with CommunityToolkit.Mvvm 8.4+ source generators: [ObservableProperty] partial properties, [RelayCommand] async commands, Generic Host + DI, UI-thread dispatching, navigation, messaging, and testable view models. Load when the user mentions "MVVM", "ViewModel", "WPF", "desktop app", "ObservableProperty", "RelayCommand", "ICommand", "INotifyPropertyChanged", "Dispatcher", or "UI thread".

Installationen / 1h
3
GitHub Stars
804
Aktualisiert
4. Nov.
codewithmukesh
Community

native-aot

Native AOT, trimming, and single-file publishing for .NET 10: PublishAot, IsAotCompatible analyzers, source-generated JSON/regex/logging/config binding, fixing IL2xxx/IL3xxx trim and AOT warnings, per-RID publishing, and when NOT to use AOT. Load when the user mentions "Native AOT", "PublishAot", "trimming", "PublishTrimmed", "single-file", "IL2026", "IL3050", "trim warning", "startup time", "self-contained", or "JsonSerializerContext".

Installationen / 1h
3
GitHub Stars
804
Aktualisiert
4. Nov.
codewithmukesh
Community

token-report

Report how many tokens the Roslyn Navigator MCP server has saved, across one repository or all of them, with a per-tool leaderboard, adoption rate, and the methodology behind the number. Use when: "token report", "token savings", "how many tokens saved", "is the MCP worth it", "MCP usage", "tool leaderboard", "adoption rate", "which MCP tools are used", "prove the savings".

Installationen / 1h
3
GitHub Stars
804
Aktualisiert
4. Nov.