| name | avalonia-wpf-migration |
| description | Use when migrating a WPF application to Avalonia, mapping WPF concepts to Avalonia equivalents, or understanding differences between WPF and Avalonia APIs. |
Avalonia WPF Migration
Overview
Avalonia's API is intentionally WPF-like. Most XAML, binding, and MVVM patterns transfer directly. Key differences: styling system, ControlTemplates → ControlThemes, no Triggers, .axaml extension, namespace differences.
Quick Mapping Table
| WPF | Avalonia | Notes |
|---|
.xaml | .axaml | Extension differs |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | xmlns="https://github.com/avaloniaui" | Root namespace |
DependencyProperty | StyledProperty / DirectProperty | See property system |
FrameworkElement | Control | Base for custom controls |
UserControl (base) | UserControl | Same name, similar API |
Control (for lookless) | TemplatedControl | Different base class |
Style with TargetType | ControlTheme with TargetType | Styling lookless controls |
Style with Triggers | Style with nested styles + pseudoclasses | No Triggers in Avalonia |
DataTrigger | Binding + pseudoclass / Classes binding | Pattern change |
ResourceDictionary Source= | ResourceInclude Source="avares://..." | URI scheme differs |
pack://application:,,,/ | avares://AssemblyName/ | Asset URI scheme |
Window.ShowDialog() | window.ShowDialog(owner) | Requires owner parameter |
MessageBox.Show() | No built-in — use dialog or notification | API removed |
Dispatcher.Invoke() | Dispatcher.UIThread.InvokeAsync() | Async-first |
BindingOperations.ClearBinding() | control.ClearValue(prop) | Slightly different |
x:Static | x:Static | Same |
TemplateBinding | TemplateBinding | Same |
RelativeSource Self | {Binding $self.Property} | Different syntax |
RelativeSource AncestorType | {Binding $parent[TypeName].Property} | Different syntax |
EventTrigger + Storyboard | Animation in styles | Different animation system |
BitmapImage + Uri | Bitmap + AssetLoader | Different image loading |
IValueConverter | IValueConverter | Same interface |
IMultiValueConverter | IMultiValueConverter | Same interface |
ObservableCollection<T> | ObservableCollection<T> | Same |
ICommand | ICommand | Same |
CollectionViewSource | DataGridCollectionView | Different class |
Frame + NavigationService | ContentControl + ViewModel swap | MVVM pattern preferred |
Styles: No Triggers
WPF:
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
Avalonia:
<Style Selector="Button:pointerover">
<Setter Property="Background" Value="Red"/>
</Style>
ControlTemplate → ControlTheme
WPF:
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Avalonia:
<ControlTheme TargetType="Button" x:Key="{x:Type Button}">
<Setter Property="Template">
<ControlTemplate>
...
</ControlTemplate>
</Setter>
</ControlTheme>
RelativeSource Binding
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Title}"/>
<TextBlock Text="{Binding $parent[Window].Title}"/>
<TextBlock Text="{Binding $self.Tag}"/>
<TextBlock Text="{Binding $parent.Tag}"/>
Asset URIs
<Image Source="pack://application:,,,/Assets/logo.png"/>
<Image Source="avares://MyApp/Assets/logo.png"/>
Dialogs
MessageBox.Show("Hello");
var dialog = new OpenFileDialog();
dialog.ShowDialog();
var result = await new ConfirmDialog("Are you sure?").ShowDialog<bool>(owner);
Dispatcher
Dispatcher.Invoke(() => { });
await Dispatcher.UIThread.InvokeAsync(() => { });
Dispatcher.UIThread.Post(() => { });
What Works Without Changes
INotifyPropertyChanged implementations
ICommand implementations
ObservableCollection<T> usage
IValueConverter implementations
- Data binding patterns (with namespace/syntax adjustments)
- Most MVVM framework code (ReactiveUI, CommunityToolkit.Mvvm)
ItemsControl, ListBox, DataGrid (similar APIs)
- Grid, StackPanel, DockPanel layouts (same attached properties)
Common Mistakes
- Using WPF Trigger syntax — silently ignored or compile error
- Using
pack:// URIs — assets not found
- Calling
MessageBox.Show() — doesn't exist
- Using
FrameworkElement as base class — use Control
Style TargetType without x:Key for ControlThemes — template not applied