WrapPanel i DockPanel – zawijanie i dokowanie
Poznasz dwa przydatne layouty: WrapPanel, który automatycznie zawija elementy do nowego wiersza, oraz DockPanel, który przykleja elementy do krawędzi okna – jak paski narzędzi i menu w prawdziwych aplikacjach.
WrapPanel
Czym jest WrapPanel?
WrapPanel to layout podobny do StackPanel – układa elementy jeden za drugim. Kluczowa różnica: gdy zabraknie miejsca w wierszu, automatycznie przechodzi do nowego wiersza (lub kolumny – zależnie od kierunku).
Wyobraź sobie, że wkładasz książki na półkę. Kiedy na pierwszej półce nie ma już miejsca, zaczynasz następną. WrapPanel działa dokładnie tak – elementy „zawijają się” do nowego wiersza gdy zabraknie miejsca. To samo co robią słowa w edytorze tekstu – gdy wiersz jest pełny, nowe słowo pojawia się w kolejnej linii.
<WrapPanel> <Button Content="Jeden" Margin="5" Width="80" /> <Button Content="Dwa" Margin="5" Width="80" /> <Button Content="Trzy" Margin="5" Width="80" /> <Button Content="Cztery" Margin="5" Width="80" /> <Button Content="Pięć" Margin="5" Width="80" /> <Button Content="Sześć" Margin="5" Width="80" /> <Button Content="Siedem" Margin="5" Width="80" /> </WrapPanel> <!-- Gdy okno wąskie – przyciski zawijają się do kolejnych wierszy -->
Orientation – kierunek zawijania
Podobnie jak StackPanel, WrapPanel ma właściwość Orientation.
Zmienia ona kierunek, w którym elementy są układane,
a co za tym idzie – kierunek zawijania.
| Orientation | Elementy układane | Zawijanie |
|---|---|---|
Horizontal (domyślny) |
Od lewej do prawej | Do nowego wiersza (w dół) |
Vertical |
Od góry do dołu | Do nowej kolumny (w prawo) |
<WrapPanel Orientation="Vertical" Height="200"> <!-- Elementy idą od góry w dół, gdy zapełni się kolumna – przechodzą do następnej kolumny po prawej --> <Button Content="A" Margin="3" Height="40" Width="60" /> <Button Content="B" Margin="3" Height="40" Width="60" /> <Button Content="C" Margin="3" Height="40" Width="60" /> <Button Content="D" Margin="3" Height="40" Width="60" /> <Button Content="E" Margin="3" Height="40" Width="60" /> <Button Content="F" Margin="3" Height="40" Width="60" /> </WrapPanel>
ItemWidth i ItemHeight – jednolite rozmiary
Właściwości ItemWidth i ItemHeight narzucają
jednolity rozmiar wszystkim elementom w WrapPanel
– niezależnie od tego co ustawisz na poszczególnych kontrolkach.
Przydają się do tworzenia równomiernych siatek.
<!-- Wszystkie elementy mają szerokość 100px – tworzą równą siatkę --> <WrapPanel ItemWidth="100" ItemHeight="100"> <Border Background="SteelBlue" Margin="3"> <TextBlock Text="Kafelek 1" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <Border Background="Tomato" Margin="3"> <TextBlock Text="Kafelek 2" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <Border Background="MediumSeaGreen" Margin="3"> <TextBlock Text="Kafelek 3" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <Border Background="Gold" Margin="3"> <TextBlock Text="Kafelek 4" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <Border Background="MediumPurple" Margin="3"> <TextBlock Text="Kafelek 5" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> </WrapPanel>
Border to prosty kontener, który rysuje ramkę i/lub tło
wokół swojej zawartości. Może mieć jedno dziecko.
Używamy go gdy chcemy nadać kolor tła lub obramowanie
fragmentowi interfejsu – np. kafelkowi, karcie, panelowi boczneemu.
Właściwości: Background, BorderBrush,
BorderThickness, CornerRadius.
Praktyczne użycie WrapPanel
WrapPanel świetnie sprawdza się w kilku typowych sytuacjach:
| Zastosowanie | Dlaczego WrapPanel? |
|---|---|
| Galeria miniatur zdjęć | Miniatury zawijają się do nowych wierszy gdy okno jest wąskie |
| Lista tagów / znaczników | Tagi mają różne długości, zawijają się naturalnie |
| Siatka przycisków (kafelki) | Jednolite kafelki z ItemWidth/ItemHeight |
| Pasek narzędzi z ikonami | Ikony zawijają się gdy okno jest za wąskie |
DockPanel
Czym jest DockPanel?
DockPanel to layout, który pozwala przyklejać elementy do krawędzi – góry, dołu, lewej lub prawej strony panelu. Ostatni element domyślnie wypełnia całą pozostałą przestrzeń pośrodku.
Wyobraź sobie typowy program – Word, Notepad++, Visual Studio. Na górze jest menu i pasek narzędzi, na dole pasek stanu, po lewej może być panel projektów, a cała reszta to obszar roboczy. To dokładnie DockPanel – elementy przyklejone do krawędzi, środek wypełniony główną treścią.
DockPanel.Dock – cztery krawędzie
Każdemu elementowi w DockPanel mówisz, do której krawędzi ma się
„przykleić” za pomocą attached property DockPanel.Dock.
| Wartość | Element przyklejony do |
|---|---|
Top | Górnej krawędzi (zajmuje całą szerokość) |
Bottom | Dolnej krawędzi (zajmuje całą szerokość) |
Left | Lewej krawędzi (zajmuje całą pozostałą wysokość) |
Right | Prawej krawędzi (zajmuje całą pozostałą wysokość) |
<DockPanel> <!-- Górny pasek --> <Border DockPanel.Dock="Top" Background="#2d2d3f" Height="40"> <TextBlock Text="Pasek menu" Foreground="White" VerticalAlignment="Center" Margin="10,0" /> </Border> <!-- Dolny pasek stanu --> <Border DockPanel.Dock="Bottom" Background="#1e7e34" Height="25"> <TextBlock Text="Gotowy" Foreground="White" VerticalAlignment="Center" Margin="10,0" /> </Border> <!-- Panel boczny po lewej --> <Border DockPanel.Dock="Left" Background="#252535" Width="160"> <TextBlock Text="Panel boczny" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> <!-- Główna treść – ostatni element, wypełnia środek --> <Border Background="#1a1a2e"> <TextBlock Text="Główny obszar roboczy" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Border> </DockPanel>
LastChildFill – czy ostatni wypełnia środek?
Domyślnie ostatni element w DockPanel wypełnia całą
pozostałą przestrzeń – bez potrzeby podawania Dock.
To zachowanie kontroluje właściwość LastChildFill.
<!-- LastChildFill="True" jest domyślne – ostatni element wypełnia środek --> <DockPanel LastChildFill="True"> <Menu DockPanel.Dock="Top"></Menu> <StatusBar DockPanel.Dock="Bottom"></StatusBar> <TextBox AcceptsReturn="True" /> <!-- ostatni – wypełnia resztę --> </DockPanel> <!-- Gdy LastChildFill="False" – ostatni element zachowuje się normalnie --> <DockPanel LastChildFill="False"> <Button DockPanel.Dock="Top" Content="Góra" /> <Button DockPanel.Dock="Bottom" Content="Dół" /> <Button DockPanel.Dock="Left" Content="Lewo" /> <Button Content="Ostatni" /> <!-- Ostatni NIE wypełnia reszty – jest mały, w lewym rogu --> </DockPanel>
Kolejność elementów ma znaczenie!
W DockPanel kolejność, w jakiej piszesz elementy, ma ogromne znaczenie. Każdy element „zabiera” kawałek przestrzeni, a następny dostaje to co zostało.
<!-- PRZYKŁAD 1: Top → Bottom → Left → treść Panel boczny sięga od menu do pasku stanu --> <DockPanel> <Border DockPanel.Dock="Top" Height="40" Background="SteelBlue" /> <!-- menu --> <Border DockPanel.Dock="Bottom" Height="25" Background="Gray" /> <!-- pasek stanu --> <Border DockPanel.Dock="Left" Width="150" Background="#2d2d3f" /> <!-- sidebar --> <Border Background="#1a1a2e" /> <!-- treść --> </DockPanel> <!-- Sidebar sięga od dołu menu do góry paska stanu ✅ --> <!-- PRZYKŁAD 2: Left → Top → Bottom → treść Panel boczny sięga od góry do dołu okna --> <DockPanel> <Border DockPanel.Dock="Left" Width="150" Background="#2d2d3f" /> <!-- sidebar --> <Border DockPanel.Dock="Top" Height="40" Background="SteelBlue" /> <!-- menu --> <Border DockPanel.Dock="Bottom" Height="25" Background="Gray" /> <!-- pasek stanu --> <Border Background="#1a1a2e" /> <!-- treść --> </DockPanel> <!-- Sidebar sięga od góry do dołu okna (nad menu i pasek stanu) -->
Jeśli chcesz, żeby panel boczny był między
paskiem menu a paskiem stanu (jak w większości aplikacji),
zawsze definiuj Top i Bottom
przed Left i Right.
To najczęstszy sposób budowania layoutu okna w WPF.
Typowe zastosowania
Kompletny layout okna z DockPanel
DockPanel jest świetny do budowania szkieletu całego okna aplikacji. Oto kompletny przykład prostego notatnika:
<Window Title="Notatnik" Width="700" Height="500" WindowStartupLocation="CenterScreen"> <DockPanel> <!-- 1. Menu na górze --> <Menu DockPanel.Dock="Top"> <MenuItem Header="_Plik"> <MenuItem Header="_Nowy" x:Name="menuNowy" Click="MenuNowy_Click" /> <MenuItem Header="_Otwórz" x:Name="menuOtwórz" Click="MenuOtwórz_Click" /> <MenuItem Header="_Zapisz" x:Name="menuZapisz" Click="MenuZapisz_Click" /> <Separator /> <MenuItem Header="Za_mknij" Click="MenuZamknij_Click" /> </MenuItem> </Menu> <!-- 2. Pasek narzędzi pod menu --> <ToolBar DockPanel.Dock="Top"> <Button Content="📄 Nowy" Padding="8,3" /> <Button Content="📂 Otwórz" Padding="8,3" /> <Button Content="💾 Zapisz" Padding="8,3" /> </ToolBar> <!-- 3. Pasek stanu na dole --> <StatusBar DockPanel.Dock="Bottom"> <StatusBarItem> <TextBlock x:Name="lblStatus" Text="Gotowy | Wiersz: 1 Kolumna: 1" /> </StatusBarItem> </StatusBar> <!-- 4. Główny obszar – pole tekstowe wypełnia resztę --> <TextBox x:Name="txtTresc" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" FontFamily="Consolas" FontSize="14" Padding="5" /> </DockPanel> </Window>
Galeria z WrapPanel
WrapPanel idealnie nadaje się do dynamicznej galerii, której elementy dostosowują się do szerokości okna:
<!-- XAML: --> <ScrollViewer> <WrapPanel x:Name="panelGalerii" ItemWidth="150" ItemHeight="180" Margin="10" /> </ScrollViewer> // C# – wypełniamy galerię kafelkami: private void Window_Loaded(object sender, RoutedEventArgs e) { string[] nazwy = { "Jabłko", "Banan", "Gruszka", "Śliwka", "Truskawka", "Winogrono" }; string[] kolory = { "Tomato", "Gold", "YellowGreen", "MediumPurple", "HotPink", "MediumSeaGreen" }; for (int i = 0; i < nazwy.Length; i++) { // Kafelek produktu Border kafelek = new Border(); kafelek.Margin = new Thickness(5); kafelek.CornerRadius = new CornerRadius(8); kafelek.Background = (Brush)new BrushConverter().ConvertFromString(kolory[i]); // Treść kafelka StackPanel zawartosc = new StackPanel(); zawartosc.VerticalAlignment = VerticalAlignment.Center; TextBlock emoji = new TextBlock(); emoji.FontSize = 40; emoji.HorizontalAlignment = HorizontalAlignment.Center; emoji.Text = "🍎"; TextBlock nazwa = new TextBlock(); nazwa.Text = nazwy[i]; nazwa.FontSize = 14; nazwa.FontWeight = FontWeights.Bold; nazwa.Foreground = Brushes.White; nazwa.HorizontalAlignment = HorizontalAlignment.Center; nazwa.Margin = new Thickness(0, 8, 0, 0); zawartosc.Children.Add(emoji); zawartosc.Children.Add(nazwa); kafelek.Child = zawartosc; panelGalerii.Children.Add(kafelek); } }
Porównanie wszystkich layoutów WPF
Teraz znasz już wszystkie cztery podstawowe layouty. Oto kompletne podsumowanie:
| Layout | Jak układa elementy | Najlepszy do | Ograniczenia |
|---|---|---|---|
| Grid | Wiersze i kolumny (jak tabela) | Formularze, główny układ okna, precyzyjne rozmieszczenie | Więcej kodu do napisania |
| StackPanel | Jeden za drugim (pionowo lub poziomo) | Listy elementów, paski przycisków, menu boczne | Brak obsługi *, brak zawijania |
| WrapPanel | Jeden za drugim z zawijaniem do nowego wiersza | Galerie, tagi, siatki kafelków | Brak precyzji w rozmieszczeniu |
| DockPanel | Przyklejone do krawędzi, środek wolny | Szkielet okna (menu + sidebar + treść + status) | Kolejność elementów ma znaczenie |
| Canvas | Absolutne pozycje XY | Gry, rysowanie, animacje | Nie skaluje się z oknem |
Częste błędy
❌ Błąd 1: WrapPanel bez ScrollViewer przy wielu elementach
❌ Elementy uciekają poza okno
<WrapPanel>
<!-- 100 kafelków...
te na dole niewidoczne,
brak scrolla -->
</WrapPanel>
✅ ScrollViewer wokół WrapPanel
<ScrollViewer>
<WrapPanel>
<!-- 100 kafelków –
można scrollować -->
</WrapPanel>
</ScrollViewer>
❌ Błąd 2: Zła kolejność w DockPanel
❌ Sidebar zajmuje całą wysokość
<DockPanel>
<!-- Left przed Top/Bottom:
sidebar jest od góry do dołu,
menu i status na nim się NIE mieszczą -->
<Border DockPanel.Dock="Left" />
<Border DockPanel.Dock="Top" />
<Border DockPanel.Dock="Bottom" />
<Border />
</DockPanel>
✅ Top i Bottom przed Left
<DockPanel> <Border DockPanel.Dock="Top" /> <Border DockPanel.Dock="Bottom" /> <Border DockPanel.Dock="Left" /> <Border /> </DockPanel>
❌ Błąd 3: Brak Height/Width na elementach WrapPanel
❌ Elementy mają rozmiar 0 lub nieprzewidywalny
<WrapPanel>
<Border Background="Red" />
<!-- Border bez Width i Height
ma rozmiar 0 – niewidoczny! -->
</WrapPanel>
✅ Jawny rozmiar lub ItemWidth/ItemHeight
<WrapPanel ItemWidth="100" ItemHeight="80">
<Border Background="Red" />
<!-- ItemWidth/ItemHeight
narzuca rozmiar wszystkim -->
</WrapPanel>
Zadania do wykonania
Stwórz okno z WrapPanel zawierającym 12 kolorowych
kwadratów (Border) o rozmiarze 80×80px w różnych kolorach.
Użyj ItemWidth="90" i ItemHeight="90" oraz
owiń WrapPanel w ScrollViewer.
Sprawdź jak zachowuje się układ przy zmniejszaniu szerokości okna.
Zbuduj layout okna używając DockPanel:
- Na górze: niebieski pasek z napisem „Moja Aplikacja” (wysokość 45px)
- Na dole: szary pasek stanu z TextBlock „Gotowy” (wysokość 25px)
- Po lewej: ciemny panel boczny z trzema przyciskami (szerokość 150px)
- Środek: duże białe pole TextBox
Pamiętaj o poprawnej kolejności elementów!
Stwórz galerię kart produktów używając WrapPanel.
W C# dynamicznie utwórz 8 kart produktów (wymyśl nazwy i ceny).
Każda karta to Border (zaokrąglone rogi, cień lub obramowanie)
z StackPanel w środku: nazwa produktu (pogrubiona), cena (zielona),
przycisk „Kup”. Po kliknięciu „Kup” w TextBlock obok panelu
pojawia się nazwa kupionego produktu.
Zbuduj kompletny notatnik używając DockPanel jako głównego layoutu:
- Menu z opcjami: Nowy (czyści pole), Zapisz (wyświetla MessageBox z ilością znaków), Zamknij
- Pasek narzędzi z trzema przyciskami (Nowy, Zapisz, Zamknij)
- Pasek stanu pokazujący aktualną liczbę znaków w polu tekstowym (aktualizuje się przy każdej zmianie)
- Główne pole TextBox (wieloliniowe, z zawijaniem tekstu)