Avalonia Desktop Development
Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.
Use When
- Building or reviewing a cross-platform desktop app (Windows, macOS, Linux) with Avalonia UI 11+ on .NET 8/10.
- Writing AXAML/XAML views, MVVM view models, data bindings, styles/themes, or custom controls in Avalonia.
- Displaying large collections (hundreds to thousands of items) that need virtualization and lightweight item templates.
- Bundling image/icon assets, localizing (multi-language), adding accessibility, hosting a WebView, testing, or packaging an Avalonia app.
- Migrating WPF/UWP/.NET MAUI thinking to Avalonia, or modernizing older Avalonia code to current 11.x practice.
Do Not Use When
- The work is web-only (React/Next/HTML), mobile-only native (Swift/Kotlin/Flutter), or pure backend/API with no Avalonia UI.
- It is generic .NET/C# work unrelated to the Avalonia view layer (use a general .NET guide instead).
- A WPF-specific skill is needed for a Windows-only WPF app that will not run cross-platform.
Required Inputs
- Target platforms (Windows / macOS / Linux), Avalonia version (assume 11.x unless told otherwise), and .NET version.
- The feature: views/controls involved, data shape and collection sizes, theming needs, and any native integration (WebView, file dialogs, DB).
- Existing project layout and MVVM toolkit in use (CommunityToolkit.Mvvm vs ReactiveUI vs hand-rolled).
Workflow
- Confirm project layout: shared UI project + thin per-platform entry projects + a domain/core project with no Avalonia reference.
- Build the UI in
.axaml; keep logic in view models; keep domain logic in the core project so it is unit-testable.
- Wire the root view +
DataContext in App.OnFrameworkInitializationCompleted, branching on IClassicDesktopStyleApplicationLifetime vs ISingleViewApplicationLifetime.
- Apply MVVM with
CommunityToolkit.Mvvm ([ObservableProperty], [RelayCommand]); reach for ReactiveUI only for genuinely reactive streams.
- Turn on compiled bindings everywhere:
x:DataType on every view/DataTemplate, AvaloniaUseCompiledBindingsByDefault on.
- Style with selectors +
Classes; re-skin controls with ControlTheme; theme with FluentTheme + RequestedThemeVariant for light/dark.
- Virtualize large lists, run IO/PDF/DB work async off the UI thread, localize all strings, set automation names, then test headless and package.
Quality Standards
- Keep Avalonia UI logic in views and view models; keep domain logic in a core project with no Avalonia dependency.
- Use compiled bindings, typed data templates, async commands, virtualization, localization, and accessibility names as default quality gates.
- Preserve responsive layouts across Windows, macOS, and Linux; avoid fixed sizes unless the control has a fixed-format reason.
- Package with explicit runtime identifiers, signing/notarization where required, and a documented smoke test for each target platform.
Project Layout
Core/ — models, services, domain (no Avalonia dependency; fully unit-testable).
App/ — shared Avalonia UI: Views/, ViewModels/, Assets/, Styles/.
Desktop/ — entry point referencing Avalonia.Desktop (one project serves Windows + macOS + Linux).
Tests/ — view-model unit tests + Avalonia.Headless UI tests.
- Files use the
.axaml extension. App.axaml holds app-level resources/styles; Program.cs configures the AppBuilder.
- Core packages:
Avalonia, Avalonia.Desktop, Avalonia.Themes.Fluent, Avalonia.Diagnostics (dev), CommunityToolkit.Mvvm.
Application Lifecycle
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
desktop.MainWindow = new MainWindow { DataContext = _provider.GetRequiredService<ShellViewModel>() };
else if (ApplicationLifetime is ISingleViewApplicationLifetime single)
single.MainView = new MainView { DataContext = _provider.GetRequiredService<ShellViewModel>() };
base.OnFrameworkInitializationCompleted();
}
Create the root DataContext here (ideally resolved from a DI container). Do not scatter view-model construction across code-behind.
MVVM + Compiled Bindings
Prefer source-generated view models over hand-written INotifyPropertyChanged:
public partial class LibraryViewModel : ObservableObject
{
public ObservableCollection<BookViewModel> Books { get; } = new();
[ObservableProperty] private BookViewModel? _selectedBook;
[RelayCommand(CanExecute = nameof(CanOpen))]
private async Task OpenAsync(BookViewModel book) => await _reader.OpenAsync(book.FilePath);
private bool CanOpen(BookViewModel b) => b is not null;
}
Always declare x:DataType and bind type-safely:
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:App.ViewModels"
x:DataType="vm:LibraryViewModel">
<ListBox ItemsSource="{Binding Books}" SelectedItem="{Binding SelectedBook}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="vm:BookViewModel">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</UserControl>
Compiled bindings fail the build on typos, run faster (no reflection), and are essential for large lists. Bind Button.Command to a [RelayCommand]; make commands async to keep the UI responsive. Use async commands + Dispatcher.UIThread.Post for any background → UI update.
- CommunityToolkit.Mvvm is the default. ReactiveUI (
ReactiveObject, RaiseAndSetIfChanged, ReactiveCommand, WhenAnyValue) is for complex reactive pipelines (e.g. debounced live search).
- Binding sources:
{Binding #Other.Prop} (element), {Binding $parent[Window].DataContext.Cmd}, RelativeSource FindAncestor, $self/$parent.
- Converters:
IValueConverter for type mismatches; prefer FuncValueConverter<TIn,TOut> and built-in BoolConverters/StringConverters/ObjectConverters.
Navigation (ViewModel-First)
Avalonia ships no navigation framework. Use a ViewLocator : IDataTemplate that maps FooViewModel → FooView by name, register it in Application.DataTemplates, and drive a CurrentPage view-model property bound to a host ContentControl/SplitView. Honour the *ViewModel → *View naming convention. Keep window/dialog code (Show, ShowDialog(owner)) behind an IDialogService so view models stay testable.
Layout & Controls
- Panels:
Grid (most versatile/performant; sizes Auto/*/absolute), StackPanel (use Spacing), WrapPanel, DockPanel (menus/toolbars/status bars), Canvas (absolute), RelativePanel, ScrollViewer (never wrap a ListBox/DataGrid, never nest).
- Controls are content-rich:
Button/SplitButton, TextBox (Watermark), AutoCompleteBox (type-ahead search), ComboBox, CheckBox, Slider, DatePicker, Menu/MenuItem (_ mnemonics, InputGesture), Flyout, Expander, TabControl, ProgressBar.
- Avoid fixed positions/sizes; arrange in panels so the UI adapts. Favour
Auto/* sizing and TextWrapping="Wrap" so layouts flex for long localized text.
Styling, Control Themes & Theming
Style with selectors (CSS-like), not WPF keyed styles. Use Classes and pseudo-classes (:pointerover, :pressed, :focus, :disabled, :checked):
<Style Selector="Button.primary">
<Setter Property="Background" Value="{DynamicResource AccentBrush}" />
<Setter Property="CornerRadius" Value="6" />
</Style>
<Style Selector="Button.primary:pointerover">
<Setter Property="Background" Value="#1E5BD0" />
</Style>
Apply via Classes="primary" (no per-control reference needed). For full re-skins use ControlTheme (TargetType, nested ^ selectors, ControlTemplate + ContentPresenter + {TemplateBinding}) applied with Theme="{StaticResource …}". Theme the app with <FluentTheme /> and switch light/dark via RequestedThemeVariant (follow OS by default, offer an override). Organize resources/styles into .axaml dictionaries merged with ResourceInclude/StyleInclude; use DynamicResource for theme colours, StaticResource elsewhere.
Modernize legacy code: <FluentTheme Mode="…"/> → RequestedThemeVariant; Items="{Binding}" → ItemsSource="{Binding}". Both old forms are pre-Avalonia-11.
Virtualization for Large Collections
For hundreds/thousands of items, virtualization is mandatory:
ListBox virtualizes by default (VirtualizingStackPanel) — keep it; bind ItemsSource to ObservableCollection<T>.
ItemsControl does not virtualize by default — set its ItemsPanel to VirtualizingStackPanel.
ItemsRepeater virtualizes and supports custom layouts (UniformGridLayout for cover grids).
<ListBox ItemsSource="{Binding Books}">
<ListBox.ItemsPanel><ItemsPanelTemplate><VirtualizingStackPanel/></ItemsPanelTemplate></ListBox.ItemsPanel>
</ListBox>
Keep item templates lightweight, load thumbnails lazily/async, and filter/group in the view model rather than rendering all rows. DataGrid (package Avalonia.Controls.DataGrid, add its Fluent theme include) suits tabular detail/admin views with explicit columns, not the main browse experience.
Assets & Image Bundling
Place images under Assets/, set Build Action AvaloniaResource (the default glob already covers Assets/**), and reference with avares://:
<Image Source="avares://App/Assets/icons/shelf.png" Width="32" Height="32" Stretch="Uniform" />
Stretch: Uniform (default), UniformToFill, Fill, None. Load runtime images via new Bitmap(...). Ship icons at the resolutions you use to limit bundle size; for monochrome/scalable iconography prefer vectors (PathIcon/StreamGeometry, or SVG via Avalonia.Svg.Skia); keep colourful raster icons as PNGs. Set Window.Icon for the app/window icon.
Localization
- Use
.resx resource files per culture (Resources.resx, Resources.fr.resx, …) with the generated strongly typed accessor.
- Set
CultureInfo.CurrentUICulture at startup; offer an in-app language switch persisted to settings.
- Route all user-facing strings through keys via a culture-aware indexer/
ILocalizer exposed on view models so switching language refreshes bindings without restart.
- Format dates/numbers with the current culture; design layouts to flex (German/French run ~30% longer than English).
Accessibility
- Set
AutomationProperties.Name (and HelpText/LabeledBy) on interactive and icon-only controls so Narrator/VoiceOver announce them.
- Ensure logical tab order, keyboard operability, access keys, and visible
:focus styles.
- Meet WCAG AA contrast in both light and dark variants. Custom templated controls should expose an
AutomationPeer when they add new interaction semantics.
Hosting a WebView
Avalonia has no first-party WebView. Use a community control (Avalonia.WebView, WebViewControl-Avalonia) wrapping the OS engine (WebView2/Edge-Chromium on Windows, WKWebView on macOS). Bundle web assets as AvaloniaResource (avares://) or serve from localhost loopback. Bridge C# ↔ JS via host objects/message channels and ExecuteScript/PostMessage; marshal results to the UI thread. On Windows, ensure the WebView2 evergreen runtime is installed (bundle the bootstrapper in the installer); WKWebView ships with macOS. Provide a native fallback view in case the WebView fails to initialize.
Custom Controls
- User controls aggregate existing controls; expose bindable state via styled properties (
AvaloniaProperty.Register<TOwner,T>) and custom routed events (RoutedEvent.Register<…> with a RoutingStrategies). Prefer these for composite UI.
- Templated controls are lookless: redefine appearance via
ControlTheme/ControlTemplate + ContentPresenter + {TemplateBinding}; never hard-code sizes/colours in a template. Use only for genuinely new reusable widgets.
Graphics & Animation
Use Skia-backed shapes (Rectangle/Ellipse/Path), Style.Animations keyframes (KeyFrame Cue="x%", Duration, IterationCount, Easing), Transitions per setter (DoubleTransition, BrushTransition, ThicknessTransition, TransformOperationsTransition), and render transforms. Prefer lightweight declarative transitions for hover/selection micro-interactions over per-frame C#.
Performance
- Compiled bindings everywhere; virtualize large lists with minimal templates.
- Async + background threads for PDF/DB/IO; never block the UI thread; marshal UI updates with
Dispatcher.UIThread.Post.
- Prefer
Grid over deeply nested StackPanels; cache/lazy-load and dispose Bitmaps; avoid Opacity/effects on large lists.
- Inspect with Avalonia DevTools (
Avalonia.Diagnostics, F12 in debug).
Testing
- View models are plain C# — unit-test them with no Avalonia dependency (the main reason to keep logic out of code-behind).
- Use
Avalonia.Headless (+ Avalonia.Headless.XUnit, [AvaloniaTest]) to render and drive the real UI headlessly: simulate input, call Dispatcher.UIThread.RunJobs(), assert on control/view-model state.
- Mock services behind interfaces (DI) so tests avoid filesystem/DB/WebView.
Packaging (Windows + macOS)
dotnet publish -c Release -r win-x64 | osx-arm64 | osx-x64; prefer self-contained so users need no separate .NET; trim cautiously (compiled bindings help).
- Windows: MSIX or Velopack/Squirrel installer; bundle the WebView2 evergreen runtime; sign binaries.
- macOS: build a
.app bundle, codesign + notarize (Gatekeeper), distribute a notarized .dmg; set bundle id, .icns, Info.plist/entitlements.
- Keep platform-specific assets (icons, manifests, entitlements) in the desktop project; share everything else.
Anti-Patterns
- Hand-written
INotifyPropertyChanged boilerplate instead of [ObservableProperty].
- Reflection bindings / missing
x:DataType, especially on hot/large lists.
- Wrapping
ListBox/DataGrid in a ScrollViewer; nesting ScrollViewers; non-virtualized big lists.
- WPF
Style TargetType thinking — Avalonia uses selectors + ControlTheme.
- Hard-coded strings, fixed widths that clip translations, hard-coded template values.
<FluentTheme Mode="…"/> and Items="{Binding}" (outdated pre-11 forms).
- Blocking the UI thread or touching UI objects off the UI thread; navigation/dialog code embedded in view models.
Outputs
- Avalonia view (
.axaml) + view model, styles/ControlTheme, or a review of existing Avalonia code against these standards.
- Concrete, prescriptive guidance ("do X, avoid Y") with build-safe compiled-binding and virtualization defaults.
Inputs
| Artifact |
Produced by |
Required? |
Why |
| Target platforms and user flows |
Product or project brief |
required |
Defines controls, accessibility, and packaging constraints |
| Existing solution and UI conventions |
Repository inspection |
optional |
Preserves architecture and styling decisions |
Decision rules
| Condition |
Choice |
Failure avoided |
| Large or frequently updated collection |
Use a virtualising control with compiled bindings |
UI stalls and excess allocation |
| Shared logic has no UI dependency |
Keep it in a view model or service |
Platform coupling |
| Platform API is unavoidable |
Isolate it behind an interface |
Broken cross-platform builds |
Capability contract
Read and search the solution first. Edit only when authorised; execute the narrowest build and headless tests available. Network access is optional for version-specific documentation.
Degraded mode
If repository access or execution is unavailable, provide a file-level plan and mark build, binding, packaging, and accessibility checks as unverified.
- This skill is self-contained. Load project files, Avalonia documentation, or related .NET skills only when the task needs version-specific API details or broader C#/.NET architecture guidance.
Read next
csharp-dotnet-development for language/runtime depth and world-class-engineering for release evidence.
1---2name: avalonia-desktop-development3description: Use when building or reviewing cross-platform .NET desktop apps with Avalonia UI, AXAML, MVVM, compiled bindings, virtualised lists, accessibility, headless tests, WebViews, or Windows and macOS packaging. Use csharp-dotnet-development for non-UI .NET architecture.4---56# Avalonia Desktop Development7Acknowledgement: Shared by Peter Bamuhigire, techguypeter.com, +256 784 464178.89<!-- dual-compat-start -->10## Use When1112- Building or reviewing a cross-platform desktop app (Windows, macOS, Linux) with Avalonia UI 11+ on .NET 8/10.13- Writing AXAML/XAML views, MVVM view models, data bindings, styles/themes, or custom controls in Avalonia.14- Displaying large collections (hundreds to thousands of items) that need virtualization and lightweight item templates.15- Bundling image/icon assets, localizing (multi-language), adding accessibility, hosting a WebView, testing, or packaging an Avalonia app.16- Migrating WPF/UWP/.NET MAUI thinking to Avalonia, or modernizing older Avalonia code to current 11.x practice.1718## Do Not Use When1920- The work is web-only (React/Next/HTML), mobile-only native (Swift/Kotlin/Flutter), or pure backend/API with no Avalonia UI.21- It is generic .NET/C# work unrelated to the Avalonia view layer (use a general .NET guide instead).22- A WPF-specific skill is needed for a Windows-only WPF app that will not run cross-platform.2324## Required Inputs2526- Target platforms (Windows / macOS / Linux), Avalonia version (assume 11.x unless told otherwise), and .NET version.27- The feature: views/controls involved, data shape and collection sizes, theming needs, and any native integration (WebView, file dialogs, DB).28- Existing project layout and MVVM toolkit in use (CommunityToolkit.Mvvm vs ReactiveUI vs hand-rolled).2930## Workflow31321. Confirm project layout: shared UI project + thin per-platform entry projects + a domain/core project with no Avalonia reference.332. Build the UI in `.axaml`; keep logic in view models; keep domain logic in the core project so it is unit-testable.343. Wire the root view + `DataContext` in `App.OnFrameworkInitializationCompleted`, branching on `IClassicDesktopStyleApplicationLifetime` vs `ISingleViewApplicationLifetime`.354. Apply MVVM with `CommunityToolkit.Mvvm` (`[ObservableProperty]`, `[RelayCommand]`); reach for ReactiveUI only for genuinely reactive streams.365. Turn on compiled bindings everywhere: `x:DataType` on every view/`DataTemplate`, `AvaloniaUseCompiledBindingsByDefault` on.376. Style with selectors + `Classes`; re-skin controls with `ControlTheme`; theme with `FluentTheme` + `RequestedThemeVariant` for light/dark.387. Virtualize large lists, run IO/PDF/DB work async off the UI thread, localize all strings, set automation names, then test headless and package.3940## Quality Standards4142- Keep Avalonia UI logic in views and view models; keep domain logic in a core project with no Avalonia dependency.43- Use compiled bindings, typed data templates, async commands, virtualization, localization, and accessibility names as default quality gates.44- Preserve responsive layouts across Windows, macOS, and Linux; avoid fixed sizes unless the control has a fixed-format reason.45- Package with explicit runtime identifiers, signing/notarization where required, and a documented smoke test for each target platform.4647## Project Layout4849- `Core/` — models, services, domain (no Avalonia dependency; fully unit-testable).50- `App/` — shared Avalonia UI: `Views/`, `ViewModels/`, `Assets/`, `Styles/`.51- `Desktop/` — entry point referencing `Avalonia.Desktop` (one project serves Windows + macOS + Linux).52- `Tests/` — view-model unit tests + `Avalonia.Headless` UI tests.53- Files use the **`.axaml`** extension. `App.axaml` holds app-level resources/styles; `Program.cs` configures the `AppBuilder`.54- Core packages: `Avalonia`, `Avalonia.Desktop`, `Avalonia.Themes.Fluent`, `Avalonia.Diagnostics` (dev), `CommunityToolkit.Mvvm`.5556## Application Lifecycle5758```csharp59public override void OnFrameworkInitializationCompleted()60{61 if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)62 desktop.MainWindow = new MainWindow { DataContext = _provider.GetRequiredService<ShellViewModel>() };63 else if (ApplicationLifetime is ISingleViewApplicationLifetime single)64 single.MainView = new MainView { DataContext = _provider.GetRequiredService<ShellViewModel>() };65 base.OnFrameworkInitializationCompleted();66}67```6869Create the root `DataContext` here (ideally resolved from a DI container). Do not scatter view-model construction across code-behind.7071## MVVM + Compiled Bindings7273Prefer source-generated view models over hand-written `INotifyPropertyChanged`:7475```csharp76public partial class LibraryViewModel : ObservableObject77{78 public ObservableCollection<BookViewModel> Books { get; } = new();79 [ObservableProperty] private BookViewModel? _selectedBook;8081 [RelayCommand(CanExecute = nameof(CanOpen))]82 private async Task OpenAsync(BookViewModel book) => await _reader.OpenAsync(book.FilePath);83 private bool CanOpen(BookViewModel b) => b is not null;84}85```8687Always declare `x:DataType` and bind type-safely:8889```xml90<UserControl xmlns="https://github.com/avaloniaui"91 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"92 xmlns:vm="using:App.ViewModels"93 x:DataType="vm:LibraryViewModel">94 <ListBox ItemsSource="{Binding Books}" SelectedItem="{Binding SelectedBook}">95 <ListBox.ItemTemplate>96 <DataTemplate x:DataType="vm:BookViewModel">97 <TextBlock Text="{Binding Title}" />98 </DataTemplate>99 </ListBox.ItemTemplate>100 </ListBox>101</UserControl>102```103104Compiled bindings fail the build on typos, run faster (no reflection), and are essential for large lists. Bind `Button.Command` to a `[RelayCommand]`; make commands async to keep the UI responsive. Use async commands + `Dispatcher.UIThread.Post` for any background → UI update.105106- **CommunityToolkit.Mvvm** is the default. **ReactiveUI** (`ReactiveObject`, `RaiseAndSetIfChanged`, `ReactiveCommand`, `WhenAnyValue`) is for complex reactive pipelines (e.g. debounced live search).107- **Binding sources:** `{Binding #Other.Prop}` (element), `{Binding $parent[Window].DataContext.Cmd}`, `RelativeSource FindAncestor`, `$self`/`$parent`.108- **Converters:** `IValueConverter` for type mismatches; prefer `FuncValueConverter<TIn,TOut>` and built-in `BoolConverters`/`StringConverters`/`ObjectConverters`.109110## Navigation (ViewModel-First)111112Avalonia ships no navigation framework. Use a `ViewLocator : IDataTemplate` that maps `FooViewModel` → `FooView` by name, register it in `Application.DataTemplates`, and drive a `CurrentPage` view-model property bound to a host `ContentControl`/`SplitView`. Honour the `*ViewModel` → `*View` naming convention. Keep window/dialog code (`Show`, `ShowDialog(owner)`) behind an `IDialogService` so view models stay testable.113114## Layout & Controls115116- Panels: `Grid` (most versatile/performant; sizes `Auto`/`*`/absolute), `StackPanel` (use `Spacing`), `WrapPanel`, `DockPanel` (menus/toolbars/status bars), `Canvas` (absolute), `RelativePanel`, `ScrollViewer` (never wrap a `ListBox`/`DataGrid`, never nest).117- Controls are content-rich: `Button`/`SplitButton`, `TextBox` (`Watermark`), `AutoCompleteBox` (type-ahead search), `ComboBox`, `CheckBox`, `Slider`, `DatePicker`, `Menu`/`MenuItem` (`_` mnemonics, `InputGesture`), `Flyout`, `Expander`, `TabControl`, `ProgressBar`.118- Avoid fixed positions/sizes; arrange in panels so the UI adapts. Favour `Auto`/`*` sizing and `TextWrapping="Wrap"` so layouts flex for long localized text.119120## Styling, Control Themes & Theming121122Style with **selectors** (CSS-like), not WPF keyed styles. Use `Classes` and pseudo-classes (`:pointerover`, `:pressed`, `:focus`, `:disabled`, `:checked`):123124```xml125<Style Selector="Button.primary">126 <Setter Property="Background" Value="{DynamicResource AccentBrush}" />127 <Setter Property="CornerRadius" Value="6" />128</Style>129<Style Selector="Button.primary:pointerover">130 <Setter Property="Background" Value="#1E5BD0" />131</Style>132```133134Apply via `Classes="primary"` (no per-control reference needed). For full re-skins use **`ControlTheme`** (`TargetType`, nested `^` selectors, `ControlTemplate` + `ContentPresenter` + `{TemplateBinding}`) applied with `Theme="{StaticResource …}"`. Theme the app with `<FluentTheme />` and switch light/dark via `RequestedThemeVariant` (follow OS by default, offer an override). Organize resources/styles into `.axaml` dictionaries merged with `ResourceInclude`/`StyleInclude`; use `DynamicResource` for theme colours, `StaticResource` elsewhere.135136> Modernize legacy code: `<FluentTheme Mode="…"/>` → `RequestedThemeVariant`; `Items="{Binding}"` → `ItemsSource="{Binding}"`. Both old forms are pre-Avalonia-11.137138## Virtualization for Large Collections139140For hundreds/thousands of items, virtualization is mandatory:141142- `ListBox` virtualizes by default (`VirtualizingStackPanel`) — keep it; bind `ItemsSource` to `ObservableCollection<T>`.143- `ItemsControl` does **not** virtualize by default — set its `ItemsPanel` to `VirtualizingStackPanel`.144- `ItemsRepeater` virtualizes and supports custom layouts (`UniformGridLayout` for cover grids).145146```xml147<ListBox ItemsSource="{Binding Books}">148 <ListBox.ItemsPanel><ItemsPanelTemplate><VirtualizingStackPanel/></ItemsPanelTemplate></ListBox.ItemsPanel>149</ListBox>150```151152Keep item templates lightweight, load thumbnails lazily/async, and filter/group in the view model rather than rendering all rows. `DataGrid` (package `Avalonia.Controls.DataGrid`, add its Fluent theme include) suits tabular detail/admin views with explicit columns, not the main browse experience.153154## Assets & Image Bundling155156Place images under `Assets/`, set Build Action `AvaloniaResource` (the default glob already covers `Assets/**`), and reference with `avares://`:157158```xml159<Image Source="avares://App/Assets/icons/shelf.png" Width="32" Height="32" Stretch="Uniform" />160```161162`Stretch`: `Uniform` (default), `UniformToFill`, `Fill`, `None`. Load runtime images via `new Bitmap(...)`. Ship icons at the resolutions you use to limit bundle size; for monochrome/scalable iconography prefer vectors (`PathIcon`/`StreamGeometry`, or SVG via `Avalonia.Svg.Skia`); keep colourful raster icons as PNGs. Set `Window.Icon` for the app/window icon.163164## Localization165166- Use `.resx` resource files per culture (`Resources.resx`, `Resources.fr.resx`, …) with the generated strongly typed accessor.167- Set `CultureInfo.CurrentUICulture` at startup; offer an in-app language switch persisted to settings.168- Route all user-facing strings through keys via a culture-aware indexer/`ILocalizer` exposed on view models so switching language refreshes bindings without restart.169- Format dates/numbers with the current culture; design layouts to flex (German/French run ~30% longer than English).170171## Accessibility172173- Set `AutomationProperties.Name` (and `HelpText`/`LabeledBy`) on interactive and icon-only controls so Narrator/VoiceOver announce them.174- Ensure logical tab order, keyboard operability, access keys, and visible `:focus` styles.175- Meet WCAG AA contrast in both light and dark variants. Custom templated controls should expose an `AutomationPeer` when they add new interaction semantics.176177## Hosting a WebView178179Avalonia has no first-party WebView. Use a community control (`Avalonia.WebView`, `WebViewControl-Avalonia`) wrapping the OS engine (WebView2/Edge-Chromium on Windows, WKWebView on macOS). Bundle web assets as `AvaloniaResource` (`avares://`) or serve from localhost loopback. Bridge C# ↔ JS via host objects/message channels and `ExecuteScript`/`PostMessage`; marshal results to the UI thread. On Windows, ensure the WebView2 evergreen runtime is installed (bundle the bootstrapper in the installer); WKWebView ships with macOS. Provide a native fallback view in case the WebView fails to initialize.180181## Custom Controls182183- **User controls** aggregate existing controls; expose bindable state via styled properties (`AvaloniaProperty.Register<TOwner,T>`) and custom routed events (`RoutedEvent.Register<…>` with a `RoutingStrategies`). Prefer these for composite UI.184- **Templated controls** are lookless: redefine appearance via `ControlTheme`/`ControlTemplate` + `ContentPresenter` + `{TemplateBinding}`; never hard-code sizes/colours in a template. Use only for genuinely new reusable widgets.185186## Graphics & Animation187188Use Skia-backed shapes (`Rectangle`/`Ellipse`/`Path`), `Style.Animations` keyframes (`KeyFrame Cue="x%"`, `Duration`, `IterationCount`, `Easing`), `Transitions` per setter (`DoubleTransition`, `BrushTransition`, `ThicknessTransition`, `TransformOperationsTransition`), and render transforms. Prefer lightweight declarative transitions for hover/selection micro-interactions over per-frame C#.189190## Performance191192- Compiled bindings everywhere; virtualize large lists with minimal templates.193- Async + background threads for PDF/DB/IO; never block the UI thread; marshal UI updates with `Dispatcher.UIThread.Post`.194- Prefer `Grid` over deeply nested `StackPanel`s; cache/lazy-load and dispose `Bitmap`s; avoid `Opacity`/effects on large lists.195- Inspect with Avalonia DevTools (`Avalonia.Diagnostics`, F12 in debug).196197## Testing198199- View models are plain C# — unit-test them with no Avalonia dependency (the main reason to keep logic out of code-behind).200- Use `Avalonia.Headless` (+ `Avalonia.Headless.XUnit`, `[AvaloniaTest]`) to render and drive the real UI headlessly: simulate input, call `Dispatcher.UIThread.RunJobs()`, assert on control/view-model state.201- Mock services behind interfaces (DI) so tests avoid filesystem/DB/WebView.202203## Packaging (Windows + macOS)204205- `dotnet publish -c Release -r win-x64 | osx-arm64 | osx-x64`; prefer self-contained so users need no separate .NET; trim cautiously (compiled bindings help).206- **Windows:** MSIX or Velopack/Squirrel installer; bundle the WebView2 evergreen runtime; sign binaries.207- **macOS:** build a `.app` bundle, codesign + notarize (Gatekeeper), distribute a notarized `.dmg`; set bundle id, `.icns`, `Info.plist`/entitlements.208- Keep platform-specific assets (icons, manifests, entitlements) in the desktop project; share everything else.209210## Anti-Patterns211212- Hand-written `INotifyPropertyChanged` boilerplate instead of `[ObservableProperty]`.213- Reflection bindings / missing `x:DataType`, especially on hot/large lists.214- Wrapping `ListBox`/`DataGrid` in a `ScrollViewer`; nesting `ScrollViewer`s; non-virtualized big lists.215- WPF `Style TargetType` thinking — Avalonia uses selectors + `ControlTheme`.216- Hard-coded strings, fixed widths that clip translations, hard-coded template values.217- `<FluentTheme Mode="…"/>` and `Items="{Binding}"` (outdated pre-11 forms).218- Blocking the UI thread or touching UI objects off the UI thread; navigation/dialog code embedded in view models.219220## Outputs221222- Avalonia view (`.axaml`) + view model, styles/`ControlTheme`, or a review of existing Avalonia code against these standards.223- Concrete, prescriptive guidance ("do X, avoid Y") with build-safe compiled-binding and virtualization defaults.224225## Inputs226227| Artifact | Produced by | Required? | Why |228|---|---|---|---|229| Target platforms and user flows | Product or project brief | required | Defines controls, accessibility, and packaging constraints |230| Existing solution and UI conventions | Repository inspection | optional | Preserves architecture and styling decisions |231232## Decision rules233234| Condition | Choice | Failure avoided |235|---|---|---|236| Large or frequently updated collection | Use a virtualising control with compiled bindings | UI stalls and excess allocation |237| Shared logic has no UI dependency | Keep it in a view model or service | Platform coupling |238| Platform API is unavoidable | Isolate it behind an interface | Broken cross-platform builds |239240## Capability contract241242Read and search the solution first. Edit only when authorised; execute the narrowest build and headless tests available. Network access is optional for version-specific documentation.243244## Degraded mode245246If repository access or execution is unavailable, provide a file-level plan and mark build, binding, packaging, and accessibility checks as unverified.247248- This skill is self-contained. Load project files, Avalonia documentation, or related .NET skills only when the task needs version-specific API details or broader C#/.NET architecture guidance.249<!-- dual-compat-end -->250## Read next251- `csharp-dotnet-development` for language/runtime depth and `world-class-engineering` for release evidence.