Панелі Layout: StackPanel, WrapPanel, DockPanel
Панелі Layout: StackPanel, WrapPanel, DockPanel
У веб-розробці є CSS. Там ви пишете position: absolute; top: 100px; left: 200px і елемент опиниться рівно там, де ви хочете. Можна також написати position: fixed, z-index: 999, margin: auto і отримати бажаний результат.
WPF пропонує інший підхід — і він значно кращий для складних інтерфейсів.
У WPF немає "абсолютного позиціонування за замовчуванням". Якщо ви просто кинете кілька кнопок у Grid — вони всі виявляться одна поверх одної по центру. Щоб розташувати їх правильно — потрібна панель. Панель — це спеціальний контейнер, що знає алгоритм розміщення своїх дочірніх елементів.
Чому ця архітектура краща? Тому що:
- Інтерфейс адаптується до розміру вікна автоматично. Ви змінюєте розмір вікна — панелі перераховують розміщення.
- Вам не треба хардкодити координати.
StackPanelсам вириховує, де ставити наступну кнопку. - Інтерфейс залишається коректним при різних DPI, масштабах, локалізаціях.
DesiredSize. Arrange Pass — другий прохід: батько каже "ось тобі ця область (finalRect), розташуйся", дочірній малює себе. StackPanel — панель, що вкладає елементи вертикально або горизонтально один за одним. WrapPanel — як StackPanel, але з Orientation="Horizontal" переносить на новий рядок при нестачі місця. DockPanel — панель з "прикріпленням" елементів до сторін: Top, Bottom, Left, Right, і один (LastChildFill) займає решту.Модель розташування WPF: Measure → Arrange
Перш ніж говорити про конкретні панелі — треба зрозуміти механізм, що лежить під усіма ними. Це двопрохідний алгоритм layout, який виконується щоразу, коли вікно змінює розмір або змінюється вміст.
Прохід 1: Measure (вимірювання)
На першому проході кожен елемент з'ясовує, скільки місця йому потрібно.
Алгоритм рекурсивний: батьківський елемент викликає Measure(availableSize) для кожного дочірнього. "Available size" — це скільки місця батько може потенційно надати. Дочірній вимірює себе (враховуючи свій вміст, шрифт, зображення) і зберігає результат у властивості DesiredSize.
// Спрощений вигляд того, що відбувається всередині WPF
protected override Size MeasureOverride(Size constraint)
{
// Запитуємо у дочірніх: "скільки місця вам треба?"
foreach (UIElement child in InternalChildren)
{
child.Measure(constraint); // constraint = скільки є у нас
// child.DesiredSize — тепер містить відповідь
}
// Повертаємо наш власний DesiredSize
return new Size(totalWidth, totalHeight);
}
Важливо: availableSize може містити double.PositiveInfinity по одній або обох осях. Це означає "місця необмежено — скільки треба". StackPanel у WPF передає нескінченний constraint по своїй осі розкладки: він говорить дочірнім "займайте стільки місця по вертикалі, скільки хочете".
Прохід 2: Arrange (розміщення)
На другому проході батьківський елемент розміщує дочірніх у конкретних прямокутниках.
Батько викликає Arrange(finalRect) для кожного дочірнього, передаючи Rect — точну позицію і розмір. Дочірній зобов'язаний розмістити себе у цій області. Він не може вийти за межі (WPF обріже зображення поза finalRect), але може розміститися менше ніж виділена область (якщо має HorizontalAlignment / VerticalAlignment).
// Спрощений Arrange у StackPanel
protected override Size ArrangeOverride(Size finalSize)
{
double y = 0; // Поточна вертикальна позиція
foreach (UIElement child in InternalChildren)
{
double childHeight = child.DesiredSize.Height;
// Ставимо дочірній елемент у прямокутник
child.Arrange(new Rect(0, y, finalSize.Width, childHeight));
y += childHeight; // Наступний елемент іде нижче
}
return finalSize;
}
Чому це важливо знати
Розуміння Measure/Arrange пояснює поведінку панелей без магії:
- Чому
StackPanelне скролиться: По осі вкладання він передає дочірнімInfinityяк constraint. Дочірні займають стільки, скільки хочуть. БатькоStackPanelсам стає нескінченно великим. Якщо вікно менше — елементи просто обрізаються. Щоб мати скролл — треба обгорнути уScrollViewer. - Чому
Width="*"у Grid працює: GridColumn у фіналі Arrange розподіляє решту простору між зірочковими стовпцями. - Чому елемент із
HorizontalAlignment="Center"не займає всю ширину: У Arrange він отримав широкийfinalRect, але обрав лишеDesiredSize.Widthі центрував себе всередині.
StackPanel: вертикальний та горизонтальний стек
StackPanel — найпростіша і найчастіше вживана панель. Вона робить одну річ: вкладає дочірні елементи один за одним у вертикальному або горизонтальному напрямку.
Вертикальний StackPanel (за замовчуванням)
За замовчуванням Orientation="Vertical". Кожен елемент іде під попереднім:
<StackPanel>
<TextBlock Text="Перший рядок"/>
<TextBlock Text="Другий рядок"/>
<TextBlock Text="Третій рядок"/>
<Button Content="Кнопка"/>
</StackPanel>
Що важливо: StackPanel розтягує дочірніх по поперечній осі (горизонталі у вертикальному StackPanel) до свого повного розміру. Тому кожен TextBlock і кожна Button займає повну ширину StackPanel. Але по основній осі (вертикалі) — елемент займає рівно стільки, скільки йому треба.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Margin="16" Background="#f8fafc">
<TextBlock Text="Меню навігації"
FontSize="16" FontWeight="Bold"
Foreground="#1e293b" Margin="0,0,0,8"/>
<Button Content="🏠 Головна" Margin="0,2" Padding="12,8" HorizontalContentAlignment="Left"/>
<Button Content="👤 Профіль" Margin="0,2" Padding="12,8" HorizontalContentAlignment="Left"/>
<Button Content="⚙️ Налаштування" Margin="0,2" Padding="12,8" HorizontalContentAlignment="Left"/>
<Button Content="📊 Статистика" Margin="0,2" Padding="12,8" HorizontalContentAlignment="Left"/>
<Button Content="❓ Допомога" Margin="0,2" Padding="12,8" HorizontalContentAlignment="Left"/>
<Separator Margin="0,8"/>
<Button Content="🚪 Вийти" Margin="0,2" Padding="12,8"
Background="#fef2f2" Foreground="#dc2626"
HorizontalContentAlignment="Left"/>
</StackPanel>
Горизонтальний StackPanel
Змінюємо Orientation="Horizontal" — і елементи вишиковуються в рядок:
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Content="Зберегти"/>
<Button Content="Скасувати"/>
<Button Content="Попередній перегляд"/>
</StackPanel>
У горизонтальному StackPanel:
- По основній осі (горизонталі) — елемент займає свій
DesiredSize.Width - По поперечній осі (вертикалі) — елемент розтягується до висоти StackPanel
Якщо ви хочете, щоб елементи не розтягувалися по висоті — задайте VerticalAlignment:
<StackPanel Orientation="Horizontal" Spacing="8" Height="60"
VerticalAlignment="Center">
<Button Content="Маленька" VerticalAlignment="Center" Padding="8,4"/>
<Button Content="Велика" Padding="8,12"/> <!-- Займе всю висоту -->
<Button Content="Середня" VerticalAlignment="Center" Padding="8,8"/>
</StackPanel>
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<StackPanel Background="#1e293b">
<!-- Toolbar верхнього рівня -->
<StackPanel Orientation="Horizontal" Margin="8,4" Spacing="2">
<Button Content="📁 Файл" Padding="10,6" Background="Transparent" Foreground="White"/>
<Button Content="✏️ Правка" Padding="10,6" Background="Transparent" Foreground="White"/>
<Button Content="👁️ Вигляд" Padding="10,6" Background="Transparent" Foreground="White"/>
<Button Content="🔧 Сервіс" Padding="10,6" Background="Transparent" Foreground="White"/>
<Button Content="❓ Довідка" Padding="10,6" Background="Transparent" Foreground="White"/>
</StackPanel>
<Separator Background="#334155"/>
<!-- Toolbar з іконками дій -->
<StackPanel Orientation="Horizontal" Margin="8,4" Spacing="4">
<Button Content="📄" Padding="8,6" Background="Transparent" Foreground="White" ToolTip="Новий файл"/>
<Button Content="📂" Padding="8,6" Background="Transparent" Foreground="White" ToolTip="Відкрити"/>
<Button Content="💾" Padding="8,6" Background="Transparent" Foreground="White" ToolTip="Зберегти"/>
<Separator Width="1" Background="#475569" Margin="4,2"/>
<Button Content="↩️" Padding="8,6" Background="Transparent" Foreground="White" ToolTip="Скасувати"/>
<Button Content="↪️" Padding="8,6" Background="Transparent" Foreground="White" ToolTip="Повторити"/>
<Separator Width="1" Background="#475569" Margin="4,2"/>
<Button Content="🔍" Padding="8,6" Background="Transparent" Foreground="White" ToolTip="Пошук"/>
</StackPanel>
</StackPanel>
Spacing — відступи між елементами
У WPF .NET 6+ у StackPanel доданий атрибут Spacing (спочатку лише в Avalonia, потім портований). Він задає відстань між елементами автоматично — без потреби руками задавати Margin кожному:
<!-- Без Spacing: треба задавати Margin кожному -->
<StackPanel>
<Button Content="Перша" Margin="0,0,0,8"/>
<Button Content="Друга" Margin="0,0,0,8"/>
<Button Content="Третя"/>
</StackPanel>
<!-- Зі Spacing: один атрибут на всіх -->
<StackPanel Spacing="8">
<Button Content="Перша"/>
<Button Content="Друга"/>
<Button Content="Третя"/>
</StackPanel>
Обидва дають однаковий результат — але Spacing значно зручніший і чистіший.
Вкладені StackPanel
StackPanel легко вкладати: вертикальний StackPanel містить горизонтальні рядки, або навпаки:
<StackPanel Spacing="12" Margin="16">
<!-- Рядок 1: Поле вводу -->
<StackPanel>
<TextBlock Text="Ім'я користувача" FontWeight="SemiBold" Margin="0,0,0,4"/>
<TextBox Padding="8,6" PlaceholderText="Введіть ім'я..."/>
</StackPanel>
<!-- Рядок 2: Поле вводу -->
<StackPanel>
<TextBlock Text="Email" FontWeight="SemiBold" Margin="0,0,0,4"/>
<TextBox Padding="8,6" PlaceholderText="email@example.com"/>
</StackPanel>
<!-- Рядок 3: Горизонтальна пара кнопок -->
<StackPanel Orientation="Horizontal" Spacing="8" HorizontalAlignment="Right">
<Button Content="Скасувати" Padding="12,8"/>
<Button Content="Зберегти" Padding="12,8" Background="#2563eb" Foreground="White"/>
</StackPanel>
</StackPanel>
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<Border Background="White" Padding="24" CornerRadius="8" Margin="20"
BorderBrush="#e2e8f0" BorderThickness="1">
<StackPanel Spacing="16" Width="320">
<TextBlock Text="Реєстрація" FontSize="22" FontWeight="Bold"
Foreground="#1e293b"/>
<StackPanel Spacing="4">
<TextBlock Text="Ім'я" FontSize="13" FontWeight="SemiBold" Foreground="#475569"/>
<TextBox Padding="10,7" Text="Іванко Петренко"
BorderBrush="#cbd5e1" BorderThickness="1"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Email" FontSize="13" FontWeight="SemiBold" Foreground="#475569"/>
<TextBox Padding="10,7" Text="ivan@example.com"
BorderBrush="#cbd5e1" BorderThickness="1"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Пароль" FontSize="13" FontWeight="SemiBold" Foreground="#475569"/>
<PasswordBox Padding="10,7" BorderBrush="#cbd5e1" BorderThickness="1"/>
</StackPanel>
<CheckBox Content="Погоджуюся з умовами використання"
Foreground="#64748b" FontSize="12"/>
<Button Content="Зареєструватися" Padding="0,10"
Background="#2563eb" Foreground="White" FontWeight="SemiBold"
HorizontalContentAlignment="Center"/>
<TextBlock HorizontalAlignment="Center" FontSize="12" Foreground="#94a3b8">
<Run Text="Вже є акаунт? "/>
<Run Text="Увійти" Foreground="#2563eb" TextDecorations="Underline"/>
</TextBlock>
</StackPanel>
</Border>
Коли StackPanel — правильний вибір
StackPanel ідеальний для:
✅ Вертикальні списки: меню, списки налаштувань, чекбокси, форми з вертикальним потоком
✅ Горизонтальні панелі: toolbar, рядок кнопок, breadcrumbs, tabs-рядок
✅ Фіксований вміст: коли кількість елементів невелика і не змінюється
✅ Як внутрішній контейнер: всередині Grid-комірок, Border, ScrollViewer
❌ Не підходить для:
- Складних 2D-сіток (там потрібен
Grid) - Великих списків з тисячами елементів (там
ListBoxабоItemsControlзVirtualizingStackPanel) - Коли потрібна точна розбивка на рядки/колонки з вирівнюванням між рядками
WrapPanel: стек з автоматичним переносом
WrapPanel — це StackPanel, якому дозволили "переносити рядок". Якщо при горизонтальному вкладанні наступний елемент не вміщається у ширину — він переходить на новий рядок. При вертикальному — на новий стовпець.
Основна поведінка
<!-- Горизонтальний WrapPanel (за замовчуванням) -->
<WrapPanel>
<Button Content="Елемент А" Margin="4" Padding="12,8"/>
<Button Content="Елемент Б" Margin="4" Padding="12,8"/>
<Button Content="Елемент В" Margin="4" Padding="12,8"/>
<!-- Якщо В не вміщається — переноситься на новий рядок -->
<Button Content="Елемент Г" Margin="4" Padding="12,8"/>
<Button Content="Елемент Д" Margin="4" Padding="12,8"/>
</WrapPanel>
Ключова відмінність від StackPanel:
StackPanelніколи не переносить — елементи просто виходять за межі і обрізаються (або розтягують панель нескінченно)WrapPanelвідстежує доступну ширину і переносить при нестачі
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<WrapPanel Margin="16">
<Button Content="JavaScript" Margin="4" Padding="10,6" Background="#f7df1e" Foreground="#000"/>
<Button Content="TypeScript" Margin="4" Padding="10,6" Background="#3178c6" Foreground="White"/>
<Button Content="Python" Margin="4" Padding="10,6" Background="#3776ab" Foreground="White"/>
<Button Content="C#" Margin="4" Padding="10,6" Background="#512bd4" Foreground="White"/>
<Button Content="Rust" Margin="4" Padding="10,6" Background="#ce422b" Foreground="White"/>
<Button Content="Go" Margin="4" Padding="10,6" Background="#00add8" Foreground="White"/>
<Button Content="Java" Margin="4" Padding="10,6" Background="#f89820" Foreground="White"/>
<Button Content="Kotlin" Margin="4" Padding="10,6" Background="#7f52ff" Foreground="White"/>
<Button Content="Swift" Margin="4" Padding="10,6" Background="#ff6347" Foreground="White"/>
<Button Content="Ruby" Margin="4" Padding="10,6" Background="#cc342d" Foreground="White"/>
<Button Content="PHP" Margin="4" Padding="10,6" Background="#777bb4" Foreground="White"/>
<Button Content="Dart" Margin="4" Padding="10,6" Background="#0175c2" Foreground="White"/>
</WrapPanel>
ItemWidth та ItemHeight: рівномірна сітка
За замовчуванням кожен елемент займає стільки місця, скільки йому треба — тому елементи у WrapPanel можуть мати різну ширину. Щоб зробити рівномірну сітку — використовуйте ItemWidth та ItemHeight:
<!-- Всі елементи матимуть ширину 120 і висоту 80 -->
<WrapPanel ItemWidth="120" ItemHeight="80">
<Border Background="#3b82f6" Margin="4">
<TextBlock Text="Картка 1" HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="White"/>
</Border>
<Border Background="#8b5cf6" Margin="4">
<TextBlock Text="Картка 2" HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="White"/>
</Border>
<Border Background="#10b981" Margin="4">
<TextBlock Text="Картка 3" HorizontalAlignment="Center"
VerticalAlignment="Center" Foreground="White"/>
</Border>
<!-- ... -->
</WrapPanel>
Коли заданий ItemWidth — WrapPanel ділить доступну ширину на ItemWidth і визначає, скільки елементів поміщається у рядок. Решта переноситься. Отримується поведінка подібна до CSS display: flex; flex-wrap: wrap;.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<WrapPanel ItemWidth="130" ItemHeight="100" Margin="12">
<Border Margin="4" Background="#dbeafe" CornerRadius="6">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="📁" FontSize="24" HorizontalAlignment="Center"/>
<TextBlock Text="Документи" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Border>
<Border Margin="4" Background="#fce7f3" CornerRadius="6">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="🖼️" FontSize="24" HorizontalAlignment="Center"/>
<TextBlock Text="Зображення" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Border>
<Border Margin="4" Background="#dcfce7" CornerRadius="6">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="🎵" FontSize="24" HorizontalAlignment="Center"/>
<TextBlock Text="Музика" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Border>
<Border Margin="4" Background="#fef9c3" CornerRadius="6">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="🎬" FontSize="24" HorizontalAlignment="Center"/>
<TextBlock Text="Відео" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Border>
<Border Margin="4" Background="#ede9fe" CornerRadius="6">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="📦" FontSize="24" HorizontalAlignment="Center"/>
<TextBlock Text="Архіви" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Border>
<Border Margin="4" Background="#fee2e2" CornerRadius="6">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="⚙️" FontSize="24" HorizontalAlignment="Center"/>
<TextBlock Text="Система" HorizontalAlignment="Center" FontSize="11"/>
</StackPanel>
</Border>
</WrapPanel>
Вертикальний WrapPanel
Orientation="Vertical" вкладає елементи зверху вниз і переносить на новий стовпець при нестачі висоти:
<WrapPanel Orientation="Vertical" Height="200">
<Button Content="А" Margin="4" Padding="10,8"/>
<Button Content="Б" Margin="4" Padding="10,8"/>
<Button Content="В" Margin="4" Padding="10,8"/>
<!-- При нестачі висоти — переноситься у другий стовпець -->
<Button Content="Г" Margin="4" Padding="10,8"/>
<Button Content="Д" Margin="4" Padding="10,8"/>
</WrapPanel>
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<WrapPanel Orientation="Vertical" Height="160" Margin="16">
<Border Background="#93c5fd" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Альфа" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#86efac" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Бета" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#fca5a5" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Гамма" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#fdba74" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Дельта" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#c4b5fd" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Епсилон" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#67e8f9" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Дзета" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#a3e635" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Ета" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
<Border Background="#fb923c" Width="80" Height="40" Margin="4" CornerRadius="4">
<TextBlock Text="Тета" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="11"/>
</Border>
</WrapPanel>
WrapPanel у ScrollViewer: галерея
WrapPanel часто використовують в парі зі ScrollViewer. Так виходить адаптивна галерея: при зменшенні вікна — більше рядків але скролл компенсує:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<WrapPanel Margin="8">
<!-- Тут може бути 100 карток/зображень -->
<Border Width="160" Height="120" Margin="6" Background="#e2e8f0" CornerRadius="8">
<TextBlock Text="Фото 1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<!-- ... -->
</WrapPanel>
</ScrollViewer>
WrapPanel — не найефективніший для великих списків. При 1000+ елементів краще використовувати ItemsControl з WrapPanel як ItemsPanel — тоді WPF застосує віртуалізацію (тільки видимі елементи існують у пам'яті). Але для UI-компонентів (кнопки тегів, набори іконок, менеджер файлів) — WrapPanel абсолютно підходить.Коли WrapPanel — правильний вибір
✅ Набори тегів або категорій: кнопки-теги, фільтри, чіпи
✅ Файловий менеджер / галерея: іконки файлів що заповнюють вікно
✅ Адаптивні панелі кнопок: toolbar що переносить кнопки при вузькому вікні
✅ Будь-яке розміщення "зліва направо з переносом"
❌ Не підходить для:
- Таблиць з вирівняними стовпцями (там
Grid) - Фіксованих сіток де важлива порядність рядків (там
UniformGridабоGrid) - Великих динамічних списків без віртуалізації
DockPanel: прикріплення до сторін
DockPanel — панель для побудови класичного інтерфейсу настільного застосунку: меню зверху, панель статусу знизу, дерево/навігація зліва, основний вміст у центрі. Цей патерн настільки поширений, що має власну назву — Shell Layout.
Принцип роботи: кожен дочірній елемент "прикріплюється" до однієї зі сторін через Attached Property DockPanel.Dock. Значення: Top, Bottom, Left, Right. Після всіх прикріплених — останній дочірній займає весь решта простору. Ця поведінка регулюється LastChildFill (за замовчуванням True).
Базовий синтаксис
<DockPanel>
<Menu DockPanel.Dock="Top"/> <!-- прикріплено зверху -->
<StatusBar DockPanel.Dock="Bottom"/> <!-- прикріплено знизу -->
<TreeView DockPanel.Dock="Left" Width="200"/> <!-- прикріплено зліва -->
<TextBox AcceptsReturn="True"/> <!-- LastChildFill: займає весь центр -->
</DockPanel>
Порядок оголошення має значення: DockPanel обробляє елементи зверху вниз по XML. Кожен наступний отримує залишок простору після попередніх.
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<DockPanel>
<Menu DockPanel.Dock="Top" Background="#1e293b" Foreground="White" Padding="4,2">
<MenuItem Header="📁 Файл" Foreground="White"/>
<MenuItem Header="✏️ Правка" Foreground="White"/>
<MenuItem Header="👁️ Вигляд" Foreground="White"/>
</Menu>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal"
Background="#f1f5f9" Padding="8,4" Spacing="4">
<Button Content="📄" Padding="8,4"/>
<Button Content="💾" Padding="8,4"/>
<Separator Width="1" Background="#cbd5e1" Margin="4,0"/>
<Button Content="↩️" Padding="8,4"/>
<Button Content="↪️" Padding="8,4"/>
</StackPanel>
<StatusBar DockPanel.Dock="Bottom" Background="#1e293b" Padding="8,4">
<StatusBarItem>
<TextBlock Text="✅ Готово | Рядок 1 | UTF-8" Foreground="White" FontSize="12"/>
</StatusBarItem>
</StatusBar>
<Border DockPanel.Dock="Left" Width="180" Background="#f8fafc"
BorderBrush="#e2e8f0" BorderThickness="0,0,1,0">
<StackPanel Margin="8" Spacing="2">
<TextBlock Text="ПРОВІДНИК" FontSize="11" FontWeight="Bold" Foreground="#94a3b8" Margin="4,8,4,4"/>
<Button Content="📁 src" HorizontalContentAlignment="Left" Padding="8,4" Background="Transparent"/>
<Button Content=" 📄 App.xaml" HorizontalContentAlignment="Left" Padding="8,4" Background="#e0f2fe" FontSize="12"/>
<Button Content=" 📄 MainWindow.xaml" HorizontalContentAlignment="Left" Padding="8,4" Background="Transparent" FontSize="12"/>
<Button Content="📁 Resources" HorizontalContentAlignment="Left" Padding="8,4" Background="Transparent"/>
</StackPanel>
</Border>
<TextBox AcceptsReturn="True" Padding="16" Background="White"
BorderThickness="0" FontFamily="Consolas" FontSize="13"
Text="// Тут ваш контент
// Займає всю решту площі
// завдяки LastChildFill=True"/>
</DockPanel>
Порядок обробки: чому важливий XAML-порядок
Кожен елемент "відрізає" свою частину від загального простору. Наступний отримує залишок:
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<DockPanel Width="420" Height="260" Margin="16">
<Border DockPanel.Dock="Top" Height="40" Background="#3b82f6">
<TextBlock Text="Top (40px)" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="White" FontWeight="Bold"/>
</Border>
<Border DockPanel.Dock="Bottom" Height="30" Background="#ef4444">
<TextBlock Text="Bottom (30px)" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="White" FontWeight="Bold"/>
</Border>
<Border DockPanel.Dock="Left" Width="90" Background="#10b981">
<TextBlock Text="Left\n(90px)" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="White" FontWeight="Bold" TextAlignment="Center"/>
</Border>
<Border DockPanel.Dock="Right" Width="70" Background="#f59e0b">
<TextBlock Text="Right\n(70px)" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="White" FontWeight="Bold" TextAlignment="Center"/>
</Border>
<Border Background="#6366f1">
<TextBlock Text="Fill
(весь залишок)" VerticalAlignment="Center" HorizontalAlignment="Center"
Foreground="White" FontWeight="Bold" TextAlignment="Center"/>
</Border>
</DockPanel>
Кілька елементів на одній стороні
Один Dock може бути у кількох елементів — вони вишиковуються по черзі в тому ж напрямку:
<DockPanel>
<!-- Обидва зверху: Menu, потім Toolbar під ним -->
<Menu DockPanel.Dock="Top"/>
<ToolBar DockPanel.Dock="Top"/>
<!-- Bottom: перший Bottom буде ближче до краю, другий — вище нього -->
<StatusBar DockPanel.Dock="Bottom"/>
<FindBar DockPanel.Dock="Bottom"/>
<TextBox/>
</DockPanel>
Loading Avalonia WebAssembly...
Downloading .NET runtime (10MB)...
<DockPanel Height="300" Margin="16">
<Border DockPanel.Dock="Top" Height="36" Background="#1e40af">
<TextBlock Text="Menu Bar" VerticalAlignment="Center" Margin="12,0" Foreground="White" FontWeight="Bold"/>
</Border>
<Border DockPanel.Dock="Top" Height="32" Background="#3b82f6">
<TextBlock Text="Toolbar" VerticalAlignment="Center" Margin="12,0" Foreground="White"/>
</Border>
<Border DockPanel.Dock="Bottom" Height="24" Background="#1e3a8a">
<TextBlock Text="Status Bar (самий низ)" VerticalAlignment="Center" Margin="8,0" Foreground="White" FontSize="11"/>
</Border>
<Border DockPanel.Dock="Bottom" Height="32" Background="#2563eb">
<TextBlock Text="Find Bar (над Status)" VerticalAlignment="Center" Margin="8,0" Foreground="White" FontSize="12"/>
</Border>
<Border DockPanel.Dock="Left" Width="120" Background="#f0fdf4">
<TextBlock Text="Nav Tree" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#166534" FontWeight="Bold"/>
</Border>
<Border DockPanel.Dock="Left" Width="60" Background="#dcfce7">
<TextBlock Text="Mini" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#166534" FontSize="11"/>
</Border>
<Border Background="#f8fafc">
<TextBlock Text="Content (Fill)" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#64748b"/>
</Border>
</DockPanel>
LastChildFill="False"
Якщо не потрібно, щоб останній елемент займав весь центр:
<!-- Всі елементи з явним Dock: центру немає -->
<DockPanel LastChildFill="False">
<Button DockPanel.Dock="Top" Content="Верх"/>
<Button DockPanel.Dock="Left" Content="Ліво"/>
<Button DockPanel.Dock="Right" Content="Право"/>
<!-- Без LastChildFill останній також просто прикріплюється зверху -->
<Button DockPanel.Dock="Top" Content="Ще верх"/>
</DockPanel>
DockPanel є одним з рідкісних випадків у WPF, де порядок дочірніх у XAML прямо впливає на результат. StackPanel і WrapPanel — теж залежать від порядку, але у DockPanel ця залежність найбільш драматична: переставити Menu нижче за StatusBar в XAML — і UI зламається.Підсумок: яку панель обрати
StackPanel
Для: Вертикальних форм, горизонтальних toolbar, вкладених рядків меню. Елементи займають свій DesiredSize по основній осі і 100% по поперечній.
Використовуйте коли: Потрібен простий лінійний потік елементів без переносу і без прив'язки до сторін.
WrapPanel
Для: Тегів, фільтрів, файлових іконок, адаптивних наборів кнопок. ItemWidth/ItemHeight для рівних клітинок.
Використовуйте коли: Елументи мають переноситися на нові рядки автоматично при нестачі місця.
DockPanel
Для: Shell Layout (Menu + Toolbar + Sidebar + Content + StatusBar). Порядок у XAML = порядок розміщення.
Використовуйте коли: Потрібна типова оболонка застосунку або будь-який layout з "периферійними" зонами та центральним вмістом.
| StackPanel | WrapPanel | DockPanel | |
|---|---|---|---|
| Авто-перенос | ❌ | ✅ | ❌ |
| Рівномірна сітка | ❌ | ItemWidth | ❌ |
| Центральний Fill | ❌ | ❌ | ✅ |
| Вкладення | ✅ | рідко | ✅ |
Практичні завдання
Завдання: Побудуйте горизонтальний toolbar через StackPanel Orientation="Horizontal".
Вимоги:
- 5 кнопок: "📄 Новий", "📂 Відкрити", "💾 Зберегти", "↩️ Скасувати", "↪️ Повторити"
Spacing="4", вертикальнийSeparatorміж групами (після "Зберегти")- Темний фон
#1e293b, кнопки прозорі зForeground="White"
Поглиблення: Додайте другий горизонтальний StackPanel-рядок вкладок файлів.
Перевірка: Toolbar виглядає як у реальному редакторі — без видимих меж кнопок.
Завдання: Класичний "оболонковий" інтерфейс через DockPanel.
Обов'язкові зони:
DockPanel.Dock="Top"— Menu (горизонтальний StackPanel з TextBlock)DockPanel.Dock="Top"— Toolbar (кнопки)DockPanel.Dock="Bottom"— StatusBarDockPanel.Dock="Left"Width="200"— Sidebar з навігаційними кнопками- Fill — велика TextBox або Border
Поглиблення: DockPanel.Dock="Right" Width="160" — панель властивостей.
Перевірка: Змінення розміру вікна → Zone зберігають свою ширину/висоту, Fill адаптується.
Завдання: Адаптивна галерея ScrollViewer → WrapPanel + адаптивні фільтри.
Вимоги:
ScrollViewer→WrapPanelItemWidth="180"ItemHeight="140"— 12+ карток файлів- Кожна картка:
Border CornerRadius="8", emoji-іконка (FontSize=32), назва файлу - Різний
Backgroundдля різних типів файлів - Фільтри зверху (через
DockPanel):WrapPanelз кнопками-тегами "Всі", "Зображення", "Відео", "Документи"
Перевірка: При вузькому вікні — менше стовпців (автоматично). ScrollViewer компенсує висоту. Фільтри-теги самі переносяться при нестачі ширини.
Розширення розмітки XAML (Markup Extensions)
Вивчаємо механізм фігурних дужок {…} у XAML: MarkupExtension, ProvideValue, вбудовані x:Static/x:Type/x:Null/x:Array/Binding, перший погляд на Binding та створення Custom Markup Extension.
Grid, Canvas, UniformGrid
Вивчаємо Grid — найпотужнішу панель WPF: типи розмірів Auto/**/fixed, Grid.Row/Column, RowSpan/ColumnSpan, SharedSizeGroup. Розбираємо Canvas та UniformGrid.