Przejdź do treści
School IT / Przestrzeń do nauki

StackPanel — wiele elementów w oknie

Okno mieści tylko jeden element. Żeby pokazać nagłówek, kilka napisów i podsumowanie, potrzebujesz panelu — pojemnika, który przyjmuje dowolnie wiele elementów i sam je układa. StackPanel ustawia je jeden pod drugim albo jeden obok drugiego. Nauczysz się też dodawać elementy do panelu z kodu C#, np. wiersze planu lekcji z tablicy.

C# XAML StackPanel Orientation Margin Children 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Wyjaśnisz, dlaczego do okna z kilkoma elementami potrzebny jest panel.
  • Ułożysz elementy pionowo i poziomo właściwością Orientation.
  • Ustawisz odstępy marginesem zapisanym jedną albo czterema liczbami.
  • Przewidzisz, które elementy w StackPanel się rozciągają, a które nie.
  • Zbudujesz prostą tabelkę z paneli zagnieżdżonych w sobie.
  • Utworzysz napisy w konstruktorze i dodasz je do panelu przez Children.Add.
TEORIA

Jedno miejsce w oknie

W lekcji 01 próba wstawienia dwóch napisów do okna kończyła się błędem The property 'Content’ is set more than once. Okno ma jedną zawartość — i tak zostanie. Rozwiązaniem jest wstawienie do okna panelu, a do panelu dowolnej liczby elementów:

MainWindow.xaml
<Window …>
    <StackPanel>                         <!-- jedyna zawartość okna -->
        <TextBlock Text="Pierwszy"/>     <!-- a w panelu — ile chcesz -->
        <TextBlock Text="Drugi"/>
        <TextBlock Text="Trzeci"/>
    </StackPanel>
</Window>

Elementy w panelu to jego dzieci (ang. children). StackPanel ustawia je w jednym rzędzie, w kolejności z XAML.

TEORIA

Pionowo i poziomo

Kierunek układania ustawia Orientation: Vertical (domyślnie) — jeden pod drugim, Horizontal — jeden obok drugiego.

MainWindow.xaml
<StackPanel Orientation="Horizontal">
    <TextBlock Text="Pon" Width="60"/>
    <TextBlock Text="Wt"  Width="60"/>
    <TextBlock Text="Śr"  Width="60"/>
</StackPanel>
Vertical–□✕
Pon
Wt
Śr
Horizontal–□✕
Pon
Wt
Śr

te same trzy napisy w dwóch orientacjach

Width="60" nadaje napisom stałą szerokość — dzięki temu w poziomym panelu stoją w równych kolumnach.

TEORIA

Odstępy: Margin

Bez odstępów elementy w panelu stykają się ze sobą, a panel dotyka krawędzi okna. Margin to odstęp na zewnątrz elementu. Ma dwa podstawowe zapisy:

ZapisZnaczenieTypowe użycie
Margin="15"15 z każdej stronyodsunięcie całego panelu od krawędzi okna
Margin="0,0,0,8"lewo, góra, prawo, dółodstęp tylko pod napisem — między kolejnymi elementami

Odstęp z jednej strony

W pionowym panelu dawaj elementom odstęp tylko od dołu (0,0,0,8), w poziomym — tylko z prawej (0,0,8,0). Wtedy łatwo panować nad układem. Więcej zapisów marginesu i odstęp wewnętrzny Padding poznasz w lekcji 05.

TEORIA

Jak StackPanel mierzy elementy

OrientacjaW kierunku układaniaW poprzek
Verticalwysokość: tyle, ile element potrzebujeszerokość: cała szerokość panelu
Horizontalszerokość: tyle, ile element potrzebujewysokość: cała wysokość panelu

Wynikają z tego dwie rzeczy, o których trzeba pamiętać:

  • Nic się nie zawija ani nie przewija. Gdy elementów jest więcej, niż mieści okno, wystają poza jego krawędź i są ucinane. Przewijanie poznasz w lekcji 11.
  • Nic nie „wypełnia reszty”. Element, który ma rosnąć razem z oknem, potrzebuje innego panelu (lekcja 09 i 13). StackPanel jest do elementów w naturalnym rozmiarze: formularzy, list, menu.
TEORIA

Panele w panelach

Panel jest elementem jak każdy inny, więc może stać w innym panelu. Pionowy panel z poziomymi w środku daje prostą tabelkę:

MainWindow.xaml
<StackPanel Margin="15">
    <TextBlock Text="Oceny" FontSize="18" FontWeight="Bold" Margin="0,0,0,8"/>

    <StackPanel Orientation="Horizontal" Margin="0,0,0,4">
        <TextBlock Text="Matematyka" Width="120"/>
        <TextBlock Text="5, 4, 5"/>
    </StackPanel>

    <StackPanel Orientation="Horizontal" Margin="0,0,0,4">
        <TextBlock Text="Programowanie" Width="120"/>
        <TextBlock Text="6, 5"/>
    </StackPanel>
</StackPanel>
Oceny–□✕
Oceny
Matematyka5, 4, 5
Programowanie6, 5

stała szerokość pierwszej kolumny wyrównuje drugą

TEORIA

Elementy dodawane z kodu

Gdy dane są w tablicy, nie musisz przepisywać każdego wiersza do XAML. Daj panelowi nazwę, a w konstruktorze — pod InitializeComponent() — twórz napisy i dodawaj je do kolekcji Children panelu:

MainWindow.xaml.cs
// w XAML: <StackPanel x:Name="Lista"/>

public MainWindow()
{
    InitializeComponent();

    string[] dni = { "poniedziałek", "wtorek", "środa" };

    foreach (string dzien in dni)
    {
        TextBlock napis = new TextBlock();   // jak <TextBlock …/> w XAML
        napis.Text = dzien;
        napis.Margin = new Thickness(0, 0, 0, 4);
        Lista.Children.Add(napis);           // dopiero teraz napis jest w oknie
    }
}
XAMLC#
<TextBlock … />TextBlock napis = new TextBlock();
Margin="0,0,0,4"napis.Margin = new Thickness(0, 0, 0, 4);
Orientation="Horizontal"panel.Orientation = Orientation.Horizontal;
FontWeight="Bold"napis.FontWeight = FontWeights.Bold;
element wpisany w środek panelupanel.Children.Add(element);

Inne przydatne operacje: Children.Count — liczba elementów, Children.Insert(0, element) — wstawienie na początek, Children.Clear() — usunięcie wszystkich. Klasy TextBlock, StackPanel i Orientation są w przestrzeni nazw System.Windows.Controls — dopisz using System.Windows.Controls; na górze pliku.

PRZYKŁAD

Plan lekcji na dziś

Nagłówek, dzisiejsza data i nagłówki kolumn są w XAML. Wiersze planu tworzy konstruktor z dwóch tablic: godzin i przedmiotów. Na dole podsumowanie liczy wiersze w panelu.

Plan lekcji–□✕
Plan lekcji — 3 TP
czwartek, 8 października
GodzinaPrzedmiot
8:00–8:45Matematyka
8:55–9:40Programowanie
9:50–10:35Programowanie
10:55–11:40Język polski
11:50–12:35Wychowanie fizyczne
12:45–13:30Język angielski
Lekcji: 6, koniec o 13:30

okno po uruchomieniu

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja03.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Plan lekcji" Height="420" Width="360"
        WindowStartupLocation="CenterScreen">

    <StackPanel Margin="15">
        <TextBlock Text="Plan lekcji — 3 TP" FontSize="22" FontWeight="Bold"/>
        <TextBlock x:Name="Dzien" Foreground="Gray" Margin="0,0,0,12"/>

        <!-- Nagłówek tabeli: dwa napisy obok siebie -->
        <StackPanel Orientation="Horizontal" Margin="0,0,0,6">
            <TextBlock Text="Godzina" Width="110" FontWeight="Bold"/>
            <TextBlock Text="Przedmiot" FontWeight="Bold"/>
        </StackPanel>

        <!-- Pusty panel — wiersze doda konstruktor -->
        <StackPanel x:Name="Plan"/>

        <TextBlock x:Name="Podsumowanie" Margin="0,12,0,0"/>
    </StackPanel>
</Window>
MainWindow.xaml.cs — pełny plik
using System.Windows;
using System.Windows.Controls;

namespace Lekcja03;

public partial class MainWindow : Window
{
    private string[] godziny = { "8:00–8:45", "8:55–9:40", "9:50–10:35", "10:55–11:40", "11:50–12:35", "12:45–13:30" };
    private string[] przedmioty = { "Matematyka", "Programowanie", "Programowanie", "Język polski", "Wychowanie fizyczne", "Język angielski" };

    public MainWindow()
    {
        InitializeComponent();

        Dzien.Text = DateTime.Now.ToString("dddd, d MMMM");

        for (int i = 0; i < godziny.Length; i++)
        {
            // Jeden wiersz = poziomy StackPanel z dwoma napisami
            StackPanel wiersz = new StackPanel();
            wiersz.Orientation = Orientation.Horizontal;
            wiersz.Margin = new Thickness(0, 0, 0, 4);

            TextBlock godzina = new TextBlock();
            godzina.Text = godziny[i];
            godzina.Width = 110;

            TextBlock przedmiot = new TextBlock();
            przedmiot.Text = przedmioty[i];

            wiersz.Children.Add(godzina);
            wiersz.Children.Add(przedmiot);
            Plan.Children.Add(wiersz);
        }

        Podsumowanie.Text = $"Lekcji: {Plan.Children.Count}, koniec o {godziny[godziny.Length - 1].Split('–')[1]}";
    }
}

Co dzieje się po kolei

  • Jedyną zawartością okna jest pionowy StackPanel z marginesem 15 — cała treść jest odsunięta od krawędzi.
  • Nagłówek kolumn to poziomy panel. Napis „Godzina” ma Width="110", tak samo jak godziny w wierszach — dzięki temu przedmioty zaczynają się w jednej linii.
  • <StackPanel x:Name="Plan"/> jest w XAML pusty. Stoi między nagłówkiem a podsumowaniem, więc wiersze dodane z kodu pojawią się właśnie tam.
  • Tablice godziny i przedmioty są polami klasy. Element o indeksie i w jednej odpowiada elementowi o tym samym indeksie w drugiej.
  • Pętla for idzie po indeksach. W każdym obiegu powstaje nowy poziomy panel i dwa nowe napisy — każdy wiersz to osobne obiekty.
  • wiersz.Children.Add wkłada napisy do wiersza, a Plan.Children.Add(wiersz) — wiersz do planu. Kolejność dodawania to kolejność na ekranie.
  • Podsumowanie liczy wiersze przez Plan.Children.Count, a godzinę końca bierze z ostatniej godziny po znaku „–” (Split('–')[1]).
  • DateTime.Now.ToString("dddd, d MMMM") daje polską nazwę dnia i miesiąca, np. „czwartek, 8 października”.

Sprawdź trzy rzeczy

  1. Dopisz do tablic jeszcze dwie lekcje (np. 13:40–14:25). Wiersze i podsumowanie zmienią się same — XAML zostaje bez zmian.
  2. Usuń godzina.Width = 110;. Przedmioty przestaną tworzyć równą kolumnę.
  3. Zmniejsz wysokość okna do 250. Ostatnie wiersze i podsumowanie zostaną ucięte — StackPanel nie przewija.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementZnaczenieUwagi
StackPanelpanel układający dzieci w rzędzieNie zawija, nie przewija, nie rozciąga w kierunku układania.
OrientationVertical / HorizontalDomyślnie Vertical. W C#: Orientation.Horizontal.
Marginodstęp na zewnątrzJedna liczba albo cztery: lewo, góra, prawo, dół. W C#: new Thickness(…).
Widthstała szerokośćWyrównuje kolumny w poziomych panelach.
Children.Add(e)dodaje element na końcuElement bez Add nie pojawi się w oknie.
Children.Insert(i, e)wstawia pod indeks0 — na początek.
Children.Countliczba elementówTyp int.
Children.Clear()usuwa wszystkie elementyPanel zostaje, znika zawartość.
FontWeights.Boldpogrubienie w C#Klasa FontWeights — z „s” na końcu.
DateTime.Now.ToString("dddd")nazwa dnia tygodnia"d MMMM" — dzień i miesiąc słownie.
CZĘSTE BŁĘDY

Zanim utkniesz

Zapis lub sytuacjaProblem
dwa panele bezpośrednio w <Window>Okno nadal ma jedną zawartość. Włóż oba panele do jednego, zewnętrznego.
napis utworzony przez new, ale bez Children.AddObiekt istnieje tylko w pamięci — w oknie go nie ma.
jeden obiekt dodany dwa razy do ChildrenBłąd w czasie działania: element może mieć tylko jednego rodzica. W pętli twórz nowy obiekt w każdym obiegu.
pętla dodająca napisy nad InitializeComponent()NullReferenceException — panelu jeszcze nie ma.
wiersz.Orientation = "Horizontal";CS0029 — w C# to wartość wyliczenia: Orientation.Horizontal.
napis.Margin = "0,0,0,4";CS0029 — margines w C# to new Thickness(0, 0, 0, 4).
Margin="10,5,10"Trzy liczby to błąd. Dozwolone: jedna, dwie albo cztery.
brak using System.Windows.Controls;CS0246 — kompilator nie zna TextBlock ani StackPanel w C#.
długa lista w StackPanelElementy poza oknem są ucięte, pasek przewijania się nie pojawi.
ZADANIA

Zadania

Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Używaj tylko napisów i paneli — przyciski poznasz w następnej lekcji. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Menu szkolnej stołówki★☆☆

Zbuduj w XAML okno „Stołówka” z nagłówkiem „Menu na dziś” i pięcioma daniami w wierszach: nazwa dania po lewej, cena po prawej (np. „Pierogi ruskie … 12 zł”). Ceny mają tworzyć równą kolumnę, nagłówek ma być większy i pogrubiony, a między nagłówkiem a listą ma być wyraźny odstęp. Na dole, szarym kolorem: „Zupa dnia w cenie drugiego dania”.

Stołówka–□✕
Menu na dziś
Pierogi ruskie12 zł
Kotlet schabowy16 zł
……
Zupa dnia w cenie drugiego dania
pokaż wskazówkę
  • Pionowy panel na całość, a w nim poziome panele — po jednym na danie.
  • Równą kolumnę cen da stała szerokość napisu z nazwą dania.
ZAD 2Tabliczka mnożenia★☆☆

Okno „Tabliczka mnożenia przez 7”. W konstruktorze pętlą utwórz dziesięć napisów w formacie 7 × 3 = 21 i dodaj je do panelu. Liczbę 7 zapisz w jednej stałej — po zmianie stałej na 9 cała tabliczka i tytuł okna mają się zmienić same. Wynik równy 49 (albo kwadrat wybranej liczby) ma być pogrubiony.

pokaż wskazówkę
  • Pogrubienie z kodu: FontWeight i FontWeights.Bold.
  • Kwadrat liczby wypada w obiegu, w którym mnożnik jest równy liczbie.
ZAD 3Rozkład jazdy autobusu★★☆

Okno przystanku „ZSMI Lębork”. W kodzie zapisz tablicę godzin odjazdów linii 3 (co najmniej 8 kursów, np. "7:05") i tablicę kierunków. Konstruktor buduje tabelkę: godzina, numer linii, kierunek — w trzech równych kolumnach, z pogrubionym nagłówkiem kolumn. Pod tabelką napis „Kursów dziś: 8”. Kursy po 15:00 oznacz dopiskiem „(popołudnie)”.

pokaż wskazówkę
  • Każdy wiersz to poziomy panel z trzema napisami o stałych szerokościach.
  • Godzinę jako liczbę wyciągniesz z tekstu przez Split(':') i int.Parse — godziny w tablicy są zawsze poprawne.
ZAD 4Ten tydzień★★☆

Okno pokazuje siedem kolejnych dni, zaczynając od dziś, obok siebie: w każdej kolumnie skrót dnia („czw”) i numer dnia miesiąca pod spodem. Dzisiejszy dzień jest pogrubiony i ma większą czcionkę. Kolumny mają równą szerokość i odstęp między sobą. Całość tworzy konstruktor w pętli.

Ten tydzień–□✕
czw
8
pt
9
sob
10
niedz
11
pon
12
wt
13
śr
14
pokaż wskazówkę
  • Zewnętrzny panel poziomy, a w nim siedem pionowych — każdy z dwoma napisami.
  • DateTime.Today.AddDays(i) daje datę za i dni; skrót dnia — format "ddd", numer dnia — właściwość Day.
ZAD 5Paragon★★★

Okno sklepiku szkolnego wyświetla paragon. W kodzie trzy tablice: nazwy produktów, ilości i ceny jednostkowe (co najmniej 5 pozycji). Każdy wiersz paragonu: nazwa, „ilość × cena”, wartość pozycji — w trzech kolumnach, kwoty z dwoma miejscami po przecinku. Pod pozycjami pozioma linia z myślników, a niżej pogrubione „RAZEM: 23,40 zł” policzone w pętli. Jeżeli suma przekracza 20 zł, dopisz linię „Rabat 5%: −1,17 zł” i „DO ZAPŁATY: 22,23 zł”. W komentarzu zapisz, dlaczego kwoty trzymasz w typie decimal.

pokaż wskazówkę
  • Najpierw zbuduj wiersze i licz sumę w tej samej pętli, potem dodaj wiersze podsumowania.
  • Format kwoty: {kwota:F2}. Linia z myślników to zwykły napis, np. new string('-', 40).
ZAD 6Ranking turnieju★★★

Okno wyników szkolnego turnieju e-sportowego. W kodzie tablica nicków i tablica punktów (8 graczy, w przypadkowej kolejności). Konstruktor wyświetla ranking od najlepszego: miejsce, nick, punkty. Trzej pierwsi są pogrubieni, a przy pierwszym dopisz „— zwycięzca”. Na dole: „Średnia punktów: 412,5”. Tablic nie sortuj ręcznie w kodzie źródłowym — kolejność ma ustalić program.

pokaż wskazówkę
  • Array.Sort(punkty, nicki) sortuje obie tablice naraz według punktów — rosnąco. Ranking zbudujesz pętlą od końca.
  • Numer miejsca to licznik zwiększany w każdym obiegu, niezależnie od indeksu tablicy.
PODSUMOWANIE

Co trzeba zapamiętać

  • Okno ma jedną zawartość — wstaw do niego panel, a do panelu dowolnie wiele elementów.
  • StackPanel układa dzieci w rzędzie: Vertical (domyślnie) albo Horizontal.
  • W kierunku układania element dostaje tylko tyle miejsca, ile potrzebuje; nic się nie zawija ani nie przewija.
  • Margin — odstęp na zewnątrz: jedna liczba albo cztery (lewo, góra, prawo, dół).
  • Panele można zagnieżdżać; stała szerokość napisów wyrównuje kolumny.
  • Z kodu: new TextBlock(), ustawienie właściwości i Children.Add — zawsze pod InitializeComponent().

Dokumentacja: Microsoft Learn — klasa StackPanel.

Postęp zapisuje się w tej przeglądarce.