📦

WPF: Контейнеры (Panels)

Раскладываем элементы правильно

Полное руководство

Сегодня узнаем о всех типах контейнеров WPF:

  • 📐 Grid — табличная верстка
  • 📏 StackPanel — линейное расположение
  • 🎯 Canvas — абсолютное позиционирование
  • 📊 WrapPanel — перенос на новую строку
  • 📐 DockPanel — пристыковка к краям
  • 📦 VirtualizingPanel — для больших списков

🤔 Что такое контейнеры?

Контейнеры (Panels) — это специальные элементы управления, которые определяют, как дочерние элементы располагаются на экране.

Без контейнера

🟦 Кнопка 1
🟩 Кнопка 2
🟥 Кнопка 3
(просто друг под другом)

С контейнером Grid

1
2
3
4
5
6
💡 Зачем нужны контейнеры? Они делают интерфейс гибким, адаптивным и красивым!

🌳 Иерархия Panel в WPF

Panel (абстрактный класс)
├── Grid ⭐⭐⭐⭐⭐ (самый популярный)
├── StackPanel ⭐⭐⭐⭐
├── WrapPanel ⭐⭐⭐
├── DockPanel ⭐⭐⭐
├── Canvas ⭐⭐
├── VirtualizingPanel ⭐⭐⭐⭐ (для производительности)
│   ├── VirtualizingStackPanel
│   └── VirtualizingWrapPanel
└── UniformGrid ⭐⭐
💡 Совет: В 90% случаев вам хватит Grid и StackPanel!

📊 Grid — табличная верстка

<Grid>
    <!-- Определяем строки -->
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>      <!-- По содержимому -->
        <RowDefinition Height="*"/>          <!-- Занять всё свободное место -->
        <RowDefinition Height="2*"/>         <!-- В 2 раза больше -->
        <RowDefinition Height="50"/>         <!-- Фиксированная высота -->
    </Grid.RowDefinitions>
    
    <!-- Определяем столбцы -->
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="100"/>
    </Grid.ColumnDefinitions>
    
    <!-- Элементы -->
    <Label Grid.Row="0" Grid.Column="0" Content="Имя:"/>
    <TextBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"/>
    <Button Grid.Row="1" Grid.Column="1" Content="OK"/>
</Grid>
📌 Важно: Нумерация строк и столбцов начинается с 0! Grid.Row="0" — первая строка.

👁️ Визуализация Grid

Label
TextBox (ColumnSpan=2)
Button
Большая область (2* высота)
Фиксированная высота 50px
<!-- Размеры строк -->
Row 0: Auto (по содержимому)
Row 1: * (занимает доступное место)
Row 2: 2* (в 2 раза больше)
Row 3: 50 (фиксированная)

✏️ Практика: Создаем форму регистрации с Grid

<Grid Margin="20">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    
    <Label Grid.Row="0" Grid.Column="0" Content="Имя:" VerticalAlignment="Center"/>
    <TextBox Grid.Row="0" Grid.Column="1" Margin="5"/>
    
    <Label Grid.Row="1" Grid.Column="0" Content="Email:" VerticalAlignment="Center"/>
    <TextBox Grid.Row="1" Grid.Column="1" Margin="5"/>
    
    <Label Grid.Row="2" Grid.Column="0" Content="Пароль:" VerticalAlignment="Center"/>
    <PasswordBox Grid.Row="2" Grid.Column="1" Margin="5"/>
    
    <Label Grid.Row="3" Grid.Column="0" Content="О себе:" VerticalAlignment="Top"/>
    <TextBox Grid.Row="3" Grid.Column="1" Height="80" TextWrapping="Wrap" Margin="5"/>
    
    <CheckBox Grid.Row="4" Grid.Column="1" Content="Согласен с условиями" Margin="5"/>
    
    <Button Grid.Row="5" Grid.Column="1" Content="Зарегистрироваться" 
            Height="40" Width="150" HorizontalAlignment="Left" Margin="5"/>
</Grid>

✏️ ЗАДАНИЕ 1 (10 минут)

Создай форму входа (Login) с помощью Grid: Поля "Логин", "Пароль", кнопка "Войти", ссылка "Забыли пароль?".

📏 StackPanel — линейное расположение

Вертикальный (по умолчанию)

Элемент 1
Элемент 2
Элемент 3
<StackPanel>
    <Button Content="Кнопка 1"/>
    <Button Content="Кнопка 2"/>
    <Button Content="Кнопка 3"/>
</StackPanel>

Горизонтальный

1
2
3
<StackPanel Orientation="Horizontal">
    <Button Content="1"/>
    <Button Content="2"/>
    <Button Content="3"/>
</StackPanel>
💡 Когда использовать: Меню, панели инструментов, списки кнопок.

📱 Примеры использования StackPanel

<!-- Горизонтальное меню -->
<StackPanel Orientation="Horizontal" Background="#333">
    <Button Content="Главная" Style="{StaticResource MenuButton}"/>
    <Button Content="О нас" Style="{StaticResource MenuButton}"/>
    <Button Content="Контакты" Style="{StaticResource MenuButton}"/>
</StackPanel>

<!-- Боковая панель с инструментами -->
<StackPanel Background="#f0f0f0" Width="200">
    <Button Content="✏️ Рисовать" Margin="5"/>
    <Button Content="📷 Вставить картинку" Margin="5"/>
    <Button Content="💾 Сохранить" Margin="5"/>
    <Separator Margin="5"/>
    <Button Content="🗑️ Удалить" Margin="5"/>
</StackPanel>

<!-- Форма (вертикально) -->
<StackPanel>
    <Label Content="Имя:"/>
    <TextBox/>
    <Label Content="Возраст:"/>
    <TextBox/>
    <Button Content="Отправить"/>
</StackPanel>

📊 WrapPanel — перенос на новую строку

WrapPanel располагает элементы, перенося их на новую строку/столбец, когда заканчивается место.
Кнопка 1 Кнопка 2 Кнопка 3 Кнопка 4 Кнопка 5 Кнопка 6
Если не влезают → перенос на следующую строку
<WrapPanel Orientation="Horizontal">
    <Button Content="Элемент 1" Width="120" Height="40"/>
    <Button Content="Элемент 2" Width="120" Height="40"/>
    <Button Content="Элемент 3" Width="120" Height="40"/>
    <Button Content="Элемент 4" Width="120" Height="40"/>
    <Button Content="Элемент 5" Width="120" Height="40"/>
    <Button Content="Элемент 6" Width="120" Height="40"/>
</WrapPanel>
💡 Идеально для: Облака тегов, галерей изображений, кнопок фильтров.

🎯 DockPanel — пристыковка к краям

Top (сверху)
Left
Center (заполняет всё)
Right
Bottom (снизу)
<DockPanel LastChildFill="True">
    <!-- Последний элемент заполняет оставшееся место -->
    <Button Content="Верх" DockPanel.Dock="Top" Height="40"/>
    <Button Content="Низ" DockPanel.Dock="Bottom" Height="40"/>
    <Button Content="Лево" DockPanel.Dock="Left" Width="150"/>
    <Button Content="Право" DockPanel.Dock="Right" Width="100"/>
    <Button Content="Центр (заполняет всё)"/>
</DockPanel>
💡 LastChildFill="True" — последний элемент займет всё оставшееся место.

📍 Canvas — абсолютное позиционирование

⚠️ Canvas НЕ адаптивен! Элементы имеют фиксированные координаты.
<Canvas Background="LightGray" Width="400" Height="300">
    <Button Content="Кнопка 1" Canvas.Left="50" Canvas.Top="30"/>
    <Button Content="Кнопка 2" Canvas.Left="200" Canvas.Top="30"/>
    <Button Content="Кнопка 3" Canvas.Left="50" Canvas.Top="100"/>
    <Button Content="Кнопка 4" Canvas.Left="200" Canvas.Top="100"/>
    <!-- Рисование -->
    <Rectangle Fill="Red" Width="50" Height="50" Canvas.Left="300" Canvas.Top="200"/>
    <Ellipse Fill="Blue" Width="40" Height="40" Canvas.Left="10" Canvas.Top="250"/>
</Canvas>
💡 Когда использовать: Графические редакторы, игры, анимации, где нужно точное позиционирование.

🔲 UniformGrid — равномерная сетка

<UniformGrid Rows="3" Columns="3">
    <Button Content="1"/> <Button Content="2"/> <Button Content="3"/>
    <Button Content="4"/> <Button Content="5"/> <Button Content="6"/>
    <Button Content="7"/> <Button Content="8"/> <Button Content="9"/>
</UniformGrid>

<!-- Или проще: без Rows/Columns -- сам определит -->
<UniformGrid>
    <Button Content="A"/> <Button Content="B"/> <Button Content="C"/>
    <Button Content="D"/> <Button Content="E"/> <Button Content="F"/>
</UniformGrid>
1
2
3
4
5
6
7
8
9
💡 Идеально для: Калькуляторов, шахматной доски, галерей.

📊 Сравнение контейнеров WPF

КонтейнерГибкостьСложностьКогда использовать
Grid⭐⭐⭐⭐⭐⭐⭐⭐Сложные формы, привязка к позиции
StackPanel⭐⭐⭐⭐Списки, меню, простые формы
WrapPanel⭐⭐⭐⭐⭐Облака тегов, адаптивные панели
DockPanel⭐⭐⭐⭐⭐⭐IDE, главные окна с панелями
Canvas⭐⭐Рисование, анимация, игры
UniformGrid⭐⭐⭐⭐Каталоги, калькуляторы
🏆 Grid — король контейнеров! Используйте его в 80% случаев.

🪆 Вложенные контейнеры (Nested Panels)

В WPF можно комбинировать контейнеры, вкладывая их друг в друга!
<!-- Главный Grid -->
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>   <!-- Заголовок -->
        <RowDefinition Height="*"/>       <!-- Контент -->
        <RowDefinition Height="Auto"/>   <!-- Подвал -->
    </Grid.RowDefinitions>
    
    <!-- Верхняя панель (StackPanel) -->
    <StackPanel Grid.Row="0" Orientation="Horizontal" Background="#333">
        <Button Content="Главная"/>
        <Button Content="Новости"/>
        <Button Content="Контакты"/>
    </StackPanel>
    
    <!-- Основной контент (Grid внутри Grid) -->
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        
        <!-- Левая панель (WrapPanel) -->
        <WrapPanel Grid.Column="0" Background="#f0f0f0">
            <Button Content="Инструмент 1" Width="180" Margin="5"/>
            <Button Content="Инструмент 2" Width="180" Margin="5"/>
            <Button Content="Инструмент 3" Width="180" Margin="5"/>
        </WrapPanel>
        
        <!-- Правая панель (DockPanel) -->
        <DockPanel Grid.Column="1">
            <TextBox DockPanel.Dock="Top" Height="30" Margin="5"/>
            <ListBox/>
        </DockPanel>
    </Grid>
    
    <!-- Подвал (DockPanel) -->
    <StatusBar Grid.Row="2">
        <StatusBarItem Content="Готов"/>
    </StatusBar>
</Grid>

✏️ Практика: Создаем макет приложения

✏️ ЗАДАНИЕ 2 (20 минут)

Создай типичный макет приложения с помощью контейнеров:

  • 📌 Верхняя панель (StackPanel) — меню: "Файл, Правка, Вид, Справка"
  • 📌 Левая панель (StackPanel) — инструменты (5 кнопок)
  • 📌 Центральная область (Grid) — основное содержимое
  • 📌 Нижняя панель (StatusBar) — строка состояния
  • 📌 Правая панель (WrapPanel) — свойства

🎨 Используй разные цвета для наглядности.

⚡ VirtualizingPanel — для производительности

VirtualizingPanel виртуализирует элементы — создает только те, которые видны на экране!

Без виртуализации

📦 10,000 элементов созданы в памяти
💾 Память: ~200 MB
🐌 Очень медленно

С виртуализацией

📦 Только видимые элементы (10-20)
💾 Память: ~10 MB
⚡ Очень быстро
<!-- VirtualizingStackPanel используется по умолчанию в ListBox -->
<ListBox ItemsSource="{Binding BigList}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel VirtualizationMode="Recycling"/>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

<!-- Отключить виртуализацию (не рекомендуется для больших списков) -->
<ListBox VirtualizingPanel.IsVirtualizing="False" .../>
⚠️ Виртуализация работает только при прокрутке! Без прокрутки она не нужна.

🔄 GridSplitter — изменяемые размеры

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200"/>
        <ColumnDefinition Width="5"/>    <!-- Место для Splitter -->
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    
    <!-- Левая панель -->
    <ListBox Grid.Column="0" Background="#f0f0f0"/>
    
    <!-- Разделитель -->
    <GridSplitter Grid.Column="1" Width="5" 
                  HorizontalAlignment="Stretch" 
                  Background="Gray"
                  ResizeDirection="Columns"
                  ResizeBehavior="PreviousAndNext"/>
    
    <!-- Правая панель (изменяемый размер) -->
    <TextBox Grid.Column="2"/>
</Grid>
Левая панель
Правая панель
↔️ Можно перетаскивать мышкой

📜 ScrollViewer + Panel = Прокрутка

<!-- WrapPanel с прокруткой -->
<ScrollViewer Height="300">
    <WrapPanel>
        <!-- Много элементов -->
        <Button Content="1" Width="100" Height="50" Margin="5"/>
        <Button Content="2" Width="100" Height="50" Margin="5"/>
        <!-- ... 50 элементов -->
    </WrapPanel>
</ScrollViewer>

<!-- StackPanel с прокруткой -->
<ScrollViewer>
    <StackPanel>
        <TextBox Height="500"/>
        <TextBox Height="500"/>
        <TextBox Height="500"/>
    </StackPanel>
</ScrollViewer>
💡 ScrollViewer автоматически показывает полосы прокрутки, когда контент не помещается!

🖼️ Border — рамка для элементов

<Border Background="LightBlue" 
        BorderBrush="Blue" 
        BorderThickness="2" 
        CornerRadius="10" 
        Padding="10">
    
    <StackPanel>
        <Label Content="Заголовок" FontWeight="Bold"/>
        <TextBox Text="Содержимое"/>
        <Button Content="OK"/>
    </StackPanel>
</Border>
Заголовок
Содержимое
💡 Border может оборачивать ТОЛЬКО ОДИН элемент! Используйте Panel внутри для нескольких.

🔍 Viewbox — масштабирование содержимого

<!-- Viewbox автоматически масштабирует содержимое -->
<Viewbox Width="200" Height="200">
    <Button Content="Кнопка" Width="100" Height="30"/>
</Viewbox>

<!-- Масштабирование с сохранением пропорций -->
<Viewbox Stretch="Uniform">
    <Canvas Width="100" Height="100">
        <Rectangle Fill="Red" Width="100" Height="100"/>
        <Ellipse Fill="Blue" Width="80" Height="80" Canvas.Left="10" Canvas.Top="10"/>
    </Canvas>
</Viewbox>
💡 Идеально для: Иконок, которые должны масштабироваться без потери качества.

📋 Шпаргалка: Какой контейнер выбрать?

🎯 Нужна таблица (сетка) с разными размерами строк/столбцов? → Grid
📏 Элементы просто идут друг за другом (меню, список)? → StackPanel
📊 Элементы переносятся на новую строку (облако тегов)? → WrapPanel
🎯 Нужно пристыковать элементы к краям (окно IDE)? → DockPanel
📍 Точное позиционирование (игра, редактор)? → Canvas
🔲 Все ячейки одинакового размера? → UniformGrid
⚡ Огромный список (10,000+ элементов)? → VirtualizingStackPanel

⚠️ Типичные ошибки с контейнерами

❌ Ошибка 1: Слишком много вложенных контейнеров
<Grid><StackPanel><WrapPanel><DockPanel>... (сложно читать)
✅ Решение: Используйте Grid с правильной разметкой
❌ Ошибка 2: Забыли указать Grid.Row/Grid.Column
<Grid>
    <Button/> <!-- Окажется в Row=0, Col=0 -->
    <Button Grid.Row="1"/> <!-- Ошибка: нет Column -->
</Grid>
❌ Ошибка 3: Canvas для адаптивного дизайна
<Canvas><Button Canvas.Left="50" Canvas.Top="30"/></Canvas>
✅ Решение: Используйте Grid или StackPanel
❌ Ошибка 4: Забыли включить виртуализацию для больших списков
<ListBox VirtualizingPanel.IsVirtualizing="False"/>
✅ Решение: Оставьте по умолчанию (True)

🏆 ИТОГОВОЕ ЗАДАНИЕ (30 минут)

🎯 Создай приложение "Мой идеальный рабочий стол"

Требования:

  • ✅ Использовать минимум 4 разных контейнера
  • ✅ Grid в качестве основы
  • ✅ Верхнее меню (StackPanel горизонтальный)
  • ✅ Левая боковая панель с инструментами (StackPanel вертикальный)
  • ✅ Центральная область с GridSplitter для изменения размеров
  • ✅ Правая панель с настройками (WrapPanel)
  • ✅ Нижняя панель статуса (DockPanel)
  • ✅ Кнопки, TextBox, ListBox, CheckBox для демонстрации
  • ✅ Красивый дизайн с цветами и отступами
💡 Бонус: Добавь прокрутку в центральную область (ScrollViewer)!

💡 Пример решения итогового задания

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    
    <!-- Верхнее меню -->
    <StackPanel Grid.Row="0" Orientation="Horizontal" Background="#333">
        <Button Content="Файл" Foreground="White" Background="Transparent"/>
        <Button Content="Правка" Foreground="White" Background="Transparent"/>
        <Button Content="Вид" Foreground="White" Background="Transparent"/>
    </StackPanel>
    
    <!-- Основная область -->
    <Grid Grid.Row="1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="200"/>
        </Grid.ColumnDefinitions>
        
        <!-- Левая панель -->
        <StackPanel Grid.Column="0" Background="#f0f0f0">
            <Button Content="Инструмент 1" Margin="5"/>
            <Button Content="Инструмент 2" Margin="5"/>
            <Button Content="Инструмент 3" Margin="5"/>
        </StackPanel>
        
        <GridSplitter Grid.Column="1" Width="5" Background="Gray"/>
        
        <!-- Центр с прокруткой -->
        <ScrollViewer Grid.Column="2">
            <StackPanel>
                <TextBox Height="200"/>
                <ListBox Height="200"/>
            </StackPanel>
        </ScrollViewer>
        
        <GridSplitter Grid.Column="3" Width="5" Background="Gray"/>
        
        <!-- Правая панель -->
        <WrapPanel Grid.Column="4" Background="#e8f4f8">
            <CheckBox Content="Опция 1" Margin="5"/>
            <CheckBox Content="Опция 2" Margin="5"/>
            <CheckBox Content="Опция 3" Margin="5"/>
        </WrapPanel>
    </Grid>
    
    <!-- Нижняя панель -->
    <StatusBar Grid.Row="2" Background="#ddd">
        <StatusBarItem Content="Готов"/>
        <Separator/>
        <StatusBarItem Content="Пользователь: Admin"/>
    </StatusBar>
</Grid>
🎉

Поздравляю!

Вы освоили контейнеры WPF

Что вы узнали:

  • ✅ Grid — табличная верстка, звездочки (*) и Auto
  • ✅ StackPanel — линейное расположение (вертикальное/горизонтальное)
  • ✅ WrapPanel — перенос элементов на новую строку
  • ✅ DockPanel — пристыковка к краям
  • ✅ Canvas — абсолютные координаты
  • ✅ UniformGrid — равномерная сетка
  • ✅ VirtualizingPanel — производительность для больших списков
  • ✅ GridSplitter — изменяемые размеры панелей
  • ✅ Вложенные контейнеры для сложных макетов
💪 Помните: Grid — ваш лучший друг в WPF!

🙏 Вопросы?