У попередній статті ми розглянули Resources та Themes у WPF, де для зміни теми потрібно вручну підміняти ResourceDictionary через MergedDictionaries. Це працює, але вимагає багато ручної роботи.
Проблема WPF:
// WPF — ручна підміна теми
private void SwitchTheme(string theme)
{
var dictionaries = Application.Current.Resources.MergedDictionaries;
dictionaries.Clear();
dictionaries.Add(new ResourceDictionary
{
Source = new Uri($"Themes/{theme}.xaml", UriKind.Relative)
});
}
Avalonia має вбудовану систему тематизації:
Fluent Theme — це вбудована тема Avalonia, що імітує дизайн Windows 11 з підтримкою Dark/Light режимів.
App.axaml:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.App">
<Application.Styles>
<!-- Fluent Theme -->
<FluentTheme />
</Application.Styles>
</Application>
Що це дає:
| Аспект | WPF | Avalonia Fluent Theme |
|---|---|---|
| Підключення | Ручне (MergedDictionaries) | <FluentTheme /> |
| Dark/Light | Ручна підміна словників | Вбудований ThemeVariant |
| Акцентний колір | Ручна реалізація | SystemAccentColor |
| Стиль | Aero (застарілий) | Windows 11 Fluent Design |
| Оновлення | Не оновлюється | Регулярні оновлення |
ThemeVariant — це механізм Avalonia для перемикання між світлою та темною темами.
Метод 1: На рівні Application
<Application xmlns="https://github.com/avaloniaui"
RequestedThemeVariant="Dark">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
Метод 2: На рівні Window
<Window xmlns="https://github.com/avaloniaui"
RequestedThemeVariant="Light">
<!-- Контент -->
</Window>
Метод 3: На рівні Control
<Border RequestedThemeVariant="Dark">
<!-- Темний контент всередині світлого вікна -->
</Border>
| Значення | Опис |
|---|---|
Default | Слідує за системною темою (Windows, macOS, Linux) |
Light | Світла тема |
Dark | Темна тема |
C# код:
using Avalonia;
using Avalonia.Styling;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void SwitchToDark()
{
// Змінити тему на рівні Application
Application.Current.RequestedThemeVariant = ThemeVariant.Dark;
}
private void SwitchToLight()
{
Application.Current.RequestedThemeVariant = ThemeVariant.Light;
}
private void FollowSystem()
{
Application.Current.RequestedThemeVariant = ThemeVariant.Default;
}
}
З ViewModel (MVVM):
public partial class SettingsViewModel : ObservableObject
{
[ObservableProperty]
private string _selectedTheme = "Default";
partial void OnSelectedThemeChanged(string value)
{
var themeVariant = value switch
{
"Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default
};
Application.Current.RequestedThemeVariant = themeVariant;
}
}
XAML з ComboBox:
<StackPanel Margin="20">
<TextBlock Text="Тема:" FontWeight="Bold"/>
<ComboBox SelectedItem="{Binding SelectedTheme}" Margin="0,5,0,0">
<ComboBoxItem Content="Системна (Default)"/>
<ComboBoxItem Content="Світла (Light)"/>
<ComboBoxItem Content="Темна (Dark)"/>
</ComboBox>
</StackPanel>
Переваги ThemeVariant.Default:
Приклад:
<Application RequestedThemeVariant="Default">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
Тепер додаток автоматично перемикається між світлою та темною темою разом з системою.
Avalonia автоматично змінює значення ресурсів залежно від ThemeVariant.
Приклад використання:
<Border Background="{DynamicResource SystemAccentColor}"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumBrush}"
BorderThickness="1"
CornerRadius="8"
Padding="20">
<TextBlock Text="Акцентний колір"
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
</Border>
Ключові системні ресурси:
| Ресурс | Опис |
|---|---|
SystemAccentColor | Акцентний колір системи |
SystemAccentColorLight1 | Світліший відтінок акценту |
SystemAccentColorDark1 | Темніший відтінок акценту |
SystemControlForegroundBaseHighBrush | Основний текст |
SystemControlForegroundBaseMediumBrush | Вторинний текст |
SystemControlBackgroundAltHighBrush | Альтернативний фон |
SystemControlHighlightAccentBrush | Підсвічування акцентом |
Визначення ресурсів для різних тем:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<!-- Світла тема -->
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="MyCustomBrush" Color="#2196F3"/>
<x:Double x:Key="MyCustomOpacity">0.9</x:Double>
</ResourceDictionary>
<!-- Темна тема -->
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="MyCustomBrush" Color="#64B5F6"/>
<x:Double x:Key="MyCustomOpacity">0.8</x:Double>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
Використання:
<Border Background="{DynamicResource MyCustomBrush}"
Opacity="{DynamicResource MyCustomOpacity}">
<!-- Автоматично змінюється при зміні теми -->
</Border>
Ключові моменти:
ThemeDictionaries — словник словників для різних темx:Key="Light" / x:Key="Dark" — ключі для світлої та темної темиDynamicResource — обов'язково для автоматичної зміниSimple Theme — це мінімальна тема Avalonia без зайвих стилів. Ідеально для створення власного дизайну з нуля.
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<SimpleTheme />
</Application.Styles>
</Application>
Відмінності від Fluent Theme:
| Аспект | Fluent Theme | Simple Theme |
|---|---|---|
| Стилізація | Повна (Windows 11) | Мінімальна |
| Розмір | ~500 KB | ~50 KB |
| Кастомізація | Складна | Легка |
| Використання | Production додатки | Кастомний дизайн |
Коли використовувати Simple Theme:
Avalonia має активну спільноту, що створює альтернативні теми.
Встановлення:
dotnet add package Material.Avalonia
Підключення:
<Application xmlns="https://github.com/avaloniaui"
xmlns:themes="clr-namespace:Material.Styles.Themes;assembly=Material.Styles">
<Application.Styles>
<themes:MaterialTheme BaseTheme="Dark" PrimaryColor="Blue" SecondaryColor="Lime"/>
</Application.Styles>
</Application>
Переваги:
Встановлення:
dotnet add package Semi.Avalonia
Підключення:
<Application xmlns="https://github.com/avaloniaui"
xmlns:semiTheme="clr-namespace:Semi.Avalonia.Themes;assembly=Semi.Avalonia">
<Application.Styles>
<semiTheme:SemiTheme Locale="zh-CN"/>
</Application.Styles>
</Application>
Переваги:
| Тема | Стиль | Розмір | Підтримка | Використання |
|---|---|---|---|---|
| Fluent | Windows 11 | Середній | Офіційна | За замовчуванням |
| Simple | Мінімалістичний | Малий | Офіційна | Кастомізація |
| Material | Material Design | Великий | Community | Android-подібні додатки |
| Semi | Semi Design | Великий | Community | Сучасні додатки |
Fluent Theme можна кастомізувати через властивості.
Метод 1: Через XAML
<Application xmlns="https://github.com/avaloniaui">
<Application.Styles>
<FluentTheme>
<FluentTheme.Palettes>
<ColorPaletteResources x:Key="Light" Accent="#FF6B35" AltHigh="White"/>
<ColorPaletteResources x:Key="Dark" Accent="#FF6B35" AltHigh="Black"/>
</FluentTheme.Palettes>
</FluentTheme>
</Application.Styles>
</Application>
Метод 2: Через код
var fluentTheme = Application.Current.Styles.OfType<FluentTheme>().FirstOrDefault();
if (fluentTheme != null)
{
fluentTheme.Palettes[ThemeVariant.Light].Accent = Color.Parse("#FF6B35");
fluentTheme.Palettes[ThemeVariant.Dark].Accent = Color.Parse("#FF6B35");
}
Fluent Theme підтримує різну щільність:
<FluentTheme DensityStyle="Compact"/>
<!-- або -->
<FluentTheme DensityStyle="Normal"/>
Відмінності:
| Density | Padding | FontSize | Використання |
|---|---|---|---|
| Normal | Стандартний | 14px | Desktop додатки |
| Compact | Зменшений | 12px | Інформаційні панелі, таблиці |
Розберемо детально відмінності у підходах до тематизації.
Структура:
Themes/
├── Light.xaml
├── Dark.xaml
└── Blue.xaml
App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Light.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Зміна теми:
private void SwitchTheme(string theme)
{
var dictionaries = Application.Current.Resources.MergedDictionaries;
dictionaries.Clear();
dictionaries.Add(new ResourceDictionary
{
Source = new Uri($"Themes/{theme}.xaml", UriKind.Relative)
});
}
Проблеми:
App.axaml:
<Application RequestedThemeVariant="Default">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
Зміна теми:
Application.Current.RequestedThemeVariant = ThemeVariant.Dark;
Переваги:
| Аспект | WPF | Avalonia |
|---|---|---|
| Підключення теми | 10+ рядків XAML | 1 рядок |
| Зміна теми | 5+ рядків C# | 1 рядок |
| Dark/Light | Ручна реалізація | Вбудована |
| Системна тема | Немає | ThemeVariant.Default |
| Акцентний колір | Ручна реалізація | SystemAccentColor |
| Стилі контролів | Створювати вручну | Готові |
| Підтримка | Застаріла | Активна |
Мета: Навчитися перемикати теми у Avalonia.
Завдання:
Створіть додаток з перемикачем теми:
RequestedThemeVariantDynamicResource для кольорівКритерії успіху:
DynamicResource для тематичних кольорівПідказка XAML:
<Window xmlns="https://github.com/avaloniaui"
x:Class="MyApp.MainWindow"
Title="Theme Switcher" Width="400" Height="300">
<StackPanel Margin="20" Spacing="10">
<ToggleSwitch Content="Темна тема"
IsChecked="{Binding IsDarkTheme}"
OffContent="Світла тема"
OnContent="Темна тема"/>
<Border Background="{DynamicResource SystemAccentColor}"
Padding="20"
CornerRadius="8">
<TextBlock Text="Акцентний колір"
Foreground="White"
FontSize="16"/>
</Border>
<Button Content="Кнопка з темою" HorizontalAlignment="Stretch"/>
<TextBox Watermark="Введіть текст..."/>
</StackPanel>
</Window>
Підказка C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private bool _isDarkTheme;
partial void OnIsDarkThemeChanged(bool value)
{
Application.Current.RequestedThemeVariant = value
? ThemeVariant.Dark
: ThemeVariant.Light;
}
}
Мета: Навчитися кастомізувати Fluent Theme.
Завдання:
Створіть додаток з вибором акцентного кольору:
Accent у FluentTheme.PalettesSystemAccentColorКритерії успіху:
Підказка:
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private string _selectedColor = "Blue";
partial void OnSelectedColorChanged(string value)
{
var color = value switch
{
"Red" => Color.Parse("#F44336"),
"Green" => Color.Parse("#4CAF50"),
"Purple" => Color.Parse("#9C27B0"),
"Orange" => Color.Parse("#FF9800"),
_ => Color.Parse("#2196F3") // Blue
};
var fluentTheme = Application.Current.Styles.OfType<FluentTheme>().FirstOrDefault();
if (fluentTheme != null)
{
fluentTheme.Palettes[ThemeVariant.Light].Accent = color;
fluentTheme.Palettes[ThemeVariant.Dark].Accent = color;
}
}
}
Мета: Реалізувати автоматичне слідування за системною темою з можливістю override.
Завдання:
Створіть додаток з налаштуваннями теми:
ThemeVariant.Default (слідує за ОС)ThemeVariant.LightThemeVariant.DarkКритерії успіху:
Підказка:
public partial class SettingsViewModel : ObservableObject
{
[ObservableProperty]
private string _themeMode = "System";
[ObservableProperty]
private string _currentActiveTheme;
public SettingsViewModel()
{
// Завантажити збережені налаштування
LoadSettings();
// Підписатися на зміни теми
Application.Current.ActualThemeVariantChanged += OnThemeChanged;
UpdateCurrentTheme();
}
partial void OnThemeModeChanged(string value)
{
var themeVariant = value switch
{
"Light" => ThemeVariant.Light,
"Dark" => ThemeVariant.Dark,
_ => ThemeVariant.Default
};
Application.Current.RequestedThemeVariant = themeVariant;
SaveSettings();
}
private void OnThemeChanged(object sender, EventArgs e)
{
UpdateCurrentTheme();
}
private void UpdateCurrentTheme()
{
CurrentActiveTheme = Application.Current.ActualThemeVariant == ThemeVariant.Dark
? "Темна"
: "Світла";
}
private void LoadSettings()
{
// Завантажити з файлу або реєстру
ThemeMode = Preferences.Get("ThemeMode", "System");
}
private void SaveSettings()
{
// Зберегти у файл або реєстр
Preferences.Set("ThemeMode", ThemeMode);
}
}
Avalonia має вбудовану потужну систему тематизації, що набагато зручніша за WPF.
Ключові висновки:
🎨 Fluent Theme
🌓 ThemeVariant
🔄 Runtime Switching
🎯 DynamicResource
🎭 Community Themes
⚡ Простота
Переваги Avalonia Theming:
Порівняння з WPF:
| Аспект | WPF | Avalonia |
|---|---|---|
| Складність | Висока | Низька |
| Рядків коду | 500+ | 1-10 |
| Підтримка Dark/Light | Ручна | Вбудована |
| Системна тема | Немає | ThemeVariant.Default |
| Готові теми | Немає | Fluent, Simple |
Що далі?
Ви завершили Block 8: Стилізація! Наступний блок — Просунуті контроли:
Теми та ресурсні словники у WPF
Побудова системи тематизації WPF-додатку. DynamicResource, MergedDictionaries, Light/Dark теми з runtime-перемиканням, SystemColors, огляд MaterialDesignInXamlToolkit, MahApps.Metro та HandyControl.
Контроли колекцій — глибоке занурення
Архітектура ItemsControl та просунуте використання ListBox/ListView для роботи з великими наборами даних