Zum Inhalt springen

Avalonia Cheat Sheet

Overview

Avalonia is an open-source, cross-platform UI framework for .NET that enables developers to build desktop, mobile, and web applications from a single C# and XAML codebase. Often described as the WPF of cross-platform, Avalonia provides a familiar XAML-based development experience for .NET developers while supporting Windows, macOS, Linux, iOS, Android, and WebAssembly. It uses its own rendering engine built on Skia, ensuring pixel-perfect consistency across all platforms.

Avalonia supports the MVVM (Model-View-ViewModel) pattern natively, with powerful data binding, styling, and templating systems. It includes a comprehensive set of built-in controls, supports custom controls, offers CSS-like styling with selectors, and provides responsive layouts. The framework integrates with popular .NET libraries and supports both AOT (ahead-of-time) and JIT compilation. Avalonia has been adopted by JetBrains for some of their tools and is used in production by many companies building cross-platform desktop applications.

Installation

# Prerequisites: .NET 8 SDK or later
dotnet --version

# Install Avalonia templates
dotnet new install Avalonia.Templates

# Create new Avalonia project
dotnet new avalonia.app -o MyAvaloniaApp
cd MyAvaloniaApp

# Create MVVM project (with ReactiveUI or CommunityToolkit)
dotnet new avalonia.mvvm -o MyMvvmApp

# Create cross-platform project (desktop + mobile + browser)
dotnet new avalonia.xplat -o MyCrossPlatApp

# Build and run
dotnet build
dotnet run

# With hot reload
dotnet watch run

# Install Avalonia for Visual Studio / VS Code / Rider
# Rider: built-in support
# VS Code: Install "Avalonia for VSCode" extension
# Visual Studio: Install "Avalonia for Visual Studio" extension

Project Structure

MyAvaloniaApp/
├── MyAvaloniaApp.csproj
├── App.axaml                 # Application resources/styles
├── App.axaml.cs              # Application entry
├── Program.cs                # Entry point
├── ViewLocator.cs            # View-ViewModel resolver
├── Views/
│   ├── MainWindow.axaml      # Main window XAML
│   └── MainWindow.axaml.cs   # Code-behind
├── ViewModels/
│   ├── ViewModelBase.cs
│   └── MainWindowViewModel.cs
├── Models/
├── Assets/                   # Images, fonts, etc.
└── Styles/
    └── Themes.axaml

AXAML Basics

<!-- Views/MainWindow.axaml -->
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:MyApp.ViewModels"
        x:Class="MyApp.Views.MainWindow"
        Title="My Avalonia App"
        Width="800" Height="600"
        WindowStartupLocation="CenterScreen">

    <Design.DataContext>
        <vm:MainWindowViewModel />
    </Design.DataContext>

    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="_File">
                <MenuItem Header="_Open" Command="{Binding OpenCommand}" />
                <Separator />
                <MenuItem Header="E_xit" Command="{Binding ExitCommand}" />
            </MenuItem>
        </Menu>

        <StackPanel Margin="20" Spacing="10">
            <TextBlock Text="Welcome to Avalonia!"
                       FontSize="24"
                       FontWeight="Bold"
                       HorizontalAlignment="Center" />
            
            <TextBox Watermark="Enter your name"
                     Text="{Binding Name}" />
            
            <Button Content="Say Hello"
                    Command="{Binding GreetCommand}"
                    HorizontalAlignment="Center" />
            
            <TextBlock Text="{Binding Greeting}"
                       FontSize="18"
                       HorizontalAlignment="Center" />
        </StackPanel>
    </DockPanel>
</Window>

Common Controls

ControlDescription
<TextBlock>Display text
<TextBox>Text input field
<Button>Clickable button
<ToggleButton>Toggle state button
<CheckBox>Boolean checkbox
<RadioButton>Single selection
<ComboBox>Dropdown selection
<Slider>Range slider
<ProgressBar>Progress indicator
<Image>Display image
<ListBox>Scrollable list
<DataGrid>Tabular data display
<TreeView>Hierarchical data
<TabControl>Tabbed interface
<Expander>Collapsible section
<Calendar>Date picker
<NumericUpDown>Numeric input
<AutoCompleteBox>Auto-complete input

Layouts

<!-- StackPanel -->
<StackPanel Orientation="Vertical" Spacing="8">
    <Button Content="First" />
    <Button Content="Second" />
    <Button Content="Third" />
</StackPanel>

<!-- Grid -->
<Grid RowDefinitions="Auto,*,50"
      ColumnDefinitions="200,*"
      Margin="10">
    <TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
               Text="Header" FontSize="20" />
    <ListBox Grid.Row="1" Grid.Column="0" />
    <Border Grid.Row="1" Grid.Column="1" Background="#f0f0f0">
        <TextBlock Text="Content Area" />
    </Border>
    <StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
                Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8">
        <Button Content="Cancel" />
        <Button Content="OK" />
    </StackPanel>
</Grid>

<!-- DockPanel -->
<DockPanel LastChildFill="True">
    <Menu DockPanel.Dock="Top" />
    <StatusBar DockPanel.Dock="Bottom" />
    <TreeView DockPanel.Dock="Left" Width="200" />
    <Border> <!-- Fills remaining space -->
        <TextBlock Text="Main Content" />
    </Border>
</DockPanel>

<!-- WrapPanel -->
<WrapPanel Orientation="Horizontal">
    <Button Content="Tag 1" Margin="4" />
    <Button Content="Tag 2" Margin="4" />
    <Button Content="Tag 3" Margin="4" />
</WrapPanel>

<!-- UniformGrid -->
<UniformGrid Rows="3" Columns="3">
    <Button Content="1" /><Button Content="2" /><Button Content="3" />
    <Button Content="4" /><Button Content="5" /><Button Content="6" />
    <Button Content="7" /><Button Content="8" /><Button Content="9" />
</UniformGrid>

Data Binding and MVVM

// ViewModels/MainWindowViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public partial class MainWindowViewModel : ViewModelBase
{
    [ObservableProperty]
    private string _name = string.Empty;

    [ObservableProperty]
    private string _greeting = string.Empty;

    [ObservableProperty]
    private ObservableCollection<TodoItem> _items = new();

    [RelayCommand]
    private void Greet()
    {
        Greeting = string.IsNullOrWhiteSpace(Name) 
            ? "Hello, World!" 
            : $"Hello, {Name}!";
    }

    [RelayCommand]
    private async Task OpenAsync()
    {
        var files = await TopLevel.StorageProvider.OpenFilePickerAsync(
            new FilePickerOpenOptions
            {
                Title = "Open File",
                AllowMultiple = false
            });
        
        if (files.Count > 0)
        {
            // Process file
        }
    }

    [RelayCommand]
    private void AddItem()
    {
        if (!string.IsNullOrWhiteSpace(Name))
        {
            Items.Add(new TodoItem { Title = Name, IsDone = false });
            Name = string.Empty;
        }
    }
}
<!-- Data binding in AXAML -->
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Spacing="8">
                <CheckBox IsChecked="{Binding IsDone}" />
                <TextBlock Text="{Binding Title}" VerticalAlignment="Center" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

<!-- Value converters -->
<TextBlock Text="{Binding Count, StringFormat='Items: {0}'}" />
<TextBlock IsVisible="{Binding HasItems}" />
<TextBlock Text="{Binding Price, StringFormat='{}{0:C}'}" />

Styling

<!-- CSS-like selectors in Avalonia -->
<Window.Styles>
    <!-- Target all buttons -->
    <Style Selector="Button">
        <Setter Property="Background" Value="#0078D4" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="CornerRadius" Value="4" />
        <Setter Property="Padding" Value="12,6" />
    </Style>
    
    <!-- Target by class -->
    <Style Selector="Button.primary">
        <Setter Property="Background" Value="#0078D4" />
    </Style>
    <Style Selector="Button.danger">
        <Setter Property="Background" Value="#D32F2F" />
    </Style>
    
    <!-- Hover state -->
    <Style Selector="Button:pointerover">
        <Setter Property="Background" Value="#106EBE" />
    </Style>
    
    <!-- Child selector -->
    <Style Selector="ListBox > ListBoxItem:selected">
        <Setter Property="Background" Value="#E3F2FD" />
    </Style>
    
    <!-- Descendant selector -->
    <Style Selector="StackPanel TextBlock">
        <Setter Property="FontSize" Value="14" />
    </Style>
</Window.Styles>

<!-- Apply class -->
<Button Classes="primary" Content="Save" />
<Button Classes="danger" Content="Delete" />

Configuration

// Program.cs
using Avalonia;

public class Program
{
    [STAThread]
    public static void Main(string[] args) => BuildAvaloniaApp()
        .StartWithClassicDesktopLifetime(args);

    public static AppBuilder BuildAvaloniaApp()
        => AppBuilder.Configure<App>()
            .UsePlatformDetect()
            .WithInterFont()
            .LogToTrace()
            .UseReactiveUI();  // Or UseMicrosoftDependencyResolver()
}

// App.axaml.cs
public partial class App : Application
{
    public override void Initialize()
    {
        AvaloniaXamlLoader.Load(this);
    }

    public override void OnFrameworkInitializationCompleted()
    {
        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            desktop.MainWindow = new MainWindow
            {
                DataContext = new MainWindowViewModel()
            };
        }
        base.OnFrameworkInitializationCompleted();
    }
}

Advanced Usage

// Platform-specific code
if (OperatingSystem.IsWindows())
    // Windows-specific logic
else if (OperatingSystem.IsMacOS())
    // macOS-specific logic
else if (OperatingSystem.IsLinux())
    // Linux-specific logic

// File dialogs
var topLevel = TopLevel.GetTopLevel(this);
var files = await topLevel.StorageProvider.OpenFilePickerAsync(
    new FilePickerOpenOptions
    {
        Title = "Select File",
        AllowMultiple = false,
        FileTypeFilter = new[]
        {
            new FilePickerFileType("Text Files") { Patterns = new[] { "*.txt" } },
            new FilePickerFileType("All Files") { Patterns = new[] { "*" } }
        }
    });

// Clipboard
var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
await clipboard.SetTextAsync("Copied text");
var text = await clipboard.GetTextAsync();

// Custom rendering
public class CustomControl : Control
{
    public override void Render(DrawingContext context)
    {
        var pen = new Pen(Brushes.Blue, 2);
        context.DrawRectangle(Brushes.LightBlue, pen, new Rect(0, 0, Bounds.Width, Bounds.Height));
        context.DrawLine(pen, new Point(0, 0), new Point(Bounds.Width, Bounds.Height));
        
        var text = new FormattedText("Hello", CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
            new Typeface("Arial"), 16, Brushes.Black);
        context.DrawText(text, new Point(10, 10));
    }
}

// Animations
var animation = new Animation
{
    Duration = TimeSpan.FromSeconds(0.5),
    Children =
    {
        new KeyFrame
        {
            Cue = new Cue(0),
            Setters = { new Setter(OpacityProperty, 0.0) }
        },
        new KeyFrame
        {
            Cue = new Cue(1),
            Setters = { new Setter(OpacityProperty, 1.0) }
        }
    }
};
await animation.RunAsync(myControl);

Building and Publishing

# Build for current platform
dotnet build -c Release

# Publish self-contained
dotnet publish -c Release -r win-x64 --self-contained
dotnet publish -c Release -r osx-arm64 --self-contained
dotnet publish -c Release -r linux-x64 --self-contained

# Single file publish
dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true

# Trimmed publish (smaller binary)
dotnet publish -c Release -r win-x64 --self-contained -p:PublishTrimmed=true

# AOT compilation
dotnet publish -c Release -r win-x64 -p:PublishAot=true

Troubleshooting

IssueSolution
AXAML designer not workingInstall Avalonia extension for your IDE; rebuild project
Binding not updatingEnsure ViewModel implements INotifyPropertyChanged or uses [ObservableProperty]
Control not renderingCheck layout properties (Width/Height/HorizontalAlignment)
Linux display issuesInstall Skia dependencies: libskia-sharp or libX11
Font rendering differentUse WithInterFont() for consistent cross-platform fonts
Style not applyingCheck selector syntax; Avalonia uses CSS-like selectors, not WPF style
Hot reload not workingUse dotnet watch run; check file save triggers
macOS app not signingUse dotnet publish with proper entitlements and code signing
Memory leakDispose subscriptions; use WeakReference for event handlers
Build slowEnable incremental build; consider trimming in release