У попередній статті ми детально розібрали XAML: namespace'и, ResourceDictionary, StaticResource та DynamicResource. Все це стосувалося WPF. Але якщо ви вже знаєте WPF XAML — ви знаєте 90% Avalonia XAML.
Залишилися 10%. Саме вони відрізняють файл MainWindow.xaml (WPF) від MainWindow.axaml (Avalonia). Ці відмінності невеликі, але якщо не знати про них — компілятор і IDE будуть лякати незрозумілими помилками на рівному місці.
Ця стаття — коротка шпаргалка: що змінилося, чому і як правильно.
.axaml — розширення файлів Avalonia XAML (Avalonia XAML, не WPF XAML). xmlns="https://github.com/avaloniaui" — головний namespace Avalonia, аналог довгого WPF URI. using: — скорочений синтаксис підключення CLR namespace в Avalonia (using:MyApp замість clr-namespace:MyApp). ThemeResource — Avalonia-специфічний Markup Extension для ресурсів, що реагують на зміну теми (Light/Dark). Avalonia Previewer — вбудований інструмент попереднього перегляду .axaml у Rider та VS.Перш ніж перейти до відмінностей — важливо зафіксувати, що не змінюється:
| Концепція | WPF | Avalonia |
|---|---|---|
| Синтаксис тегів | <Button Content="OK"/> | ✅ Той самий |
xmlns:x namespace | http://schemas.microsoft.com/winfx/2006/xaml | ✅ Той самий |
x:Name, x:Key, x:Class | ✅ Є | ✅ Є |
| Property Element Syntax | <Button.Background>...</Button.Background> | ✅ Той самий |
| Content Property | <Button>Text</Button> | ✅ Той самий |
| Type Converters | Background="Red" → Brush | ✅ Той самий |
| Attached Properties | Grid.Row="1" | ✅ Той самий |
| StaticResource / DynamicResource | ✅ Є | ✅ Є |
ResourceDictionary + x:Key | ✅ Є | ✅ Є |
| MergedDictionaries | ✅ Є | ✅ Є |
Весь WPF XAML-синтаксис, що ви вивчали — валідний в Avalonia. Це не перебільшення, а факт архітектурного рішення команди Avalonia: зберегти максимальну сумісність з WPF на рівні розмітки.
.axaml замість .xamlПерше, що помічаєш у структурі Avalonia-проєкту — всі файли розмітки мають розширення .axaml, а не .xaml.
WPF: MainWindow.xaml + MainWindow.xaml.cs
Avalonia: MainWindow.axaml + MainWindow.axaml.cs
.axamlПричина суто практична — конфлікт інструментів у Visual Studio.
Коли у проєкті є файл з розширенням .xaml, Visual Studio автоматично відкриває його у WPF XAML Designer. Дизайнер намагається завантажити WPF-типи, не знаходить їх у Avalonia-збірці і падає з помилками. Це спричиняло масу плутанини у проєктах, де розробники використовували і WPF, і Avalonia.
Розширення .axaml (Avalonia XAML) не асоційоване з WPF Designer — Visual Studio відкриває його як звичайний текстовий XML-файл, а Avalonia Extension підключає власний previewer.
.axaml — це конвенція, не технічна вимога. Avalonia чудово компілює і .xaml файли — синтаксис абсолютно однаковий. Але використання .axaml є офіційною рекомендацією команди Avalonia і це те, що генерують усі шаблони.
// .axaml.cs виглядає ідентично до .xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); // Та сама магія, що в WPF
}
}
Найпомітніша відмінність у першому рядку XAML-файлу — різний URI у xmlns:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyWpfApp.MainWindow"
Title="WPF вікно">
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyAvaloniaApp.MainWindow"
Title="Avalonia вікно">
Що змінилося:
xmlns (default namespace): WPF URI → Avalonia URIxmlns:x (XAML-директиви): незмінний — той самий http://schemas.microsoft.com/winfx/2006/xamlAvalonia URI https://github.com/avaloniaui — так само просто ідентифікатор-рядок, як і WPF URI. Він теж не завантажує нічого з GitHub. Просто більш лаконічний і легко запам'ятовується.
Аналогічно до WPF (де XmlnsDefinition реєструвала маппінг у сборці), в Avalonia збірки також реєструють маппінг своїх namespace'ів на https://github.com/avaloniaui:
// В Avalonia.Controls.dll (спрощено)
[assembly: XmlnsDefinition(
"https://github.com/avaloniaui",
"Avalonia.Controls")]
[assembly: XmlnsDefinition(
"https://github.com/avaloniaui",
"Avalonia.Media")]
[assembly: XmlnsDefinition(
"https://github.com/avaloniaui",
"Avalonia.Layout")]
// ... інші namespace'и
Тому <Button>, <TextBlock>, <Grid>, <StackPanel> — все це доступне без prefix'а, так само як у WPF.
using: замість clr-namespace:У WPF для підключення власних класів ви писали:
xmlns:local="clr-namespace:MyApp"
xmlns:vm="clr-namespace:MyApp.ViewModels"
xmlns:ctrl="clr-namespace:MyApp.Controls"
В Avalonia є скорочений еквівалентний синтаксис через using::
xmlns:local="using:MyApp"
xmlns:vm="using:MyApp.ViewModels"
xmlns:ctrl="using:MyApp.Controls"
Обидва синтаксиси підтримуються в Avalonia. Але using: є рекомендованим — він коротший і більш читабельний. Всі шаблони Avalonia генерують саме using:.
Для класів з іншої збірки формат такий самий, як і в WPF — потрібен assembly=:
<!-- WPF -->
xmlns:ext="clr-namespace:SomeLib.Extensions;assembly=SomeLib"
<!-- Avalonia (також працює clr-namespace, але using: скорочує запис) -->
xmlns:ext="using:SomeLib.Extensions;assembly=SomeLib"
clr-namespace: не потребує заміни. Він по-справжньому сумісний. Замінюйте на using: лише за бажанням або при рефакторингу.ThemeResourceМеханізм ресурсів у Avalonia — той самий що й у WPF: ResourceDictionary, x:Key, StaticResource, DynamicResource, MergedDictionaries. Всі ці концепції переносяться без змін.
Але Avalonia додає один новий Markup Extension — ThemeResource.
ThemeResourceThemeResource — це спеціалізований аналог DynamicResource, який розуміє Avalonia-теми (Light / Dark / HighContrast). Він шукає ресурс у активній темі застосунку, а не тільки у звичайних ResourceDictionary.
<!-- Ресурс реагує на перемикання теми Light/Dark -->
<TextBlock Foreground="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
<!-- DynamicResource — шукає у ResourceDictionary ланцюжку -->
<TextBlock Foreground="{DynamicResource MyCustomBrush}"/>
<!-- StaticResource — одноразовий пошук при ініціалізації -->
<Button Background="{StaticResource PrimaryBrush}"/>
ThemeResource особливо важливий при використанні Avalonia Fluent Theme: всі системні кольори теми (фон вікна, колір тексту, колір акценту) задані через ThemeResource. Якщо ви хочете, щоб ваш кастомний контрол "підхоплював" активну тему автоматично — використовуйте ThemeResource для системних кольорів.
<!-- Це 100% однаково у WPF і Avalonia -->
<Window.Resources>
<SolidColorBrush x:Key="MyBrush" Color="#2563eb"/>
<sys:String x:Key="MyString">Привіт</sys:String>
</Window.Resources>
<!-- StaticResource та DynamicResource — без змін -->
<Button Background="{StaticResource MyBrush}"/>
<TextBlock Foreground="{DynamicResource MyBrush}"/>
<!-- MergedDictionaries — без змін -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="avares://MyApp/Resources/Colors.axaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Одна деталь: у Avalonia для embedded-ресурсів (файлів, вбудованих у збірку) шлях починається з avares:// протоколу замість звичайного відносного шляху:
<!-- WPF: відносний шлях -->
<ResourceDictionary Source="Resources/Colors.xaml"/>
<!-- Avalonia: avares:// протокол для embedded-ресурсів -->
<ResourceDictionary Source="avares://MyAvaloniaApp/Resources/Colors.axaml"/>
Де MyAvaloniaApp — назва збірки (Assembly Name з .csproj). Це трохи детальніше, але дозволяє точно адресувати ресурси з різних збірок.
avares:// (Avalonia Resources) — URI-схема для доступу до файлів, вбудованих у GZ-архів всередині Avalonia-збірки. Аналог pack://application:,,,/ у WPF, але читабельніший. При розробці на Desktop можна використовувати і відносні шляхи — Avalonia їх теж розуміє, але avares:// є правильнішим для production.У WPF є вбудований XAML Designer у Visual Studio — панель, що показує вікно поруч з кодом. Він часто "ламається", вимагає перезапуску і підтримує лише Windows.
Avalonia пішла іншим шляхом: Avalonia Previewer — окремий процес, що рендерить .axaml через власний движок Avalonia і показує результат у боковій панелі IDE.
.axaml файл.axaml файл| Аспект | WPF XAML Designer | Avalonia Previewer |
|---|---|---|
| Платформа | Windows only | Windows, macOS, Linux |
| Рендеринг | WPF (DirectX) | Реальний Avalonia (Skia) |
| Точність | Часто відрізняється від runtime | Піксельно точний |
| Стабільність | Часто зависає, падає | Стабільніший |
| Hot Reload | ✅ Є | ✅ Є |
| DataContext у preview | Складно налаштувати | d:DataContext або DesignData |
Avalonia Previewer рендерить той самий Skia-рушій, що й реальний застосунок — тому те, що ви бачите у preview, точно відповідає результату при запуску.
Ось side-by-side порівняння 15 ключових конструкцій:
| Конструкція | WPF XAML | Avalonia XAML |
|---|---|---|
| Розширення файлу | .xaml | .axaml (рекомендовано) |
| Default namespace | http://schemas.microsoft.com/winfx/2006/xaml/presentation | https://github.com/avaloniaui |
| XAML directive namespace | http://schemas.microsoft.com/winfx/2006/xaml | ✅ Той самий |
| Клас window | : Window | : Window |
| x:Class | x:Class="MyApp.MainWindow" | ✅ Той самий |
| x:Name | x:Name="myButton" | ✅ Той самий |
| Підключення CLR namespace | clr-namespace:MyApp | using:MyApp (або clr-namespace:) |
| StaticResource | {StaticResource MyKey} | ✅ Той самий |
| DynamicResource | {DynamicResource MyKey} | ✅ Той самий |
| Тематичний ресурс | ❌ Немає | {ThemeResource SystemBrush} |
| Шлях до ресурсів | Resources/Colors.xaml | avares://App/Resources/Colors.axaml |
| Attached Properties | Grid.Row="1" | ✅ Той самий |
| Property Element Syntax | <Button.Background> | ✅ Той самий |
| x:Key у ResourceDictionary | x:Key="MyKey" | ✅ Той самий |
| Design-time namespace | xmlns:d="..." | ✅ Той самий |
Завдання: Створіть Avalonia-застосунок (dotnet new avalonia.app) і відтворіть у MainWindow.axaml ресурсний підхід зі статті 05.
Кроки:
Window.Resources 4 SolidColorBrush з x:Key{StaticResource} у кількох кнопках та TextBlockЗверніть увагу:
xmlns на https://github.com/avaloniaui.axaml, а не .xamlButton, TextBlock, StackPanel — ті ж самі назвиПеревірка: dotnet run, вікно відкривається, кольори застосовані через ресурси.
Завдання: Візьміть MainWindow.xaml з WPF-застосунку зі статті 05 (з MergedDictionaries та ресурсами) і портуйте його на Avalonia.
Що треба змінити:
.xaml → .axamlxmlns URIclr-namespace: на using: (за бажанням)avares://...).xaml → .axamlЗапустіть (dotnet run) та перевірте, що UI виглядає так само.
Що треба знати: Весь синтаксис тегів залишається незмінним — лише namespace та шляхи.
Завдання: Зробіть Avalonia-застосунок, UI якого реагує на перемикання теми через ThemeResource та власні ресурси.
Кроки:
Resources/LightTheme.axaml та Resources/DarkTheme.axaml — обидва мають ключі AppBackground, AppForeground, AppAccentLightTheme.axaml у App.axaml через MergedDictionaries як перший словникMainWindow.axaml прив'яжіть Window.Background, TextBlock.Foreground та Button.Background через {DynamicResource ...}MergedDictionaries[0]Додатково: Спробуйте замінити один зі своїх ресурсів на {ThemeResource SystemControlForegroundBaseHighBrush} — подивіться як він поводиться при перемиканні RequestedThemeVariant у App.axaml між Light та Dark.
Що треба знати: avares:// шляхи, Application.Current.Resources.MergedDictionaries, ThemeResource, RequestedThemeVariant.
Простори імен та ресурси XAML
Розбираємо xmlns-простори імен, підключення власних класів у XAML, ResourceDictionary, StaticResource vs DynamicResource та організацію ресурсів у великих проєктах.
Розширення розмітки XAML (Markup Extensions)
Вивчаємо механізм фігурних дужок {…} у XAML: MarkupExtension, ProvideValue, вбудовані x:Static/x:Type/x:Null/x:Array/Binding, перший погляд на Binding та створення Custom Markup Extension.