Професійні desktop-застосунки мають стандартизовану структуру інтерфейсу: меню зверху, панель інструментів під меню, контекстне меню на правий клік та статусний рядок знизу. Ця структура знайома користувачам з десятиліть роботи з програмами на кшталт Microsoft Word, Visual Studio, Adobe Photoshop.
У цій статті ми навчимося створювати всі ці елементи інтерфейсу, інтегрувати їх з MVVM через Commands та побудуємо повноцінну систему навігації для складного застосунку.
Menu — це горизонтальна панель меню, що зазвичай розташовується у верхній частині вікна. Вона містить MenuItem елементи, які можуть мати вкладені підменю.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="400">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Файл">
<MenuItem Header="Створити" />
<MenuItem Header="Відкрити..." />
<MenuItem Header="Зберегти" />
<Separator />
<MenuItem Header="Вихід" />
</MenuItem>
<MenuItem Header="Редагувати">
<MenuItem Header="Скасувати" />
<MenuItem Header="Повторити" />
<Separator />
<MenuItem Header="Вирізати" />
<MenuItem Header="Копіювати" />
<MenuItem Header="Вставити" />
</MenuItem>
<MenuItem Header="Вигляд">
<MenuItem Header="Панель інструментів" />
<MenuItem Header="Статусний рядок" />
</MenuItem>
<MenuItem Header="Довідка">
<MenuItem Header="Документація" />
<MenuItem Header="Про програму" />
</MenuItem>
</Menu>
<Border Background="#f3f4f6" DockPanel.Dock="Top">
<TextBlock Text="Основний вміст вікна"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Foreground="#6b7280" />
</Border>
</DockPanel>
</Window>
Додавання іконок робить меню більш зрозумілим та привабливим:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="600" Height="400">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Файл">
<MenuItem Header="Створити">
<MenuItem.Icon>
<TextBlock Text="📄" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Відкрити...">
<MenuItem.Icon>
<TextBlock Text="📂" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Зберегти">
<MenuItem.Icon>
<TextBlock Text="💾" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Вихід">
<MenuItem.Icon>
<TextBlock Text="🚪" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="Редагувати">
<MenuItem Header="Скасувати">
<MenuItem.Icon>
<TextBlock Text="↶" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Повторити">
<MenuItem.Icon>
<TextBlock Text="↷" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Вирізати">
<MenuItem.Icon>
<TextBlock Text="✂️" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Копіювати">
<MenuItem.Icon>
<TextBlock Text="📋" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Вставити">
<MenuItem.Icon>
<TextBlock Text="📌" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<Border Background="#f3f4f6" DockPanel.Dock="Top">
<TextBlock Text="Вміст застосунку"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Foreground="#6b7280" />
</Border>
</DockPanel>
</Window>
Професійні застосунки завжди мають клавіатурні скорочення для швидкого доступу:
<MenuItem Header="Файл">
<MenuItem Header="Створити"
InputGesture="Ctrl+N">
<MenuItem.Icon>
<TextBlock Text="📄" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Відкрити..."
InputGesture="Ctrl+O">
<MenuItem.Icon>
<TextBlock Text="📂" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Зберегти"
InputGesture="Ctrl+S">
<MenuItem.Icon>
<TextBlock Text="💾" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Друк..."
InputGesture="Ctrl+P">
<MenuItem.Icon>
<TextBlock Text="🖨️" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="Редагувати">
<MenuItem Header="Скасувати"
InputGesture="Ctrl+Z" />
<MenuItem Header="Повторити"
InputGesture="Ctrl+Y" />
<Separator />
<MenuItem Header="Вирізати"
InputGesture="Ctrl+X" />
<MenuItem Header="Копіювати"
InputGesture="Ctrl+C" />
<MenuItem Header="Вставити"
InputGesture="Ctrl+V" />
</MenuItem>
InputGesture — це лише текстова підказка для користувача. Вона не активує команду автоматично. Для реальної роботи клавіатурних скорочень потрібно додати KeyBinding у Window.InputBindings:<Window.InputBindings>
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding NewCommand}" />
<KeyBinding Key="O" Modifiers="Ctrl" Command="{Binding OpenCommand}" />
<KeyBinding Key="S" Modifiers="Ctrl" Command="{Binding SaveCommand}" />
</Window.InputBindings>
Інтеграція з MVVM через Commands:
public class MainViewModel : ObservableObject
{
public ICommand NewCommand { get; }
public ICommand OpenCommand { get; }
public ICommand SaveCommand { get; }
public ICommand ExitCommand { get; }
public ICommand UndoCommand { get; }
public ICommand RedoCommand { get; }
public ICommand CutCommand { get; }
public ICommand CopyCommand { get; }
public ICommand PasteCommand { get; }
private bool _hasUnsavedChanges;
public MainViewModel()
{
NewCommand = new RelayCommand(New);
OpenCommand = new AsyncRelayCommand(OpenAsync);
SaveCommand = new AsyncRelayCommand(SaveAsync, CanSave);
ExitCommand = new RelayCommand(Exit);
UndoCommand = new RelayCommand(Undo, CanUndo);
RedoCommand = new RelayCommand(Redo, CanRedo);
CutCommand = new RelayCommand(Cut, CanCut);
CopyCommand = new RelayCommand(Copy, CanCopy);
PasteCommand = new RelayCommand(Paste, CanPaste);
}
private void New()
{
if (_hasUnsavedChanges)
{
// Показати діалог "Зберегти зміни?"
}
// Створити новий документ
}
private async Task OpenAsync()
{
// Відкрити діалог вибору файлу
var file = await _dialogService.OpenFileAsync();
if (file != null)
{
// Завантажити файл
}
}
private async Task SaveAsync()
{
// Зберегти документ
await _documentService.SaveAsync();
_hasUnsavedChanges = false;
}
private bool CanSave() => _hasUnsavedChanges;
private void Exit()
{
if (_hasUnsavedChanges)
{
// Показати діалог підтвердження
}
Application.Current.Shutdown();
}
private void Undo() { /* Скасувати останню дію */ }
private bool CanUndo() => _undoStack.Count > 0;
private void Redo() { /* Повторити скасовану дію */ }
private bool CanRedo() => _redoStack.Count > 0;
private void Cut() { /* Вирізати */ }
private bool CanCut() => _selectedText != null;
private void Copy() { /* Копіювати */ }
private bool CanCopy() => _selectedText != null;
private void Paste() { /* Вставити */ }
private bool CanPaste() => Clipboard.ContainsText();
}
XAML прив'язка:
<Menu DockPanel.Dock="Top">
<MenuItem Header="Файл">
<MenuItem Header="Створити"
InputGesture="Ctrl+N"
Command="{Binding NewCommand}">
<MenuItem.Icon>
<TextBlock Text="📄" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Відкрити..."
InputGesture="Ctrl+O"
Command="{Binding OpenCommand}">
<MenuItem.Icon>
<TextBlock Text="📂" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Зберегти"
InputGesture="Ctrl+S"
Command="{Binding SaveCommand}">
<MenuItem.Icon>
<TextBlock Text="💾" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Вихід"
Command="{Binding ExitCommand}">
<MenuItem.Icon>
<TextBlock Text="🚪" FontSize="16" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="Редагувати">
<MenuItem Header="Скасувати"
InputGesture="Ctrl+Z"
Command="{Binding UndoCommand}" />
<MenuItem Header="Повторити"
InputGesture="Ctrl+Y"
Command="{Binding RedoCommand}" />
<Separator />
<MenuItem Header="Вирізати"
InputGesture="Ctrl+X"
Command="{Binding CutCommand}" />
<MenuItem Header="Копіювати"
InputGesture="Ctrl+C"
Command="{Binding CopyCommand}" />
<MenuItem Header="Вставити"
InputGesture="Ctrl+V"
Command="{Binding PasteCommand}" />
</MenuItem>
</Menu>
<Window.InputBindings>
<KeyBinding Key="N" Modifiers="Ctrl" Command="{Binding NewCommand}" />
<KeyBinding Key="O" Modifiers="Ctrl" Command="{Binding OpenCommand}" />
<KeyBinding Key="S" Modifiers="Ctrl" Command="{Binding SaveCommand}" />
<KeyBinding Key="Z" Modifiers="Ctrl" Command="{Binding UndoCommand}" />
<KeyBinding Key="Y" Modifiers="Ctrl" Command="{Binding RedoCommand}" />
<KeyBinding Key="X" Modifiers="Ctrl" Command="{Binding CutCommand}" />
<KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyCommand}" />
<KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteCommand}" />
</Window.InputBindings>
CanExecute команди повертає false, MenuItem автоматично стає disabled (сірий, неклікабельний). Це чудовий спосіб показати користувачу, які дії доступні в поточному стані застосунку.Деякі пункти меню можуть бути "перемикачами":
<MenuItem Header="Вигляд">
<MenuItem Header="Панель інструментів"
IsCheckable="True"
IsChecked="{Binding IsToolbarVisible}" />
<MenuItem Header="Статусний рядок"
IsCheckable="True"
IsChecked="{Binding IsStatusBarVisible}" />
<MenuItem Header="Бічна панель"
IsCheckable="True"
IsChecked="{Binding IsSidebarVisible}" />
<Separator />
<MenuItem Header="Повноекранний режим"
IsCheckable="True"
IsChecked="{Binding IsFullScreen}"
InputGesture="F11" />
</MenuItem>
ContextMenu — це меню, що з'являється при правому кліку миші. Воно надає швидкий доступ до найбільш релевантних дій для конкретного елемента.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="300">
<Grid Margin="20">
<TextBox AcceptsReturn="True"
TextWrapping="Wrap"
Text="Клікніть правою кнопкою миші для відкриття контекстного меню">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Вирізати">
<MenuItem.Icon>
<TextBlock Text="✂️" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Копіювати">
<MenuItem.Icon>
<TextBlock Text="📋" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Вставити">
<MenuItem.Icon>
<TextBlock Text="📌" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Виділити все" />
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
</Window>
Контекстне меню особливо корисне для списків:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="400">
<Grid Margin="20">
<ListBox>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Відкрити">
<MenuItem.Icon>
<TextBlock Text="📂" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Редагувати">
<MenuItem.Icon>
<TextBlock Text="✏️" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Видалити">
<MenuItem.Icon>
<TextBlock Text="🗑️" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Властивості">
<MenuItem.Icon>
<TextBlock Text="⚙️" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
<ListBoxItem Content="Документ 1.docx" />
<ListBoxItem Content="Презентація.pptx" />
<ListBoxItem Content="Таблиця.xlsx" />
<ListBoxItem Content="Зображення.png" />
</ListBox>
</Grid>
</Window>
Одна з найпоширеніших проблем з ContextMenu — він не успадковує DataContext від батьківського елемента, оскільки не є частиною візуального дерева.
Проблема:
<!-- ❌ Не працює! ContextMenu не бачить DataContext -->
<ListBox ItemsSource="{Binding Items}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Видалити"
Command="{Binding DeleteCommand}" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
Рішення 1: PlacementTarget
<!-- ✅ Працює через PlacementTarget -->
<ListBox ItemsSource="{Binding Items}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Видалити"
Command="{Binding PlacementTarget.DataContext.DeleteCommand,
RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
Рішення 2: BindingProxy (Freezable Trick)
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get => GetValue(DataProperty);
set => SetValue(DataProperty, value);
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register(nameof(Data), typeof(object),
typeof(BindingProxy), new PropertyMetadata(null));
}
Використання:
<Window.Resources>
<local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</Window.Resources>
<ListBox ItemsSource="{Binding Items}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Видалити"
Command="{Binding Data.DeleteCommand, Source={StaticResource Proxy}}"
CommandParameter="{Binding PlacementTarget.SelectedItem,
RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
Різні елементи можуть мати різні контекстні меню:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemContainerStyle>
<Style Selector="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="{Binding Name}" IsEnabled="False" FontWeight="Bold" />
<Separator />
<MenuItem Header="Відкрити" Command="{Binding OpenCommand}" />
<MenuItem Header="Редагувати" Command="{Binding EditCommand}" />
<Separator />
<MenuItem Header="Видалити" Command="{Binding DeleteCommand}" />
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
ToolBar — це горизонтальна панель з кнопками швидкого доступу до найбільш використовуваних команд.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="700" Height="400">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Файл" />
<MenuItem Header="Редагувати" />
<MenuItem Header="Вигляд" />
</Menu>
<Border DockPanel.Dock="Top"
Background="#f3f4f6"
BorderBrush="#d1d5db"
BorderThickness="0,0,0,1"
Padding="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<Button Content="📄"
ToolTip.Tip="Створити (Ctrl+N)"
Padding="8"
Width="36" Height="36" />
<Button Content="📂"
ToolTip.Tip="Відкрити (Ctrl+O)"
Padding="8"
Width="36" Height="36" />
<Button Content="💾"
ToolTip.Tip="Зберегти (Ctrl+S)"
Padding="8"
Width="36" Height="36" />
<Separator Width="1" Height="30" Margin="5,0" />
<Button Content="✂️"
ToolTip.Tip="Вирізати (Ctrl+X)"
Padding="8"
Width="36" Height="36" />
<Button Content="📋"
ToolTip.Tip="Копіювати (Ctrl+C)"
Padding="8"
Width="36" Height="36" />
<Button Content="📌"
ToolTip.Tip="Вставити (Ctrl+V)"
Padding="8"
Width="36" Height="36" />
<Separator Width="1" Height="30" Margin="5,0" />
<Button Content="↶"
ToolTip.Tip="Скасувати (Ctrl+Z)"
Padding="8"
Width="36" Height="36" />
<Button Content="↷"
ToolTip.Tip="Повторити (Ctrl+Y)"
Padding="8"
Width="36" Height="36" />
</StackPanel>
</Border>
<Border Background="White" DockPanel.Dock="Top">
<TextBlock Text="Вміст документа"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Foreground="#6b7280"
Margin="0,50,0,0" />
</Border>
</DockPanel>
</Window>
<Border DockPanel.Dock="Top"
Background="#f3f4f6"
BorderBrush="#d1d5db"
BorderThickness="0,0,0,1"
Padding="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<Button Content="📄"
ToolTip.Tip="Створити (Ctrl+N)"
Command="{Binding NewCommand}"
Padding="8"
Width="36" Height="36" />
<Button Content="📂"
ToolTip.Tip="Відкрити (Ctrl+O)"
Command="{Binding OpenCommand}"
Padding="8"
Width="36" Height="36" />
<Button Content="💾"
ToolTip.Tip="Зберегти (Ctrl+S)"
Command="{Binding SaveCommand}"
Padding="8"
Width="36" Height="36" />
<Separator Width="1" Height="30" Margin="5,0" />
<Button Content="✂️"
ToolTip.Tip="Вирізати (Ctrl+X)"
Command="{Binding CutCommand}"
Padding="8"
Width="36" Height="36" />
<Button Content="📋"
ToolTip.Tip="Копіювати (Ctrl+C)"
Command="{Binding CopyCommand}"
Padding="8"
Width="36" Height="36" />
<Button Content="📌"
ToolTip.Tip="Вставити (Ctrl+V)"
Command="{Binding PasteCommand}"
Padding="8"
Width="36" Height="36" />
</StackPanel>
</Border>
Menu та ToolBar. Коли CanExecute повертає false, і пункт меню, і кнопка на панелі інструментів автоматично стануть disabled. Це забезпечує консистентність інтерфейсу.Для перемикачів (Bold, Italic, Underline) використовуйте ToggleButton:
<StackPanel Orientation="Horizontal" Spacing="5">
<ToggleButton Content="B"
ToolTip.Tip="Жирний (Ctrl+B)"
IsChecked="{Binding IsBold}"
FontWeight="Bold"
Width="36" Height="36" />
<ToggleButton Content="I"
ToolTip.Tip="Курсив (Ctrl+I)"
IsChecked="{Binding IsItalic}"
FontStyle="Italic"
Width="36" Height="36" />
<ToggleButton Content="U"
ToolTip.Tip="Підкреслений (Ctrl+U)"
IsChecked="{Binding IsUnderline}"
TextDecorations="Underline"
Width="36" Height="36" />
</StackPanel>
Для вибору зі списку (шрифт, розмір):
<StackPanel Orientation="Horizontal" Spacing="10">
<ComboBox Width="150"
SelectedItem="{Binding SelectedFont}"
ToolTip.Tip="Шрифт">
<ComboBoxItem Content="Arial" />
<ComboBoxItem Content="Times New Roman" />
<ComboBoxItem Content="Courier New" />
<ComboBoxItem Content="Verdana" />
</ComboBox>
<ComboBox Width="80"
SelectedItem="{Binding FontSize}"
ToolTip.Tip="Розмір шрифту">
<ComboBoxItem Content="8" />
<ComboBoxItem Content="10" />
<ComboBoxItem Content="12" IsSelected="True" />
<ComboBoxItem Content="14" />
<ComboBoxItem Content="16" />
<ComboBoxItem Content="18" />
<ComboBoxItem Content="24" />
</ComboBox>
</StackPanel>
StatusBar — це панель внизу вікна, що показує інформацію про поточний стан застосунку.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="700" Height="400">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Файл" />
<MenuItem Header="Редагувати" />
</Menu>
<Border DockPanel.Dock="Bottom"
Background="#f3f4f6"
BorderBrush="#d1d5db"
BorderThickness="0,1,0,0"
Padding="10,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="Готово"
VerticalAlignment="Center"
Foreground="#6b7280" />
<TextBlock Grid.Column="1"
Text="Рядок: 1, Стовпець: 1"
VerticalAlignment="Center"
Margin="0,0,20,0"
Foreground="#6b7280" />
<TextBlock Grid.Column="2"
Text="UTF-8"
VerticalAlignment="Center"
Foreground="#6b7280" />
</Grid>
</Border>
<Border Background="White" DockPanel.Dock="Top">
<TextBlock Text="Вміст документа"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
Foreground="#6b7280" />
</Border>
</DockPanel>
</Window>
<Border DockPanel.Dock="Bottom"
Background="#f3f4f6"
BorderBrush="#d1d5db"
BorderThickness="0,1,0,0"
Padding="10,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding StatusMessage}"
VerticalAlignment="Center"
Foreground="#6b7280" />
<ProgressBar Grid.Column="1"
Value="{Binding Progress}"
Maximum="100"
Height="16"
Margin="10,0"
IsVisible="{Binding IsProcessing}" />
<TextBlock Grid.Column="2"
Text="{Binding Progress, StringFormat='{}{0}%'}"
VerticalAlignment="Center"
Foreground="#6b7280"
IsVisible="{Binding IsProcessing}" />
</Grid>
</Border>
ViewModel:
public class MainViewModel : ObservableObject
{
private string _statusMessage = "Готово";
private double _progress;
private bool _isProcessing;
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
public double Progress
{
get => _progress;
set => SetProperty(ref _progress, value);
}
public bool IsProcessing
{
get => _isProcessing;
set => SetProperty(ref _isProcessing, value);
}
public async Task ProcessAsync()
{
IsProcessing = true;
StatusMessage = "Обробка...";
for (int i = 0; i <= 100; i++)
{
Progress = i;
await Task.Delay(50);
}
IsProcessing = false;
StatusMessage = "Готово";
}
}
Ribbon — це сучасна альтернатива традиційному меню та панелі інструментів, популяризована Microsoft Office.
Ribbon контролу. Найпопулярніша бібліотека — Fluent.Ribbon:dotnet add package Fluent.Ribbon
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fluent="clr-namespace:Fluent;assembly=Fluent">
<fluent:RibbonWindow>
<fluent:Ribbon>
<!-- Вкладка "Головна" -->
<fluent:RibbonTabItem Header="Головна">
<fluent:RibbonGroupBox Header="Буфер обміну">
<fluent:Button Header="Вставити"
Icon="📌"
LargeIcon="📌"
Command="{Binding PasteCommand}" />
<fluent:Button Header="Вирізати"
Icon="✂️"
Command="{Binding CutCommand}" />
<fluent:Button Header="Копіювати"
Icon="📋"
Command="{Binding CopyCommand}" />
</fluent:RibbonGroupBox>
<fluent:RibbonGroupBox Header="Шрифт">
<fluent:ComboBox Header="Шрифт" Width="150" />
<fluent:ComboBox Header="Розмір" Width="80" />
<fluent:ToggleButton Header="B" FontWeight="Bold" />
<fluent:ToggleButton Header="I" FontStyle="Italic" />
<fluent:ToggleButton Header="U" TextDecorations="Underline" />
</fluent:RibbonGroupBox>
</fluent:RibbonTabItem>
<!-- Вкладка "Вставка" -->
<fluent:RibbonTabItem Header="Вставка">
<fluent:RibbonGroupBox Header="Таблиці">
<fluent:Button Header="Таблиця" Icon="📊" />
</fluent:RibbonGroupBox>
<fluent:RibbonGroupBox Header="Ілюстрації">
<fluent:Button Header="Зображення" Icon="🖼️" />
<fluent:Button Header="Фігури" Icon="⬜" />
</fluent:RibbonGroupBox>
</fluent:RibbonTabItem>
</fluent:Ribbon>
<Grid>
<!-- Вміст застосунку -->
</Grid>
</fluent:RibbonWindow>
</Window>
Об'єднаємо всі компоненти в один застосунок:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800" Height="600"
Title="Текстовий редактор">
<DockPanel>
<!-- Меню -->
<Menu DockPanel.Dock="Top">
<MenuItem Header="Файл">
<MenuItem Header="Створити" InputGesture="Ctrl+N">
<MenuItem.Icon>
<TextBlock Text="📄" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Відкрити..." InputGesture="Ctrl+O">
<MenuItem.Icon>
<TextBlock Text="📂" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Зберегти" InputGesture="Ctrl+S">
<MenuItem.Icon>
<TextBlock Text="💾" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Вихід" />
</MenuItem>
<MenuItem Header="Редагувати">
<MenuItem Header="Скасувати" InputGesture="Ctrl+Z" />
<MenuItem Header="Повторити" InputGesture="Ctrl+Y" />
<Separator />
<MenuItem Header="Вирізати" InputGesture="Ctrl+X" />
<MenuItem Header="Копіювати" InputGesture="Ctrl+C" />
<MenuItem Header="Вставити" InputGesture="Ctrl+V" />
</MenuItem>
<MenuItem Header="Вигляд">
<MenuItem Header="Панель інструментів" IsCheckable="True" IsChecked="True" />
<MenuItem Header="Статусний рядок" IsCheckable="True" IsChecked="True" />
</MenuItem>
</Menu>
<!-- Панель інструментів -->
<Border DockPanel.Dock="Top"
Background="#f3f4f6"
BorderBrush="#d1d5db"
BorderThickness="0,0,0,1"
Padding="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<Button Content="📄" ToolTip.Tip="Створити" Padding="8" Width="36" Height="36" />
<Button Content="📂" ToolTip.Tip="Відкрити" Padding="8" Width="36" Height="36" />
<Button Content="💾" ToolTip.Tip="Зберегти" Padding="8" Width="36" Height="36" />
<Separator Width="1" Height="30" Margin="5,0" />
<Button Content="✂️" ToolTip.Tip="Вирізати" Padding="8" Width="36" Height="36" />
<Button Content="📋" ToolTip.Tip="Копіювати" Padding="8" Width="36" Height="36" />
<Button Content="📌" ToolTip.Tip="Вставити" Padding="8" Width="36" Height="36" />
<Separator Width="1" Height="30" Margin="5,0" />
<ToggleButton Content="B" ToolTip.Tip="Жирний" FontWeight="Bold" Width="36" Height="36" />
<ToggleButton Content="I" ToolTip.Tip="Курсив" FontStyle="Italic" Width="36" Height="36" />
<ToggleButton Content="U" ToolTip.Tip="Підкреслений" Width="36" Height="36" />
</StackPanel>
</Border>
<!-- Статусний рядок -->
<Border DockPanel.Dock="Bottom"
Background="#f3f4f6"
BorderBrush="#d1d5db"
BorderThickness="0,1,0,0"
Padding="10,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="Готово"
VerticalAlignment="Center"
Foreground="#6b7280" />
<TextBlock Grid.Column="1"
Text="Рядок: 1, Стовпець: 1"
VerticalAlignment="Center"
Margin="0,0,20,0"
Foreground="#6b7280" />
<TextBlock Grid.Column="2"
Text="Символів: 0"
VerticalAlignment="Center"
Foreground="#6b7280" />
</Grid>
</Border>
<!-- Основний вміст -->
<TextBox AcceptsReturn="True"
TextWrapping="Wrap"
BorderThickness="0"
Padding="10"
FontSize="14"
Text="Почніть вводити текст...">
<TextBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Вирізати">
<MenuItem.Icon>
<TextBlock Text="✂️" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Копіювати">
<MenuItem.Icon>
<TextBlock Text="📋" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Вставити">
<MenuItem.Icon>
<TextBlock Text="📌" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="Виділити все" />
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</DockPanel>
</Window>
Мета: Навчитися створювати базове меню з клавіатурними скороченнями.
Завдання: Створіть застосунок з повноцінним меню та підтримкою клавіатурних скорочень.
Вимоги:
KeyBinding для всіх скороченьCanExecute логіку (наприклад, "Зберегти" доступне тільки якщо є зміни)Підказка:
public class MainViewModel : ObservableObject
{
private bool _hasUnsavedChanges;
public ICommand SaveCommand { get; }
public MainViewModel()
{
SaveCommand = new RelayCommand(Save, CanSave);
}
private void Save()
{
// Зберегти файл
_hasUnsavedChanges = false;
SaveCommand.NotifyCanExecuteChanged();
}
private bool CanSave() => _hasUnsavedChanges;
}
Мета: Опанувати створення контекстних меню з правильною прив'язкою до DataContext.
Завдання: Створіть список завдань з контекстним меню для кожного елемента.
Вимоги:
TaskItem з властивостями: Title, Description, IsCompletedObservableCollection<TaskItem> у ViewModelListBox з DataTemplate для відображення завданьContextMenu з пунктами:
PlacementTarget або BindingProxyДодаткові виклики:
Мета: Створити повноцінний інтерфейс застосунку з усіма компонентами.
Завдання: Розробіть текстовий редактор з професійним інтерфейсом.
Вимоги:
Структура ViewModel:
public class TextEditorViewModel : ObservableObject
{
private string _content;
private string _currentFilePath;
private bool _hasUnsavedChanges;
private int _cursorLine;
private int _cursorColumn;
private string _selectedFont;
private int _fontSize;
private bool _isBold;
private bool _isItalic;
private bool _isUnderline;
private Stack<string> _undoStack;
private Stack<string> _redoStack;
public ICommand NewCommand { get; }
public ICommand OpenCommand { get; }
public ICommand SaveCommand { get; }
public ICommand SaveAsCommand { get; }
public ICommand ExitCommand { get; }
public ICommand UndoCommand { get; }
public ICommand RedoCommand { get; }
public ICommand CutCommand { get; }
public ICommand CopyCommand { get; }
public ICommand PasteCommand { get; }
// Реалізація...
}
Додаткові виклики:
У цій статті ми детально розібрали систему меню та панелей інструментів:
Menu:
MenuItem з Header, Icon, InputGestureMenuItemSeparator для розділення груп командIsCheckable для перемикачівKeyBinding для реальної роботи клавіатурних скороченьContextMenu:
PlacementTargetBindingProxy (Freezable Trick) для складних сценаріївToolBar:
ToggleButton для перемикачівComboBox для вибору зі спискуStatusBar:
ProgressBar для довгих операційRibbon:
Fluent.RibbonНаступні кроки: У наступному блоці ми розглянемо навігацію між вікнами, діалогові вікна та створення власних UserControl і Custom Controls.
TreeView та GridView
Ієрархічні та табличні контроли для відображення складних структур даних
Навігація та керування вікнами. Частина 1: вікна та сторінки
Як WPF організовує багатовіконний інтерфейс: Show та ShowDialog, передача даних між вікнами, Owner-зв'язок. Концепція Frame та Page — навігація у стилі браузера та чому вона не використовується в реальних проєктах.