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.
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
StackPanelsię 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.
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:
<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.
Pionowo i poziomo
Kierunek układania ustawia Orientation: Vertical (domyślnie) — jeden pod drugim, Horizontal — jeden obok drugiego.
<StackPanel Orientation="Horizontal">
<TextBlock Text="Pon" Width="60"/>
<TextBlock Text="Wt" Width="60"/>
<TextBlock Text="Śr" Width="60"/>
</StackPanel>
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.
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:
| Zapis | Znaczenie | Typowe użycie |
|---|---|---|
Margin="15" | 15 z każdej strony | odsunię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.
Jak StackPanel mierzy elementy
| Orientacja | W kierunku układania | W poprzek |
|---|---|---|
Vertical | wysokość: tyle, ile element potrzebuje | szerokość: cała szerokość panelu |
Horizontal | szerokość: tyle, ile element potrzebuje | wysokość: 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).
StackPaneljest do elementów w naturalnym rozmiarze: formularzy, list, menu.
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ę:
<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>
stała szerokość pierwszej kolumny wyrównuje drugą
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:
// 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
}
}
| XAML | C# |
|---|---|
<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 panelu | panel.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.
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.
okno po uruchomieniu
<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>
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
StackPanelz 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
godzinyiprzedmiotysą polami klasy. Element o indeksieiw jednej odpowiada elementowi o tym samym indeksie w drugiej. - Pętla
foridzie po indeksach. W każdym obiegu powstaje nowy poziomy panel i dwa nowe napisy — każdy wiersz to osobne obiekty. wiersz.Children.Addwkłada napisy do wiersza, aPlan.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
- Dopisz do tablic jeszcze dwie lekcje (np. 13:40–14:25). Wiersze i podsumowanie zmienią się same — XAML zostaje bez zmian.
- Usuń
godzina.Width = 110;. Przedmioty przestaną tworzyć równą kolumnę. - Zmniejsz wysokość okna do 250. Ostatnie wiersze i podsumowanie zostaną ucięte —
StackPanelnie przewija.
Zestawienie elementów
| Element | Znaczenie | Uwagi |
|---|---|---|
StackPanel | panel układający dzieci w rzędzie | Nie zawija, nie przewija, nie rozciąga w kierunku układania. |
Orientation | Vertical / Horizontal | Domyślnie Vertical. W C#: Orientation.Horizontal. |
Margin | odstęp na zewnątrz | Jedna liczba albo cztery: lewo, góra, prawo, dół. W C#: new Thickness(…). |
Width | stała szerokość | Wyrównuje kolumny w poziomych panelach. |
Children.Add(e) | dodaje element na końcu | Element bez Add nie pojawi się w oknie. |
Children.Insert(i, e) | wstawia pod indeks | 0 — na początek. |
Children.Count | liczba elementów | Typ int. |
Children.Clear() | usuwa wszystkie elementy | Panel zostaje, znika zawartość. |
FontWeights.Bold | pogrubienie w C# | Klasa FontWeights — z „s” na końcu. |
DateTime.Now.ToString("dddd") | nazwa dnia tygodnia | "d MMMM" — dzień i miesiąc słownie. |
Zanim utkniesz
| Zapis lub sytuacja | Problem |
|---|---|
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.Add | Obiekt istnieje tylko w pamięci — w oknie go nie ma. |
jeden obiekt dodany dwa razy do Children | Błą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 StackPanel | Elementy poza oknem są ucięte, pasek przewijania się nie pojawi. |
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.
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”.
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.
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:
FontWeightiFontWeights.Bold. - Kwadrat liczby wypada w obiegu, w którym mnożnik jest równy liczbie.
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(':')iint.Parse— godziny w tablicy są zawsze poprawne.
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.
8
9
10
11
12
13
14
pokaż wskazówkę
- Zewnętrzny panel poziomy, a w nim siedem pionowych — każdy z dwoma napisami.
DateTime.Today.AddDays(i)daje datę zaidni; skrót dnia — format"ddd", numer dnia — właściwośćDay.
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).
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.
Co trzeba zapamiętać
- Okno ma jedną zawartość — wstaw do niego panel, a do panelu dowolnie wiele elementów.
StackPanelukłada dzieci w rzędzie:Vertical(domyślnie) alboHorizontal.- 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 iChildren.Add— zawsze podInitializeComponent().
Dokumentacja: Microsoft Learn — klasa StackPanel.