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 DockPanel Dock LastChildFill
1

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).

Analogia

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.

Podstawowy WrapPanel
<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.

OrientationElementy układaneZawijanie
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 pionowy – zawijanie do kolumn
<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.

ItemWidth – równa siatka kafelków
<!-- 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 – co to jest?

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:

ZastosowanieDlaczego WrapPanel?
Galeria miniatur zdjęćMiniatury zawijają się do nowych wierszy gdy okno jest wąskie
Lista tagów / znacznikówTagi mają różne długości, zawijają się naturalnie
Siatka przycisków (kafelki)Jednolite kafelki z ItemWidth/ItemHeight
Pasek narzędzi z ikonamiIkony zawijają się gdy okno jest za wąskie
2

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.

Analogia

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
TopGórnej krawędzi (zajmuje całą szerokość)
BottomDolnej krawędzi (zajmuje całą szerokość)
LeftLewej krawędzi (zajmuje całą pozostałą wysokość)
RightPrawej krawędzi (zajmuje całą pozostałą wysokość)
DockPanel – podstawowy przykład
<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 – domyślnie True
<!-- 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.

Kolejność elementów zmienia wynik
<!-- 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) -->
Zasada: górę i dół przed lewą i prawą

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.

3

Typowe zastosowania

Kompletny layout okna z DockPanel

DockPanel jest świetny do budowania szkieletu całego okna aplikacji. Oto kompletny przykład prostego notatnika:

Szkielet notatnika – DockPanel
<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:

Dynamiczna galeria produktów – C#
<!-- 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);
    }
}
4

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
Jak wybierać layout w praktyce
Cały szkielet okna?
DockPanel lub Grid
Formularz z polami?
Grid (Auto + *)
Przyciski w rzędzie?
StackPanel Horizontal
Galeria / kafelki?
WrapPanel + ScrollViewer
Gra / rysowanie?
Canvas
5

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>
6

Zadania do wykonania

Zadanie 1 łatwe

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.

Zadanie 2 łatwe

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!

Zadanie 3 średnie

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.

Zadanie 4 średnie

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)