development-plugin-for-avalonia

Avalonia 12 Migration Guide

Table of Contents

  1. Scope and Status
  2. Source Priority for This Lane
  3. Upgrade Baseline First
  4. Breaking Changes to Fix First
  5. Behavior Changes to Re-verify
  6. Platform-Specific Migration Notes
  7. Additional Source-Backed Deltas Worth Keeping
  8. Code Migration Examples
  9. AOT, Trimming, and Build Notes
  10. Troubleshooting
  11. Full Reference Pointers

Scope and Status

This lane extends the skill with Avalonia 12 migration guidance while keeping the rest of the repository pinned to Avalonia 11.3.12.

Current status for this guide:

Use this guide when you are porting an application, library, or samples from 11.3.12 toward Avalonia 12. The curated guidance below follows the current official Avalonia 12 docs page, while the generated API index and generated migration report in this repo remain source-backed against 12.0.0-rc1 until a newer Avalonia 12 tag exists upstream.

Source Priority for This Lane

Use sources in this order for Avalonia 12 migration work:

  1. the official docs page: Breaking changes in Avalonia 12,
  2. the latest published Avalonia 12 tag on GitHub Releases,
  3. this repository’s generated companion references:
  4. older Avalonia wiki pages as historical background only when they still add detail not present on the docs page.

Current generated evidence for this lane:

Coverage intent:

Upgrade Baseline First

Handle these before you start fixing individual compile errors.

1. Move the runtime baseline

Practical rule:

2. Upgrade all Avalonia packages together

The official docs page is written for the Avalonia 12 release line, not a mixed-package upgrade.

Practical rule:

3. Replace legacy DevTools packaging

The docs page now treats this as a first-class breaking change:

4. Expect compiled bindings by default

<AvaloniaUseCompiledBindingsByDefault> now defaults to true.

Practical rule:

5. Re-check custom startup when you call UseSkia() directly

The text shaper is now configured independently from the renderer.

Practical rule:

6. Remove packages that no longer exist in Avalonia 12

The official docs page calls out these removals:

Breaking Changes to Fix First

These are the migration steps most likely to block the first successful build.

1. Binding hierarchy and code-only binding APIs changed

The docs page and generated diff agree on the core binding break:

Migration rule:

2. Binding plugins are gone, and data-annotations validation is no longer implicitly on

Avalonia 12 removes configurable binding plugins and disables the data-annotations plugin by default.

Migration rule:

3. Clipboard and drag/drop code must move off IDataObject

The docs page is explicit here:

Migration rule:

4. TopLevel can no longer be treated as “the visual root plus every root service”

Avalonia 12 changes the root-host model:

Migration rule:

5. Window decorations were renamed and reworked

This is one of the most visible app-facing breaks:

Migration rule:

6. Input, selection, gesture, and access-key behavior changed

Important official docs-page changes:

Migration rule:

7. Several small but real type-shape changes now surface in app code

The official docs page also calls out these practical breaks:

8. Platform and package removals are real migration work, not footnotes

Treat these as first-pass fixes if your codebase touches them:

Behavior Changes to Re-verify

These are runtime checks, not just compile fixes.

Recommended verification passes:

Platform-Specific Migration Notes

Windows

Android

The docs page now expects this bootstrap shape:

iOS

Browser and Tizen

Headless

Avalonia 12 updates the supported unit-test baselines:

Additional Source-Backed Deltas Worth Keeping

The official docs page is now the primary migration source, but the repository’s source-backed RC1 scan still exposes several app-facing deltas worth keeping in this guide because they affect real migrations.

Focus event and focus-manager changes

The generated 11.3.12 -> 12.0.0-rc1 diff still surfaces important focus changes:

Swipe recognizer changes

Still relevant for touch-first shells and custom paging surfaces:

Preview2 -> RC1 deltas still worth checking in real codebases

These are not from the official docs page, but they still matter if your code moved early:

Code Migration Examples

Target framework, developer tools, and text shaping

Before:

<PropertyGroup>
  <TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="Avalonia.Diagnostics" Version="11.3.12" />
</ItemGroup>
public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>()
        .UseSkia();

After:

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.0" />
  <PackageReference Include="Avalonia.HarfBuzz" Version="12.0.0-rc1" />
</ItemGroup>
public static AppBuilder BuildAvaloniaApp()
    => AppBuilder.Configure<App>()
        .UseSkia()
        .UseHarfBuzz();

Use AttachDeveloperTools() on the Avalonia 12 line.

Binding constructor migration

Before:

using Avalonia.Data;

var nameBinding = new Binding("Customer.Name", BindingMode.TwoWay);
var titleBinding = new ReflectionBinding("WindowTitle", BindingMode.OneWay);

After:

using Avalonia.Data;

var nameBinding = new Binding("Customer.Name")
{
    Mode = BindingMode.TwoWay
};

var titleBinding = new ReflectionBinding("WindowTitle")
{
    Mode = BindingMode.OneWay
};

var dirtyBinding = CompiledBinding.Create<EditorViewModel, bool>(vm => vm.IsDirty);

Clipboard migration

Before:

var data = new DataObject();
data.Set(DataFormats.Text, "some text");

await clipboard.SetDataObjectAsync(data);
var text = await clipboard.GetTextAsync();

After:

var item = new DataTransferItem();
item.Set(DataFormat.Text, "some text");

var data = new DataTransfer();
data.Add(item);

await clipboard.SetDataAsync(data);
var text = await clipboard.TryGetTextAsync();

OpenFileDialog to StorageProvider

Before:

var dialog = new OpenFileDialog
{
    AllowMultiple = false
};

var result = await dialog.ShowAsync(this);

After:

var topLevel = TopLevel.GetTopLevel(this)
    ?? throw new InvalidOperationException("No TopLevel available.");

var files = await topLevel.StorageProvider.OpenFilePickerAsync(
    new FilePickerOpenOptions
    {
        AllowMultiple = false,
        FileTypeFilter = new[]
        {
            new FilePickerFileType("Images")
            {
                Patterns = new[] { "*.png", "*.jpg" }
            }
        }
    });

Window chrome migration

Before:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        SystemDecorations="None"
        ExtendClientAreaChromeHints="NoChrome"
        ExtendClientAreaToDecorationsHint="True">
  <Grid />
</Window>

After:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:chrome="clr-namespace:Avalonia.Controls.Chrome;assembly=Avalonia.Controls"
        WindowDecorations="None"
        ExtendClientAreaToDecorationsHint="True">
  <Grid>
    <Button chrome:WindowDecorationProperties.ElementRole="CloseButton"
            HorizontalAlignment="Right"
            Content="Close" />
  </Grid>
</Window>

Gesture event migration

Before:

<Button Gestures.Pinch="Button_Pinch" />

After:

<Button Pinch="Button_Pinch" />

Android bootstrap migration

Before (11.3.12):

[Activity(MainLauncher = true)]
public class MainActivity : AvaloniaMainActivity<App>
{
}

After (Avalonia 12 line):

[Application]
public class AndroidApp : AvaloniaAndroidApplication<App>
{
    protected AndroidApp(nint javaReference, JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {
    }
}

[Activity(MainLauncher = true)]
public class MainActivity : AvaloniaMainActivity
{
}

If you need dynamic content creation on Android, prefer:

if (ApplicationLifetime is IActivityApplicationLifetime activityLifetime)
{
    activityLifetime.MainViewFactory = () => new MainView();
}

AOT, Trimming, and Build Notes

Troubleshooting

  1. Build suddenly fails on views that worked in 11.3.12.
    • Add x:DataType, switch to {CompiledBinding ...}, or explicitly opt a binding into {ReflectionBinding ...}.
  2. AttachDevTools() or Avalonia.Diagnostics no longer resolves.
    • Move to AvaloniaUI.DiagnosticsSupport and AttachDeveloperTools().
  3. Startup throws No text shaping system configured.
    • If you explicitly call .UseSkia(), add .UseHarfBuzz() and reference Avalonia.HarfBuzz.
  4. Clipboard or drag/drop code no longer compiles.
    • Replace IDataObject/DataObject usage with IDataTransfer / IAsyncDataTransfer / DataTransfer.
  5. Touch selection, gesture handling, or item interception behaves differently.
    • Re-check selection-on-release behavior and remove the old Gestures. prefix in XAML.
  6. Custom title bar hit testing broke after the upgrade.
    • Rename SystemDecorations to WindowDecorations, stop relying on ExtendClientAreaChromeHints, and use WindowDecorationProperties.ElementRole.
  7. Android app starts but the Avalonia view is missing.
    • Confirm the project now has an [Application] type inheriting AvaloniaAndroidApplication<TApp> and that MainActivity inherits plain AvaloniaMainActivity.
  8. Headless test projects stopped restoring or running.
    • Move test packages and adapters to xUnit 3 / NUnit 4 compatible versions.

Full Reference Pointers