Якщо ви вивчали попередні чотири статті Блоку 4 — ви вже знаєте Button, TextBox, CheckBox, ComboBox, DatePicker, GroupBox, TabControl та ще десятки контролів. Вся ця бібліотека описана через WPF як основний фрейм. Тепер час розібратись: що з усього цього доступне в Avalonia, і де є відмінності?
Хороша новина: переважна більшість контролів повністю сумісна. Ті ж назви, ті ж властивості, той самий XAML-синтаксис. Різниця між WPF і Avalonia у сфері контролів — це переважно деталі: окремі властивості мають іншу назву, деякі контроли відсутні або замінені кращими альтернативами, а деякі — навпаки, є в Avalonia але відсутні у WPF.
Ця стаття — ваш практичний чеклист переносу знань. Вона не дублює зміст Блоку 4, а доповнює його: там описано "як", тут — "що змінюється в Avalonia".
Перш за все — великий зведений огляд, аби ви одразу знали, на що чекати в кожному розділі.
| Контрол WPF | Avalonia | Статус | Головна відмінність |
|---|---|---|---|
Button | Button | ✅ Повна | CornerRadius вбудований |
RepeatButton | RepeatButton | ✅ Повна | Без змін |
ToggleButton | ToggleButton | ✅ Повна | Без змін |
Image | Image | ✅ Повна | Інший синтаксис URI |
ProgressBar | ProgressBar | ✅ Повна | Без змін |
Slider | Slider | ✅ Повна | Без змін |
ToolTip | ToolTip | ✅ Повна | ToolTipService → ToolTip.* |
Popup | Popup | ✅ Повна | Менше Placement-режимів |
TextBlock | TextBlock | ✅ Повна | PlaceholderText є |
Label | Label | ⚠️ Обмежена | Немає AccessKey через _ |
TextBox | TextBox | ✅ Повна | PlaceholderText вбудований |
PasswordBox | TextBox (маскований) | ⚠️ Замінено | Немає окремого PasswordBox |
RichTextBox | ❌ Відсутній | 🔴 Відсутній | Замінено сторонніми бібліотеками |
FlowDocument | ❌ Відсутній | 🔴 Відсутній | WPF-специфічний |
CheckBox | CheckBox | ✅ Повна | Без змін |
RadioButton | RadioButton | ✅ Повна | Без змін |
ComboBox | ComboBox | ✅ Повна | IsEditable підтримується |
ListBox | ListBox | ✅ Повна | Без змін |
DatePicker | DatePicker | ⚠️ Часткова | BlackoutDates відсутній |
Calendar | Calendar | ⚠️ Часткова | Менше режимів SelectionMode |
GroupBox | GroupBox | ✅ Повна | Без змін |
Expander | Expander | ✅ Повна | Без змін |
TabControl | TabControl | ✅ Повна | Без змін |
StatusBar | StatusBar | ✅ Повна | Без змін |
ToolTipService | ToolTip (attached) | ⚠️ Інший синтаксис | Без окремого класу Service |
Button, RepeatButton та ToggleButton в Avalonia є — повний набір з тими ж властивостями та поведінкою. IsDefault, IsCancel, Click, IsChecked, Delay/Interval, GroupName — усе на місці. Синтаксис XAML ідентичний:
<!-- Цей XAML однаково працює у WPF і Avalonia -->
<StackPanel Spacing="8">
<Button Content="Зберегти" IsDefault="True" Click="Save_Click"/>
<Button Content="Скасувати" IsCancel="True"/>
<RepeatButton Content="+" Delay="500" Interval="100"/>
<ToggleButton Content="Жирний" x:Name="boldToggle"/>
</StackPanel>
У WPF кнопки мають прямі кути за замовчуванням. Щоб зробити заокруглені кути — потрібен кастомний Style або ControlTemplate. У Avalonia клас Button успадковує від TemplatedControl, що вже включає властивість CornerRadius. Це означає:
<!-- WPF: CornerRadius вимагає окремого стилю або ControlTemplate -->
<Button Content="Підтвердити">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="8" Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<!-- Avalonia: CornerRadius — вбудована властивість кнопки -->
<Button Content="Підтвердити" CornerRadius="8"/>
Це одне з найпомітніших зручних покращень Avalonia. Ви побачите CornerRadius і на TextBox, ComboBox, та інших контролах — без жодного стилю.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="Кнопки з CornerRadius (Avalonia-специфічно):"
Foreground="Gray" FontSize="13"/>
<WrapPanel ItemSpacing="8">
<Button Content="CornerRadius=4" CornerRadius="4" Padding="12 6"/>
<Button Content="CornerRadius=8" CornerRadius="8" Padding="12 6"
Background="#6366F1" Foreground="White"/>
<Button Content="Повністю круглі" CornerRadius="20" Padding="12 6"
Background="#10B981" Foreground="White"/>
<Button Content="Лише зверху" CornerRadius="8 8 0 0" Padding="12 6"
Background="#F59E0B" Foreground="White"/>
</WrapPanel>
<TextBlock Text="У WPF для цього потрібен повний ControlTemplate override."
Foreground="Gray" FontSize="11" FontStyle="Italic"/>
</StackPanel>
CornerRadius у Avalonia приймає 1 або 4 значення (як Thickness): "8" — всі кути однакові; "8,8,0,0" — верхній лівий, верхній правий, нижній правий, нижній лівий.
У WPF зображення з ресурсів проєкту адресуються через Pack URI:
<!-- WPF: Pack URI -->
<Image Source="pack://application:,,,/Assets/logo.png"/>
<!-- WPF: скорочений відносний запис (тільки всередині тієї ж збірки) -->
<Image Source="/Assets/logo.png"/>
В Avalonia Pack URI не підтримується. Замість нього — схема avares:// (Avalonia Resources), де після avares:// вказується назва збірки, потім шлях до файлу всередині неї:
<!-- Avalonia: avares:// URI -->
<Image Source="avares://MyApp/Assets/logo.png"/>
де MyApp — це <AssemblyName> вашого проєкту з .csproj. Переконайтесь, що файл зображення у .csproj помічений як AvaloniaResource:
<!-- .csproj — Build Action для Avalonia -->
<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
</ItemGroup>
У WPF аналог — <Resource Include="Assets\**"/>. Назва Build Action відрізняється, але принцип той самий: файл упаковується у збірку.
У WPF BitmapImage з Pack URI:
// WPF
var uri = new Uri("pack://application:,,,/Assets/logo.png");
var bitmap = new BitmapImage(uri);
image.Source = bitmap;
В Avalonia — Bitmap або IBitmap через AssetLoader:
// Avalonia
var uri = new Uri("avares://MyApp/Assets/logo.png");
var bitmap = new Bitmap(AssetLoader.Open(uri));
image.Source = bitmap;
AssetLoader — статичний клас Avalonia (Avalonia.Platform.AssetLoader), що відкриває потоки до ресурсів. Він замінює WPF-підхід через Application.GetResourceStream.
Усі чотири режими Stretch (None, Fill, Uniform, UniformToFill) і властивість ClipToBounds в Avalonia ідентичні WPF. XAML-код для Image з будь-яким Stretch переноситься без змін — необхідно лише змінити URI.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="Image: avares:// URI (Avalonia-специфічно):"
Foreground="Gray" FontSize="13"/>
<TextBlock FontSize="11" Foreground="#6366F1" FontFamily="Consolas"
Text="Source="avares://MyApp/Assets/photo.jpg""/>
<WrapPanel ItemSpacing="8">
<Border Width="120" Height="80" BorderBrush="#333" BorderThickness="1">
<Image Source="https://dummyimage.com/640x360/000/fff.png&text=Stretch.UniformToFill"
Stretch="UniformToFill" ClipToBounds="True"/>
</Border>
<Border Width="120" Height="80" BorderBrush="#333" BorderThickness="1">
<Image Source="https://dummyimage.com/640x360/000/fff.png&text=Stretch.Uniform"
Stretch="Uniform"/>
</Border>
<Border Width="120" Height="80" BorderBrush="#333" BorderThickness="1">
<Image Source="https://dummyimage.com/640x360/000/fff.png&text=Stretch.Fill"
Stretch="Fill"/>
</Border>
</WrapPanel>
<TextBlock Text="Stretch — ідентичний WPF. Лише URI відрізняється."
Foreground="Gray" FontSize="11" FontStyle="Italic"/>
</StackPanel>
У WPF затримки та розташування ToolTip регулюються через статичний клас ToolTipService з attached properties:
<!-- WPF: ToolTipService для налаштування поведінки -->
<Button Content="Зберегти"
ToolTip="Ctrl+S"
ToolTipService.InitialShowDelay="200"
ToolTipService.ShowDuration="8000"
ToolTipService.Placement="Bottom"/>
В Avalonia немає окремого класу ToolTipService. Замість нього — attached properties безпосередньо на класі ToolTip:
<!-- Avalonia: ToolTip.* замість ToolTipService.* -->
<Button Content="Зберегти"
ToolTip.Tip="Ctrl+S"
ToolTip.ShowDelay="200"
ToolTip.Placement="Bottom"/>
Зверніть: замість ToolTip="..." (скорочений запис WPF) — ToolTip.Tip="...". У Avalonia скорочений запис ToolTip="..." теж підтримується для простого рядка.
Таблиця відповідностей:
WPF (ToolTipService.*) | Avalonia (ToolTip.*) |
|---|---|
ToolTipService.InitialShowDelay | ToolTip.ShowDelay |
ToolTipService.ShowDuration | (немає прямого аналога) |
ToolTipService.Placement | ToolTip.Placement |
ToolTipService.IsEnabled | ToolTip.IsOpen (програмно) |
Структурований ToolTip із XAML-вмістом zapisується однаково в обох фреймворках:
<!-- Однаково у WPF і Avalonia -->
<Button Content="⚙ Параметри" Padding="12,8">
<Button.ToolTip>
<ToolTip>
<StackPanel Spacing="6">
<TextBlock Text="Параметри застосунку" FontWeight="Bold"/>
<TextBlock Text="Відкриває вікно налаштувань."
Foreground="Gray" TextWrapping="Wrap"/>
</StackPanel>
</ToolTip>
</Button.ToolTip>
</Button>
В Avalonia альтернативний (коротший) синтаксис через ToolTip.Tip:
<!-- Avalonia: ToolTip.Tip з вбудованим XAML -->
<Button Content="⚙ Параметри">
<ToolTip.Tip>
<StackPanel Spacing="6">
<TextBlock Text="Параметри застосунку" FontWeight="Bold"/>
<TextBlock Text="Відкриває вікно налаштувань." Foreground="Gray"/>
</StackPanel>
</ToolTip.Tip>
</Button>
У WPF TextBox — "голий": немає вбудованого placeholder-тексту і немає CornerRadius. В Avalonia TextBox отримав обидва:
| Властивість | WPF | Avalonia |
|---|---|---|
PlaceholderText | ❌ Немає (потрібен code-behind або Style) | ✅ Вбудована |
CornerRadius | ❌ Тільки через ControlTemplate | ✅ Вбудована |
Text | ✅ | ✅ |
AcceptsReturn | ✅ | ✅ |
AcceptsTab | ✅ | ✅ |
MaxLength | ✅ | ✅ |
IsReadOnly | ✅ | ✅ |
TextWrapping | ✅ | ✅ |
TextChanged | ✅ | ✅ |
SpellCheck.IsEnabled | ✅ | ❌ Немає |
У WPF, щоб зробити placeholder, потрібно або code-behind (GotFocus/LostFocus), або Style з Trigger. В Avalonia це одна властивість:
<!-- WPF: placeholder через GotFocus/LostFocus у code-behind (обхідний шлях) -->
<TextBox x:Name="searchBox" Foreground="Gray"
GotFocus="SearchBox_GotFocus"
LostFocus="SearchBox_LostFocus"/>
<!-- Avalonia: PlaceholderText — нативна властивість -->
<TextBox PlaceholderText="Пошук..." CornerRadius="8" Padding="10,6"/>
Також є PlaceholderForeground для кольору placeholder-тексту:
<TextBox PlaceholderText="Введіть email..."
PlaceholderForeground="#9CA3AF"
CornerRadius="6" Padding="10,6"/>
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="TextBox з PlaceholderText (тільки Avalonia):"
Foreground="Gray" FontSize="13"/>
<TextBox PlaceholderText="Ваше ім'я..." CornerRadius="8" Padding="10 6"/>
<TextBox PlaceholderText="Email-адреса..." CornerRadius="8" Padding="10 6"/>
<TextBox PlaceholderText="Повідомлення..."
AcceptsReturn="True" TextWrapping="Wrap"
Height="80" CornerRadius="8" Padding="10 6"
ScrollViewer.VerticalScrollBarVisibility="Auto"/>
<TextBlock Text="У WPF placeholder потребує GotFocus/LostFocus або Style+Trigger."
Foreground="Gray" FontSize="11" FontStyle="Italic"/>
</StackPanel>
У WPF Label підтримує AccessKey через символ _ у рядку Content та властивість Target. В Avalonia AccessKey через _ не підтримується. Label в Avalonia — звичайний ContentControl без особливої логіки AccessKey.
Альтернатива: у Avalonia є власний механізм гарячих клавіш через HotKeyManager або KeyBinding, але це зовсім інший підхід, що виходить за рамки цієї статті.
<!-- WPF: AccessKey через підкреслення -->
<Label Content="_Ім'я:" Target="{Binding ElementName=nameBox}"/>
<TextBox x:Name="nameBox"/>
<!-- Avalonia: Label без AccessKey — просто TextBlock або Label без Target -->
<Label Content="Ім'я:"/>
<TextBox/>
AccessKey-поведінки в Avalonia — використовуйте KeyBinding на рівні Window або UserControl. Властивість Target у Label в Avalonia присутня, але логіка активації через Alt+Key не вбудована.Avalonia свідомо відмовилася від окремого PasswordBox. Замість нього — звичайний TextBox із властивістю PasswordChar. Це і є механізм маскування:
<!-- WPF: окремий контрол PasswordBox -->
<PasswordBox PasswordChar="●" MaxLength="64"
PasswordChanged="PasswordBox_Changed"/>
<!-- Avalonia: TextBox з PasswordChar -->
<TextBox PasswordChar="●" MaxLength="64"
TextChanged="PasswordBox_Changed"/>
Ключова різниця — властивість для читання значення:
| Аспект | WPF PasswordBox | Avalonia TextBox з PasswordChar |
|---|---|---|
| Читання значення | PasswordBox.Password (string) | TextBox.Text (string) |
| Безпечне зберігання | SecurePassword (SecureString) | ❌ Немає SecureString |
| Подія зміни | PasswordChanged | TextChanged |
| Маскування | Вбудоване | Через PasswordChar |
Відсутність SecureString — суттєва відмінність. WPF PasswordBox зберігає пароль у SecureString (шифрування в пам'яті), Avalonia TextBox — у звичайному string. Для застосунків, де безпека пароля критична, використовуйте криптографічне хешування відразу в TextChanged-обробнику та очищайте Text після:
// Avalonia: читаємо та одразу хешуємо
private void PasswordBox_TextChanged(object? sender, TextChangedEventArgs e)
{
if (sender is TextBox box)
{
string raw = box.Text ?? "";
// Хешуємо та зберігаємо хеш, а не сам пароль
_passwordHash = BCrypt.Net.BCrypt.HashPassword(raw);
UpdateStrengthIndicator(raw.Length);
}
}
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="TextBox з PasswordChar (замінює WPF PasswordBox):"
Foreground="Gray" FontSize="13"/>
<TextBox PasswordChar="●"
PlaceholderText="Пароль..."
MaxLength="64"
CornerRadius="8" Padding="10 6"/>
<TextBox PasswordChar="*"
PlaceholderText="Символ * (класичний вигляд)"
CornerRadius="8" Padding="10 6"/>
<TextBlock Text="SecureString відсутній — хешуйте пароль відразу в TextChanged."
Foreground="#F59E0B" FontSize="11"/>
</StackPanel>
RichTextBox та FlowDocument — повністю WPF-специфічні компоненти. В Avalonia їх немає. Якщо ваш застосунок потребує редактора форматованого тексту в Avalonia — є кілька шляхів:
1. AvalonEdit — популярна бібліотека редактора коду та тексту, портована для Avalonia:
<!-- NuGet: AvaloniaEdit -->
<avalonEdit:TextEditor SyntaxHighlighting="C#"
FontFamily="Consolas" FontSize="13"/>
2. Sᴛарий підхід через SelectableTextBlock — Avalonia має SelectableTextBlock, що дозволяє виділяти та копіювати текст, але не редагувати його. Підходить для read-only відображення.
3. InlineCollection у TextBlock — для статичного форматованого тексту (жирний, курсивний, гіперпосилання) Avalonia підтримує ті ж Inline-класи, що й WPF: Run, Bold, Italic, Underline, Hyperlink, LineBreak. Синтаксис ідентичний і переноситься без змін.
RichTextBox + FlowDocument з WPF в Avalonia напряму — ці класи просто відсутні у Avalonia API. Якщо ваш проєкт критично залежить від RichTextBox, розгляньте або залишення WPF, або міграцію на AvaloniaEdit з переписуванням логіки форматування.Хороша новина: контроли вибору в Avalonia повністю сумісні з WPF. IsChecked, IsThreeState, GroupName, SelectionMode, SelectedItem, SelectedIndex, IsEditable — усі ці властивості присутні і працюють ідентично.
Єдиний нюанс, вже знайомий: ComboBox, ListBox та TextBox в Avalonia мають вбудований CornerRadius:
<!-- Avalonia: ComboBox з CornerRadius -->
<ComboBox CornerRadius="6" SelectedIndex="0">
<ComboBoxItem Content="🇺🇦 Українська"/>
<ComboBoxItem Content="🇬🇧 English"/>
</ComboBox>
Все інше — Checked/Unchecked/Indeterminate, SelectionChanged, SelectedItems, Items.Add() — переноситься без змін.
Найбільша відмінність DatePicker в Avalonia — відсутність BlackoutDates. У WPF цей API дозволяв заблокувати конкретні дати або діапазони (наприклад, вихідні або минулі дати). В Avalonia такого API немає.
Таблиця відмінностей:
| Властивість | WPF | Avalonia |
|---|---|---|
SelectedDate | ✅ | ✅ |
DisplayDateStart | ✅ | ✅ |
DisplayDateEnd | ✅ | ✅ |
BlackoutDates | ✅ | ❌ Відсутній |
SelectedDateChanged | ✅ | ✅ (SelectedDateChanged) |
FirstDayOfWeek | ✅ | ✅ |
SelectedDateFormat | ✅ | ⚠️ Обмежена |
Реалізація блокування дат в Avalonia потребує власного підходу — або через кастомний CalendarDayButtonStyle (тема стилізації), або через перехоплення SelectedDateChanged і відхилення невалідних значень:
// Avalonia: відхиляємо вихідні у SelectedDateChanged
private void DatePicker_SelectedDateChanged(object? sender,
DatePickerSelectedValueChangedEventArgs e)
{
if (e.NewDate is DateTimeOffset dto)
{
var day = dto.DayOfWeek;
if (day == DayOfWeek.Saturday || day == DayOfWeek.Sunday)
{
// Повертаємо попереднє значення
if (sender is DatePicker dp)
dp.SelectedDate = e.OldDate;
// Повідомляємо користувача
statusText.Text = "Вихідні дні недоступні для вибору.";
}
}
}
SelectedDate має тип DateTimeOffset?, а не DateTime? як у WPF. Це важлива відмінність при роботі з датами — DateTimeOffset містить інформацію про часовий пояс. Для конвертації: dto.DateTime або dto.UtcDateTime.В Avalonia Calendar підтримує режими Single та Multiple, але SingleRange та MultipleRange відсутні. Якщо потрібен вибір діапазону дат — розгляньте бібліотеки типу Avalonia.Controls.DateTimePicker (від спільноти) або реалізуйте власний.
Avalonia як сучасна платформа містить ряд контролів, яких немає у WPF взагалі. Ось ключові, що стосуються тем цього Блоку:
SplitView — контрол для "гамбургер-меню" інтерфейсів. Має Pane (бічна панель, що ховається) та Content (основна область). Режими Inline, Overlay, CompactInline, CompactOverlay керують поведінкою при відкритті/закритті.
<!-- Тільки Avalonia -->
<SplitView IsPaneOpen="True" OpenPaneLength="200"
DisplayMode="CompactInline">
<SplitView.Pane>
<StackPanel Spacing="4" Margin="8">
<Button Content="🏠 Головна" HorizontalAlignment="Stretch"/>
<Button Content="⚙ Налаштування" HorizontalAlignment="Stretch"/>
</StackPanel>
</SplitView.Pane>
<Grid>
<TextBlock Text="Основний вміст" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</SplitView>
NumericUpDown — стандартний контрол Avalonia для введення числових значень зі стрілками вгору/вниз. У WPF аналога немає (все реалізувалось через RepeatButton плюс TextBox).
decimal? дозволяє зберігати null (порожнє поле).null — без обмежень.1."N2" (два знаки після коми), "F0" (ціле), "P0" (відсотки). Будь-який стандартний формат .NET.true.Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="NumericUpDown — тільки Avalonia:"
FontWeight="SemiBold" FontSize="14"/>
<Grid RowSpacing="8" ColumnSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Кількість (ціле):"
VerticalAlignment="Center" Foreground="Gray"/>
<NumericUpDown Grid.Row="0" Grid.Column="1"
Value="1" Minimum="1" Maximum="99"
Increment="1" FormatString="N0"
CornerRadius="6"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Ціна ($):"
VerticalAlignment="Center" Foreground="Gray"/>
<NumericUpDown Grid.Row="1" Grid.Column="1"
Value="9.99" Minimum="0" Increment="0.50"
FormatString="F2" CornerRadius="6"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Рейтинг (0–10):"
VerticalAlignment="Center" Foreground="Gray"/>
<NumericUpDown Grid.Row="2" Grid.Column="1"
Value="7" Minimum="0" Maximum="10"
Increment="0.5" FormatString="F1"
CornerRadius="6"/>
</Grid>
<TextBlock Text="У WPF потрібно: TextBox + RepeatButton + код валідації."
Foreground="Gray" FontSize="11" FontStyle="Italic"/>
</StackPanel>
AutoCompleteBox — один з найбільш затребуваних контролів, якого немає у WPF. Це TextBox з вбудованим dropdown-списком підказок, що фільтруються по введеному тексту. У WPF подібне реалізовувалось вручну через ComboBox з IsEditable="True" + логіка фільтрації в code-behind. В Avalonia — нативний контрол.
IEnumerable<string> або IEnumerable<T> з вказанням ValueMemberPath.StartsWith (рядки, що починаються з введеного), Contains (рядки, що містять введене), StartsWithCaseSensitive, ContainsCaseSensitive, Custom (власна функція фільтрації).1. 0 — список відкривається одразу при фокусі.Text — можна обрати елемент зі списку (SelectedItem), але Text може містити довільний рядок.Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="AutoCompleteBox — тільки Avalonia:"
FontWeight="SemiBold" FontSize="14"/>
<TextBlock Text="Почніть вводити назву міста:"
Foreground="Gray" FontSize="13"/>
<AutoCompleteBox x:Name="cityBox"
PlaceholderText="Введіть назву міста..."
FilterMode="Contains"
MinimumPrefixLength="1"
CornerRadius="8" Padding="10 6"
Width="260" HorizontalAlignment="Left"/>
<TextBlock Foreground="Gray" FontSize="11" FontStyle="Italic"
Text="ItemsSource заповнюється з code-behind (Window_Loaded)."/>
</StackPanel>
private void Window_Loaded(object sender, RoutedEventArgs e)
{
cityBox.ItemsSource = new[]
{
"Київ", "Харків", "Одеса", "Дніпро", "Львів",
"Запоріжжя", "Кривий Ріг", "Миколаїв", "Херсон",
"Полтава", "Чернігів", "Черкаси", "Суми", "Житомир"
};
}
private void CityBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (cityBox.SelectedItem is string city)
statusText.Text = $"Обрано: {city}";
}
FilterMode="Custom" дозволяє задати власну функцію фільтрації через ItemFilter — callback типу Func<string, object, bool>. Це корисно для нечіткого пошуку (fuzzy search) або фільтрації за кількома полями об'єкта.TimePicker — нативний контрол для вибору часу доби. У WPF аналога немає. TimePicker показує спінери для годин, хвилин та (опційно) секунд — вертикальні прокручувані стовпці, як у мобільних iOS/Android picker-ах.
TimeSpan. null — нічого не обрано. TimeSpan у цьому контексті представляє час доби від опівночі: new TimeSpan(14, 30, 0) = 14:30:00.15 — показує 0, 15, 30, 45. За замовчуванням — 1 (кожна хвилина).UseSeconds="True").false."12HourClock" або "24HourClock". За замовчуванням визначається локаллю.Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="TimePicker — тільки Avalonia:"
FontWeight="SemiBold" FontSize="14"/>
<StackPanel Spacing="8">
<TextBlock Text="24-годинний формат, крок 15 хв:" Foreground="Gray"/>
<TimePicker ClockIdentifier="24HourClock"
MinuteIncrement="15"
HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Spacing="8">
<TextBlock Text="12-годинний формат з секундами:" Foreground="Gray"/>
<TimePicker ClockIdentifier="12HourClock"
UseSeconds="True"
SecondIncrement="30"
HorizontalAlignment="Left"/>
</StackPanel>
</StackPanel>
У WPF немає жодного вбудованого контрола для вибору кольору — потрібні сторонні бібліотеки. Avalonia має два рідних контроли:
ColorPicker — компактний drop-down (як DatePicker для кольору): у закритому стані показує кольоровий квадрат, при натисканні — розкривається повноцінний picker.ColorView — завжди видимий повноцінний picker (режим Canvas). Містить кольоровий компас (ColorSpectrum), слайдери насиченості/яскравості, HEX-поле та альфа-канал.Обидва оперують типом Color (Avalonia.Media.Color), ідентичним System.Windows.Media.Color у WPF.
ColorPicker оперує внутрішньо в HSV для точних слайдерів насиченості/яскравості.SelectionChanged для списків). Аргументи містять OldColor та NewColor.true.Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="16">
<TextBlock Text="ColorPicker — тільки Avalonia:"
FontWeight="SemiBold" FontSize="14"/>
<StackPanel Orientation="Horizontal" Spacing="16">
<StackPanel Spacing="8">
<TextBlock Text="Компактний (drop-down):"
Foreground="Gray" FontSize="13"/>
<ColorPicker Color="#6366F1"
HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Spacing="8">
<TextBlock Text="Колір фону:"
Foreground="Gray" FontSize="13"/>
<Border x:Name="colorPreview"
Width="60" Height="34"
Background="#6366F1"
CornerRadius="6"/>
</StackPanel>
</StackPanel>
<TextBlock Text="У WPF для ColorPicker потрібні Extended WPF Toolkit або MahApps."
Foreground="Gray" FontSize="11" FontStyle="Italic"/>
</StackPanel>
У WPF гіперпосилання у тексті реалізовується через Hyperlink Inline (всередині TextBlock). Окремої кнопки-гіперпосилання немає. В Avalonia є HyperlinkButton — повноцінний Button, що стилізований під посилання (синій текст, підкреслення, cursor as pointer):
<!-- WPF: тільки через TextBlock + Hyperlink Inline -->
<TextBlock>
<Hyperlink NavigateUri="https://example.com"
RequestNavigate="Hyperlink_Navigate">
Перейти на сайт
</Hyperlink>
</TextBlock>
<!-- Avalonia: окремий контрол HyperlinkButton -->
<HyperlinkButton Content="Перейти на сайт"
NavigateUri="https://example.com"/>
<!-- Або з обробником Click для відкриття у браузері -->
<HyperlinkButton Content="Документація Avalonia"
NavigateUri="https://docs.avaloniaui.net"
Click="Link_Click"/>
HyperlinkButton автоматично відкриває NavigateUri у браузері за замовчуванням — без жодного коду. Успадковує від Button, тож підтримує Content як довільний XAML.
MaskedTextBox — TextBox з прив'язаним шаблоном введення (маскою). У WPF аналог — System.Windows.Controls.MaskedTextBox зі WinForms-обгорткою або стороннє рішення. В Avalonia — нативний.
Символи маски:
| Символ | Допустимий ввід |
|---|---|
0 | Обов'язкова цифра (0–9) |
9 | Необов'язкова цифра або пробіл |
# | Цифра, пробіл, + або - |
L | Обов'язкова літера (a–z, A–Z) |
? | Необов'язкова літера |
& | Обов'язковий будь-який символ |
A | Обов'язкова літера або цифра |
a | Необов'язкова літера або цифра |
> | Усі наступні символи — UPPER |
< | Усі наступні символи — lower |
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="20" Spacing="12">
<TextBlock Text="MaskedTextBox — тільки Avalonia:"
FontWeight="SemiBold" FontSize="14"/>
<Grid RowSpacing="8" ColumnSpacing="12">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Телефон:"
VerticalAlignment="Center" Foreground="Gray"/>
<MaskedTextBox Grid.Row="0" Grid.Column="1"
Mask="+38 (000) 000-00-00"
PromptChar="_"
Padding="8,6" CornerRadius="6"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Дата (ДД.ММ.РРРР):"
VerticalAlignment="Center" Foreground="Gray"/>
<MaskedTextBox Grid.Row="1" Grid.Column="1"
Mask="00.00.0000"
PromptChar="_"
Padding="8,6" CornerRadius="6"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Картка:"
VerticalAlignment="Center" Foreground="Gray"/>
<MaskedTextBox Grid.Row="2" Grid.Column="1"
Mask="0000 0000 0000 0000"
PromptChar="_" AsciiOnly="True"
Padding="8,6" CornerRadius="6"/>
</Grid>
</StackPanel>
SelectableTextBlock — Avalonia-аналог TextBlock, але з можливістю виділення та копіювання тексту мишею або клавіатурою. У WPF TextBlock не дозволяє виділяти текст.
<!-- WPF: щоб виділяти текст — потрібен IsReadOnly TextBox (некрасиво) -->
<TextBox IsReadOnly="True" Text="Виділи мене" BorderThickness="0"/>
<!-- Avalonia: SelectableTextBlock — виглядає як TextBlock, але виділяється -->
<SelectableTextBlock Text="Серійний номер: SN-2026-ABCD-9999"
SelectionBrush="#6366F1"
TextWrapping="Wrap"/>
Властивості SelectionStart, SelectionEnd, SelectedText дозволяють читати виділений фрагмент у code-behind. SelectionChanged — подія зміни виділення.
TreeDataGrid — один з найпотужніших контролів Avalonia. Поєднує TreeView (ієрархія) і DataGrid (таблиця) в одному. У WPF DataGrid і TreeView — окремі контроли без можливості прямого поєднання.
<!-- Тільки Avalonia -->
<TreeDataGrid x:Name="fileTree"
CanUserResizeColumns="True"
CanUserSortColumns="True"/>
Налаштовується програматично через HierarchicalTreeDataGridSource<T> у code-behind. Це складніший контрол, детально розглянутий у Блоці 6 (Data Binding та колекції).
Avalonia має вбудовану систему сповіщень типу toast — WindowNotificationManager з методом Show(). У WPF для цього потрібні сторонні бібліотеки або власна реалізація.
Зробимо фінальний підсумок усіх відмінностей по чотирьох категоріях:
✅ Повна сумісність (нічого не змінювати):Button, RepeatButton, ToggleButton, ProgressBar, Slider, Popup, CheckBox, RadioButton, ComboBox, ListBox, GroupBox, Expander, TabControl, StatusBar, TextBlock Inline-елементи.
⚠️ Потребує незначних правок:
| Контрол | Що змінити |
|---|---|
Image | pack:// → avares://, <Resource> → <AvaloniaResource> |
ToolTipService.* | → ToolTip.* (напряму на елементі) |
TextBox | Додати PlaceholderText (замість code-behind), опційно CornerRadius |
DatePicker | SelectedDate → DateTimeOffset?, BlackoutDates — ручна реалізація |
Label | Прибрати _ (AccessKey), прибрати Target |
🔴 Потребує замінників:
| WPF | Avalonia-замінник |
|---|---|
PasswordBox | TextBox з PasswordChar="●" |
RichTextBox + FlowDocument | AvaloniaEdit (NuGet) або TextBox |
Calendar SingleRange/MultipleRange | Стороння бібліотека або кастомна реалізація |
🆕 Є тільки в Avalonia (без WPF-аналога):
| Контрол | Призначення |
|---|---|
NumericUpDown | Числове поле зі стрілками і FormatString |
AutoCompleteBox | TextBox з dropdown-підказками та FilterMode |
TimePicker | Вибір часу (спінери годин/хвилин/секунд) |
ColorPicker / ColorView | Вibір кольору з HSV-спектром та HEX-полем |
HyperlinkButton | Кнопка, що виглядає як HTML-посилання |
MaskedTextBox | Введення за маскою (телефон, дата, картка) |
SelectableTextBlock | TextBlock з можливістю виділення та копіювання |
TreeDataGrid | Ієрархічна таблиця (TreeView + DataGrid) |
SplitView | Бічна навігаційна панель (hamburger-меню) |
WindowNotificationManager | Toast-сповіщення (вбудовано) |
RelativePanel | Позиціонування елементів відносно сусідів |
ItemsRepeater | Легковісний список без ListBox-overhead |
Перехід з WPF на Avalonia у сфері контролів — відносно безболісний. 80% XAML-коду перенесеться без змін або з мінімальними правками (URI та ToolTipService). Справжні проблеми виникають лише в двох точках: відсутність PasswordBox з SecureString (архітектурне рішення безпеки) та відсутність RichTextBox/FlowDocument (потребує сторонньої бібліотеки). Все інше — це зручні покращення: CornerRadius, PlaceholderText, Spacing, NumericUpDown, AutoCompleteBox, TimePicker, ColorPicker — функції, які WPF-розробники зазвичай реалізовували "вручну" або через сторонні бібліотеки.
Button, Image, ProgressBar та інші базові контроли
Вивчаємо найбільш вживані контроли WPF — кнопки, зображення, індикатори прогресу, слайдери та спливаючі підказки. Кожен контрол розбирається від базових властивостей до нетривіальних деталей.
Текстові контроли — TextBlock, TextBox, RichTextBox
Детально розглядаємо контроли для відображення та введення тексту у WPF — від легковісного TextBlock до повноцінного RichTextBox із FlowDocument. Розуміємо різницю між відображенням і введенням, форматованим і неформатованим текстом, захищеним введенням через PasswordBox.