Коли ви створюєте новий WPF додаток, за замовчуванням він виглядає як додаток з Windows Vista — сірі кнопки, плоскі контроли, застарілий дизайн. Це працює, але виглядає архаїчно порівняно з сучасними додатками Windows 11. Користувачі звикли до Fluent Design System — сучасної дизайн-мови Microsoft з rounded corners, subtle shadows, smooth animations та акриловими ефектами. На щастя, Microsoft надає офіційну бібліотеку PresentationFramework.Fluent, яка дозволяє легко інтегрувати Fluent UI у ваші WPF додатки.
У цій статті ми детально розглянемо, як увімкнути Fluent UI у WPF додатку, як працює система тем, як кастомізувати кольори та стилі, як створювати власні стилі з наслідуванням від Fluent базових стилів через BasedOn, та як правильно організувати ресурси для підтримки світлої та темної теми. Ми також обговоримо обмеження Fluent UI у WPF порівняно з WinUI 3, та як максимально наблизити вигляд WPF додатку до нативних Windows 11 додатків.
Fluent UI — це не просто косметичні зміни. Це цілісна дизайн-система, яка впливає на user experience: правильні відступи, консистентна типографіка, зрозумілі візуальні стани (hover, pressed, disabled), accessibility-friendly контрасти. Інтеграція Fluent UI робить ваш додаток сучасним, професійним та приємним у використанні.
Перш ніж інтегрувати Fluent UI, важливо зрозуміти, що це таке та чому це важливо.
Windows XP (2001-2006): Luna theme — яскраві кольори, 3D ефекти, glossy кнопки. Революційно для свого часу, але швидко застарів.
Windows Vista/7 (2006-2012): Aero theme — прозорість, glass effects, subtle animations. WPF створювався саме для цієї ери.
Windows 8 (2012-2015): Metro/Modern UI — плоский дизайн, яскраві кольори, typography-focused. Радикальна зміна, яка викликала суперечки.
Windows 10 (2015-2021): Fluent Design System v1 — еволюція Metro з додаванням depth (тіні, layers), motion (animations), material (acrylic transparency).
Windows 11 (2021-present): Fluent Design System v2 — rounded corners, soft colors, centered taskbar, consistency across всіх додатків.
Fluent Design базується на п'яти принципах (5 L's):
1. Light — використання світла для створення depth та focus. Subtle shadows, highlights на інтерактивних елементах.
2. Depth — створення ієрархії через layering. Елементи на різних "шарах" з різними elevation levels.
3. Motion — smooth, purposeful animations. Не просто "красиво", а для покращення UX (показати зв'язок між елементами, direction of navigation).
4. Material — використання "матеріалів" як acrylic (напівпрозорість з blur), mica (subtle texture). Створює відчуття фізичності у digital UI.
5. Scale — адаптивність до різних розмірів екрану, input methods (mouse, touch, pen), accessibility needs.
WinUI 3 — це нова UI платформа Microsoft, створена з нуля для Fluent Design. Вона має повну підтримку всіх Fluent features: acrylic, mica, reveal effects, smooth animations.
WPF Fluent UI — це backport Fluent стилів у WPF. Обмеження:
Проте WPF Fluent UI дає:
Для нових проєктів розгляньте WinUI 3. Для існуючих WPF додатків — Fluent UI є чудовим способом модернізувати вигляд.
Тепер перейдемо до практики — як увімкнути Fluent UI у вашому WPF додатку.
Fluent UI для WPF доступний у:
Перевірте ваш .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Якщо ви на .NET Framework 4.8 — Fluent UI недоступний. Потрібна міграція на .NET 6+.
Fluent стилі знаходяться у PresentationFramework.Fluent.dll, яка входить у .NET runtime. Не потрібно встановлювати додаткові NuGet пакети.
App.xaml:
<Application x:Class="MyWPFApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Fluent Theme -->
<ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Ось і все! Тепер всі стандартні контроли (Button, TextBox, ComboBox, etc.) автоматично використовують Fluent стилі.
Створіть просте вікно для перевірки:
<Window x:Class="MyWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Fluent UI Demo" Height="400" Width="600">
<StackPanel Margin="20" Spacing="10">
<TextBlock Text="Fluent UI in WPF"
FontSize="24"
FontWeight="SemiBold" />
<Button Content="Primary Button"
Width="200"
HorizontalAlignment="Left" />
<Button Content="Disabled Button"
Width="200"
HorizontalAlignment="Left"
IsEnabled="False" />
<TextBox Text="Text input with Fluent style"
Width="300"
HorizontalAlignment="Left" />
<ComboBox Width="300"
HorizontalAlignment="Left"
SelectedIndex="0">
<ComboBoxItem Content="Option 1" />
<ComboBoxItem Content="Option 2" />
<ComboBoxItem Content="Option 3" />
</ComboBox>
<CheckBox Content="Fluent CheckBox" />
<RadioButton Content="Fluent RadioButton" />
<Slider Width="300"
HorizontalAlignment="Left"
Minimum="0" Maximum="100" Value="50" />
</StackPanel>
</Window>
Запустіть додаток. Ви побачите:
Fluent UI підтримує світлу (Light) та темну (Dark) теми. За замовчуванням використовується світла тема, але можна перемикати програмно або автоматично слідувати за системною темою.
using System.Windows;
public partial class App : Application
{
public void SetTheme(string themeName)
{
var fluentTheme = Resources.MergedDictionaries
.FirstOrDefault(d => d.Source?.ToString().Contains("Fluent.xaml") == true);
if (fluentTheme != null)
{
Resources.MergedDictionaries.Remove(fluentTheme);
}
var newTheme = new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.{themeName}.xaml")
};
Resources.MergedDictionaries.Add(newTheme);
}
}
Доступні теми:
Fluent.xaml — Light theme (за замовчуванням)Fluent.Dark.xaml — Dark themeFluent.HC.xaml — High Contrast theme (для accessibility)Використання:
// У MainWindow або ViewModel
((App)Application.Current).SetTheme("Dark");
Windows дозволяє користувачам обирати світлу або темну тему у Settings. Можна автоматично слідувати за цим вибором:
using Microsoft.Win32;
using System.Windows;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Встановити тему при старті
ApplySystemTheme();
// Слідкувати за змінами системної теми
SystemEvents.UserPreferenceChanged += OnUserPreferenceChanged;
}
private void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
{
if (e.Category == UserPreferenceCategory.General)
{
ApplySystemTheme();
}
}
private void ApplySystemTheme()
{
var isDarkTheme = IsSystemDarkTheme();
SetTheme(isDarkTheme ? "Dark" : "Light");
}
private bool IsSystemDarkTheme()
{
try
{
using var key = Registry.CurrentUser.OpenSubKey(
@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
var value = key?.GetValue("AppsUseLightTheme");
return value is int intValue && intValue == 0;
}
catch
{
return false; // За замовчуванням світла тема
}
}
public void SetTheme(string themeName)
{
Dispatcher.Invoke(() =>
{
var fluentTheme = Resources.MergedDictionaries
.FirstOrDefault(d => d.Source?.ToString().Contains("Fluent") == true);
if (fluentTheme != null)
{
Resources.MergedDictionaries.Remove(fluentTheme);
}
var newTheme = new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.{themeName}.xaml")
};
Resources.MergedDictionaries.Insert(0, newTheme);
});
}
protected override void OnExit(ExitEventArgs e)
{
SystemEvents.UserPreferenceChanged -= OnUserPreferenceChanged;
base.OnExit(e);
}
}
Тепер додаток автоматично перемикається між світлою та темною темою коли користувач змінює системні налаштування.
Додайте у вікно можливість ручного перемикання:
<StackPanel Orientation="Horizontal" Margin="10">
<TextBlock Text="Theme:" VerticalAlignment="Center" Margin="0,0,10,0" />
<ComboBox x:Name="ThemeComboBox"
Width="150"
SelectionChanged="ThemeComboBox_SelectionChanged">
<ComboBoxItem Content="Light" Tag="Light" />
<ComboBoxItem Content="Dark" Tag="Dark" />
<ComboBoxItem Content="System Default" Tag="System" IsSelected="True" />
</ComboBox>
</StackPanel>
private void ThemeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ThemeComboBox.SelectedItem is ComboBoxItem item)
{
var tag = item.Tag.ToString();
if (tag == "System")
{
((App)Application.Current).ApplySystemTheme();
}
else
{
((App)Application.Current).SetTheme(tag);
}
}
}
Fluent UI надає базові стилі, але часто потрібно кастомізувати кольори під ваш brand або створити власні варіанти контролів.
Fluent UI визначає кольори через систему ресурсів:
<!-- Приклад ресурсів з Fluent.xaml -->
<SolidColorBrush x:Key="SystemAccentBrush" Color="#0078D4" />
<SolidColorBrush x:Key="SystemAccentBrushSecondary" Color="#005A9E" />
<SolidColorBrush x:Key="SystemAccentBrushTertiary" Color="#004578" />
<SolidColorBrush x:Key="SystemControlBackgroundBaseLowBrush" Color="#F3F3F3" />
<SolidColorBrush x:Key="SystemControlBackgroundBaseMediumBrush" Color="#E6E6E6" />
<SolidColorBrush x:Key="SystemControlForegroundBaseHighBrush" Color="#000000" />
<SolidColorBrush x:Key="SystemControlForegroundBaseMediumBrush" Color="#666666" />
Ці ресурси використовуються у стилях контролів. Щоб змінити кольори, перевизначте ці ресурси.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Fluent Theme -->
<ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Перевизначення accent color -->
<SolidColorBrush x:Key="SystemAccentBrush" Color="#6B46C1" />
<SolidColorBrush x:Key="SystemAccentBrushSecondary" Color="#553C9A" />
<SolidColorBrush x:Key="SystemAccentBrushTertiary" Color="#44307A" />
</ResourceDictionary>
</Application.Resources>
Тепер всі кнопки, checkboxes, sliders використовують фіолетовий accent замість синього.
Найпотужніша можливість — створення власних стилів з наслідуванням від Fluent базових стилів через BasedOn.
Приклад: Primary та Secondary кнопки
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- Primary Button (Accent background) -->
<Style x:Key="PrimaryButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{DynamicResource SystemAccentBrush}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentBrush}" />
<Setter Property="Padding" Value="16,8" />
<Setter Property="FontWeight" Value="SemiBold" />
</Style>
<!-- Secondary Button (Outline style) -->
<Style x:Key="SecondaryButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource SystemAccentBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemAccentBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="16,8" />
</Style>
<!-- Danger Button (Red for destructive actions) -->
<Style x:Key="DangerButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="#D13438" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderBrush" Value="#D13438" />
<Setter Property="Padding" Value="16,8" />
</Style>
</ResourceDictionary>
</Application.Resources>
Використання:
<StackPanel Orientation="Horizontal" Spacing="10">
<Button Content="Save" Style="{StaticResource PrimaryButtonStyle}" />
<Button Content="Cancel" Style="{StaticResource SecondaryButtonStyle}" />
<Button Content="Delete" Style="{StaticResource DangerButtonStyle}" />
</StackPanel>
Ключовий момент: BasedOn="{StaticResource {x:Type Button}}" наслідує ВСІ Fluent стилі (rounded corners, hover effects, animations), але дозволяє перевизначити конкретні властивості (Background, Foreground).
Без BasedOn ви втрачаєте всі Fluent стилі:
<!-- ПОГАНО: Втрачаються Fluent стилі -->
<Style x:Key="MyButtonStyle" TargetType="Button">
<Setter Property="Background" Value="Red" />
</Style>
<!-- Результат: червона кнопка, але БЕЗ rounded corners, БЕЗ hover effects -->
<!-- ДОБРЕ: Зберігаються Fluent стилі -->
<Style x:Key="MyButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Red" />
</Style>
<!-- Результат: червона кнопка З rounded corners, З hover effects -->
BasedOn="{StaticResource {x:Type Button}}" означає "наслідуй стиль за замовчуванням для типу Button", який у нашому випадку є Fluent стилем.
<!-- Card Style з Fluent elevation -->
<Style x:Key="CardStyle" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Padding" Value="16" />
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect Color="Black"
Opacity="0.1"
BlurRadius="8"
ShadowDepth="2"
Direction="270" />
</Setter.Value>
</Setter>
</Style>
Використання:
<Border Style="{StaticResource CardStyle}" Width="300" Margin="20">
<StackPanel>
<TextBlock Text="Card Title"
FontSize="18"
FontWeight="SemiBold"
Margin="0,0,0,10" />
<TextBlock Text="This is a card with Fluent-style elevation and rounded corners."
TextWrapping="Wrap" />
<Button Content="Action"
Style="{StaticResource PrimaryButtonStyle}"
HorizontalAlignment="Right"
Margin="0,10,0,0" />
</StackPanel>
</Border>
Для великих проєктів не варто тримати всі стилі в App.xaml. Краще організувати їх у окремі файли.
MyWPFApp/
├── Styles/
│ ├── Colors.xaml # Кольори та brushes
│ ├── Typography.xaml # Шрифти та text styles
│ ├── Buttons.xaml # Стилі кнопок
│ ├── TextBoxes.xaml # Стилі текстових полів
│ ├── Cards.xaml # Card-style контейнери
│ └── Theme.xaml # Головний файл, який об'єднує все
├── Views/
├── ViewModels/
└── App.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Brand Colors -->
<Color x:Key="BrandPrimaryColor">#6B46C1</Color>
<Color x:Key="BrandSecondaryColor">#553C9A</Color>
<Color x:Key="BrandTertiaryColor">#44307A</Color>
<SolidColorBrush x:Key="BrandPrimaryBrush" Color="{StaticResource BrandPrimaryColor}" />
<SolidColorBrush x:Key="BrandSecondaryBrush" Color="{StaticResource BrandSecondaryColor}" />
<SolidColorBrush x:Key="BrandTertiaryBrush" Color="{StaticResource BrandTertiaryColor}" />
<!-- Semantic Colors -->
<SolidColorBrush x:Key="SuccessBrush" Color="#107C10" />
<SolidColorBrush x:Key="WarningBrush" Color="#FFA500" />
<SolidColorBrush x:Key="ErrorBrush" Color="#D13438" />
<SolidColorBrush x:Key="InfoBrush" Color="#0078D4" />
<!-- Override Fluent Accent -->
<SolidColorBrush x:Key="SystemAccentBrush" Color="{StaticResource BrandPrimaryColor}" />
<SolidColorBrush x:Key="SystemAccentBrushSecondary" Color="{StaticResource BrandSecondaryColor}" />
<SolidColorBrush x:Key="SystemAccentBrushTertiary" Color="{StaticResource BrandTertiaryColor}" />
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Typography Styles -->
<Style x:Key="TitleTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="28" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
</Style>
<Style x:Key="SubtitleTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
</Style>
<Style x:Key="BodyTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="14" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseHighBrush}" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style x:Key="CaptionTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="Foreground" Value="{DynamicResource SystemControlForegroundBaseMediumBrush}" />
</Style>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Primary Button -->
<Style x:Key="PrimaryButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{DynamicResource BrandPrimaryBrush}" />
<Setter Property="Foreground" Value="White" />
<Setter Property="BorderBrush" Value="{DynamicResource BrandPrimaryBrush}" />
<Setter Property="Padding" Value="16,8" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="MinWidth" Value="100" />
</Style>
<!-- Secondary Button -->
<Style x:Key="SecondaryButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource BrandPrimaryBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource BrandPrimaryBrush}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="16,8" />
<Setter Property="MinWidth" Value="100" />
</Style>
<!-- Icon Button -->
<Style x:Key="IconButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="8" />
<Setter Property="Width" Value="40" />
<Setter Property="Height" Value="40" />
</Style>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- Fluent Base Theme -->
<ResourceDictionary Source="pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.xaml" />
<!-- Custom Styles -->
<ResourceDictionary Source="Colors.xaml" />
<ResourceDictionary Source="Typography.xaml" />
<ResourceDictionary Source="Buttons.xaml" />
<ResourceDictionary Source="TextBoxes.xaml" />
<ResourceDictionary Source="Cards.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<Application x:Class="MyWPFApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/Theme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Тепер всі стилі організовані, легко знайти потрібний файл, та легко підтримувати.
Коли ви створюєте власні стилі, важливо щоб вони правильно працювали у обох темах.
Завжди використовуйте DynamicResource для кольорів, які залежать від теми:
<!-- ПОГАНО: StaticResource не оновлюється при зміні теми -->
<Style x:Key="MyCardStyle" TargetType="Border">
<Setter Property="Background" Value="{StaticResource SystemControlBackgroundBaseLowBrush}" />
</Style>
<!-- ДОБРЕ: DynamicResource оновлюється при зміні теми -->
<Style x:Key="MyCardStyle" TargetType="Border">
<Setter Property="Background" Value="{DynamicResource SystemControlBackgroundBaseLowBrush}" />
</Style>
Якщо потрібні власні кольори, які змінюються залежно від теми:
Colors.Light.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="CardBackgroundBrush" Color="#FFFFFF" />
<SolidColorBrush x:Key="CardBorderBrush" Color="#E0E0E0" />
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#000000" />
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#666666" />
</ResourceDictionary>
Colors.Dark.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="CardBackgroundBrush" Color="#2D2D2D" />
<SolidColorBrush x:Key="CardBorderBrush" Color="#3F3F3F" />
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#FFFFFF" />
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#B0B0B0" />
</ResourceDictionary>
Динамічне завантаження:
public void SetTheme(string themeName)
{
// Видалити старі ресурси
var oldFluentTheme = Resources.MergedDictionaries
.FirstOrDefault(d => d.Source?.ToString().Contains("Fluent") == true);
var oldColorTheme = Resources.MergedDictionaries
.FirstOrDefault(d => d.Source?.ToString().Contains("Colors.") == true);
if (oldFluentTheme != null)
Resources.MergedDictionaries.Remove(oldFluentTheme);
if (oldColorTheme != null)
Resources.MergedDictionaries.Remove(oldColorTheme);
// Додати нові ресурси
var fluentTheme = new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/PresentationFramework.Fluent;component/Themes/Fluent.{themeName}.xaml")
};
var colorTheme = new ResourceDictionary
{
Source = new Uri($"pack://application:,,,/MyWPFApp;component/Styles/Colors.{themeName}.xaml", UriKind.Absolute)
};
Resources.MergedDictionaries.Insert(0, fluentTheme);
Resources.MergedDictionaries.Insert(1, colorTheme);
}
Тепер ваші власні кольори автоматично змінюються при перемиканні теми.
Fluent UI у WPF має деякі обмеження порівняно з WinUI 3. Розглянемо їх та способи обходу.
WPF не має доступу до Windows Compositor API, тому справжній acrylic/mica неможливий.
Workaround: Імітація через напівпрозорість та blur:
<Window x:Class="MyWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
AllowsTransparency="True"
WindowStyle="None"
Background="Transparent">
<Border Background="#F0FFFFFF"
CornerRadius="8"
Margin="10">
<Border.Effect>
<BlurEffect Radius="20" />
</Border.Effect>
<!-- Content -->
</Border>
</Window>
Це не справжній acrylic, але створює схожий ефект.
WPF animations повільніші за WinUI через застарілу архітектуру.
Workaround: Використовуйте короткі animations (100-200ms) та прості easing functions:
<Style x:Key="FastHoverButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="0.8"
Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
To="1.0"
Duration="0:0:0.1" />
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
WinUI 3 має нові контроли (InfoBar, NavigationView, TeachingTip), яких немає у WPF.
Workaround: Використовуйте сторонні бібліотеки:
Або створіть власні UserControls з Fluent стилями.
Завдання: Створіть WPF додаток з Fluent UI та формою реєстрації.
Вимоги:
Критерії успіху:
Завдання: Додайте підтримку світлої та темної теми з автоматичним слідуванням за системною темою.
Вимоги:
Критерії успіху:
Завдання: Створіть власну систему стилів з організацією у окремі файли та підтримкою обох тем.
Вимоги:
Критерії успіху:
Fluent UI у WPF — це потужний спосіб модернізувати вигляд вашого додатку без міграції на WinUI 3. Основні висновки:
Інтеграція проста: Один рядок у App.xaml — і всі контроли автоматично отримують Fluent стилі з rounded corners, сучасними кольорами та smooth animations.
BasedOn — ключ до кастомізації: Завжди використовуйте BasedOn="{StaticResource {x:Type ControlType}}" при створенні власних стилів. Це зберігає всі Fluent features (rounded corners, hover effects, animations) та дозволяє перевизначити тільки потрібні властивості.
DynamicResource для тем: Використовуйте DynamicResource замість StaticResource для кольорів, щоб стилі автоматично оновлювалися при зміні теми.
Організація ресурсів: Для великих проєктів організуйте стилі у окремі файли (Colors.xaml, Typography.xaml, Buttons.xaml) для легкої підтримки.
Підтримка тем обов'язкова: Сучасні користувачі очікують підтримку темної теми. Слідкуйте за системною темою через Registry та автоматично перемикайте додаток.
Обмеження WPF: Fluent UI у WPF не має справжнього acrylic/mica, animations повільніші, та відсутні деякі нові контроли. Проте для більшості додатків це не критично — Fluent UI у WPF дає достатньо для сучасного вигляду.
Fluent UI робить ваш WPF додаток сучасним, професійним та приємним у використанні. Це інвестиція у user experience, яка окупається довірою та задоволенням користувачів.
Попередня стаття: XAML Basics — основи розмітки — базовий синтаксис XAML для створення UI.
Наступна стаття: XAML Namespaces та Resources — робота з просторами імен та ресурсами у XAML.
XAML: декларативний інтерфейс
Розбираємо XAML як мову: зв'язок з C#-об'єктами, синтаксис XML, Property Element Syntax, Content Property, Type Converters та x:Name.
WPF UI — сучасна бібліотека Fluent контролів
Інтеграція WPF UI (Wpf.Ui) у додатки: сучасні контроли Windows 11, NavigationView, Mica/Acrylic ефекти, темна тема, та повна екосистема Fluent Design