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

Label i formularz w Grid

Formularz to najczęstsze okno na egzaminie: kolumna opisów po lewej, kolumna pól po prawej i przyciski na dole. W tej lekcji budujesz go porządnie — z kontrolką Label, która przenosi kursor do pola po naciśnięciu Alt + litera, z równymi kolumnami, sensowną kolejnością klawisza Tab i oknem, którego nie da się zmniejszyć tak, że formularz się rozsypie.

XAML C# Label klawisze dostępu TabIndex SizeToContent 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Wyjaśnisz różnicę między Label a TextBlock i dobierzesz właściwą kontrolkę.
  • Dodasz klawisz dostępu (Alt + litera) do etykiety i do przycisku.
  • Zbudujesz formularz w Grid z kolumnami Auto i *, w którym krótkie pola nie rozciągają się na całą szerokość.
  • Ustawisz kolejność przechodzenia klawiszem Tab i wyłączysz z niej pola tylko do odczytu.
  • Dopasujesz rozmiar okna do formularza (SizeToContent, MinWidth, MinHeight).
TEORIA

Label czy TextBlock?

Obie kontrolki wyświetlają tekst. Label jest jednak przeznaczona do opisywania pól formularza:

LabelTextBlock
TreśćContentText
Wewnętrzny odstępPadding 5 — od razu wyrównuje się z polem tekstowymbrak
Klawisz dostęputak: _ przed literą i Targetnie
Zawijanie tekstunietak: TextWrapping
Do czegoopis pola: „Imię:”, „Klasa:”dłuższy tekst, wyniki, komunikaty
MainWindow.xaml
<StackPanel Margin="10">
    <Label Content="Imię:"/>
    <TextBox x:Name="Imie"/>
    <TextBlock x:Name="Wynik" TextWrapping="Wrap" Margin="0,10,0,0"/>
</StackPanel>

Z kodu tekst etykiety zmieniasz przez Content, a nie Text: Opis.Content = "Nazwisko:";.

TEORIA

Klawisze dostępu

Znak podkreślenia przed literą robi z niej klawisz dostępu. Litera nie wyświetla się z podkreślnikiem — gdy użytkownik przytrzyma Alt, pojawi się pod nią kreska. Alt + ta litera działa tak:

MainWindow.xaml
<!-- Alt+N przenosi kursor do pola Nazwisko -->
<Label Content="_Nazwisko:" Target="{Binding ElementName=Nazwisko}"/>
<TextBox x:Name="Nazwisko"/>

<!-- Alt+Z „klika” przycisk -->
<Button Content="_Zapisz" Click="Zapisz_Click"/>
GdzieCo robi Alt + litera
Label z TargetPrzenosi kursor do kontrolki wskazanej w Target.
ButtonWywołuje Click, tak jakby przycisk kliknięto myszą.
prawdziwy podkreślnik w tekściePiszesz dwa: Content="numer__karty" wyświetli „numer_karty”.

Zapis {Binding ElementName=…}

Na razie traktuj Target="{Binding ElementName=Nazwisko}" jako stały wzór: w miejsce Nazwisko wpisujesz x:Name pola. Co dokładnie znaczą nawiasy klamrowe i słowo Binding, dowiesz się w lekcji 41.

Litery w jednym oknie nie powinny się powtarzać. Przy dwóch etykietach z tą samą literą Alt + litera przechodzi kolejno między nimi, co myli użytkownika.

TEORIA

Układ formularza w Grid

W lekcji 07 wiersze formularza robiłeś z poziomych StackPanel, a etykiety dostawały stałą szerokość 180. Grid załatwia to sam: kolumna etykiet Auto ma szerokość najdłuższej etykiety, a kolumna pól * — resztę okna.

MainWindow.xaml
<Grid Margin="10">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <Label   Grid.Row="0" Content="_Imię:" Target="{Binding ElementName=Imie}"/>
    <TextBox Grid.Row="0" Grid.Column="1" x:Name="Imie" Margin="0,4"/>

    <Label   Grid.Row="1" Content="_Klasa:" Target="{Binding ElementName=Klasa}"/>
    <TextBox Grid.Row="1" Grid.Column="1" x:Name="Klasa" Margin="0,4"
             Width="60" HorizontalAlignment="Left"/>

    <Label   Grid.Row="2" Content="Numer w _dzienniku:" Target="{Binding ElementName=Numer}"/>
    <TextBox Grid.Row="2" Grid.Column="1" x:Name="Numer" Margin="0,4"
             Width="40" HorizontalAlignment="Left"/>
</Grid>
Uczeń–□✕
Imię:
Ola
Klasa:
3TI
Numer w dzienniku:
17

krótkie pola mają własną szerokość i HorizontalAlignment="Left"

Trzy zasady dobrego formularza:

  • Wiersze z polami jednowierszowymi — Auto. Wiersz z polem wielowierszowym — *.
  • Pole na krótką wartość (klasa, wiek, kod) dostaje Width i HorizontalAlignment="Left" — szerokość pola podpowiada, ile trzeba wpisać.
  • Przyciski na dole stoją w poziomym StackPanel z HorizontalAlignment="Right"; najważniejszy jest ostatni i ma IsDefault="True".
TEORIA

Kolejność klawisza Tab

Tab przenosi kursor do następnej kontrolki, Shift + Tab — do poprzedniej. Domyślna kolejność to kolejność w pliku XAML, więc zwykle wystarczy pisać kontrolki od góry do dołu. Gdy trzeba ją zmienić, ustawiasz TabIndex (mniejsza liczba = wcześniej). Kontrolkę, do której Tab ma nie wchodzić, oznaczasz IsTabStop="False":

MainWindow.xaml
<TextBox x:Name="Kwota" TabIndex="0"/>
<TextBox x:Name="Waluta" TabIndex="1"/>
<!-- wynik: tylko do odczytu i pomijany przez Tab -->
<TextBox x:Name="Wynik" IsReadOnly="True" IsTabStop="False"/>

Label sama nigdy nie dostaje fokusu — Tab ją przeskakuje.

TEORIA

Rozmiar okna z formularzem

Ustawienie oknaEfekt
SizeToContent="Height"Okno samo dobiera wysokość do zawartości. Atrybut Height jest wtedy pomijany. Inne wartości: Width, WidthAndHeight, Manual (domyślna).
MinWidth="400" MinHeight="300"Użytkownik nie zmniejszy okna poniżej tych wymiarów — formularz się nie rozsypie.
ResizeMode="CanMinimize"Okna nie da się w ogóle zmieniać (z lekcji 01) — dobre dla małych, stałych formularzy.

SizeToContent="Height" nie pasuje do okna z wierszem *: okno bierze wysokość z zawartości, więc wiersz z gwiazdką zachowuje się jak Auto i pole wielowierszowe kurczy się do jednej linii. Używaj go w formularzach złożonych z samych wierszy Auto.

PRZYKŁAD

Zgłoszenie usterki w pracowni

Uczeń albo nauczyciel zgłasza usterkę komputera: wpisuje imię i nazwisko, salę, numer stanowiska i opis. Formularz sprawdza pola od góry do dołu, pokazuje podsumowanie i liczy wysłane zgłoszenia. Po wysłaniu zgłaszający i sala zostają w polach, bo zwykle zgłasza się kilka usterek z jednej sali.

Zgłoszenie usterki–□✕
Imię i nazwisko:
Jan Nowak
Sala:
204
Numer stanowiska:
7
(1–16)
Opis usterki:
Monitor nie włącza się, dioda miga na pomarańczowo.
Wysłane: 2
Wyczyść
Wyślij

formularz przed wysłaniem trzeciego zgłoszenia

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja10.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Zgłoszenie usterki" Height="400" Width="480"
        MinWidth="400" MinHeight="340"
        WindowStartupLocation="CenterScreen">

    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>   <!-- etykiety: szerokość najdłuższej -->
            <ColumnDefinition Width="*"/>      <!-- pola: cała reszta -->
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>        <!-- opis rośnie razem z oknem -->
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Content="_Imię i nazwisko:"
               Target="{Binding ElementName=Osoba}"/>
        <TextBox x:Name="Osoba" Grid.Row="0" Grid.Column="1"
                 Margin="0,4" MaxLength="40"/>

        <Label Grid.Row="1" Grid.Column="0" Content="_Sala:"
               Target="{Binding ElementName=Sala}"/>
        <TextBox x:Name="Sala" Grid.Row="1" Grid.Column="1"
                 Margin="0,4" Width="80" HorizontalAlignment="Left" MaxLength="5"/>

        <Label Grid.Row="2" Grid.Column="0" Content="_Numer stanowiska:"
               Target="{Binding ElementName=Stanowisko}"/>
        <StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal">
            <TextBox x:Name="Stanowisko" Margin="0,4" Width="50" MaxLength="2"/>
            <TextBlock Text="(1–16)" Margin="8,0,0,0" VerticalAlignment="Center"
                       Foreground="Gray"/>
        </StackPanel>

        <Label Grid.Row="3" Grid.Column="0" Content="_Opis usterki:"
               Target="{Binding ElementName=Opis}" VerticalAlignment="Top"/>
        <TextBox x:Name="Opis" Grid.Row="3" Grid.Column="1" Margin="0,4"
                 AcceptsReturn="True" TextWrapping="Wrap"
                 VerticalScrollBarVisibility="Auto" MaxLength="300"/>

        <!-- dolny wiersz: licznik po lewej, przyciski po prawej -->
        <TextBlock x:Name="Licznik" Grid.Row="4" Grid.Column="0"
                   Text="Wysłane: 0" VerticalAlignment="Center" Margin="5,10,0,0"/>
        <StackPanel Grid.Row="4" Grid.Column="1" Orientation="Horizontal"
                    HorizontalAlignment="Right" Margin="0,10,0,0">
            <Button Content="Wy_czyść" Width="90" Height="28" Margin="0,0,8,0"
                    Click="Wyczysc_Click"/>
            <Button Content="Wyślij" Width="90" Height="28"
                    IsDefault="True" Click="Wyslij_Click"/>
        </StackPanel>
    </Grid>
</Window>
MainWindow.xaml.cs — pełny plik
using System.Windows;
using System.Windows.Controls;

namespace Lekcja10;

public partial class MainWindow : Window
{
    private int wyslane = 0;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void Wyslij_Click(object sender, RoutedEventArgs e)
    {
        // 1. Sprawdzanie pól od góry do dołu
        if (string.IsNullOrWhiteSpace(Osoba.Text))
        {
            PokazBlad("Wpisz imię i nazwisko.", Osoba);
            return;
        }

        if (string.IsNullOrWhiteSpace(Sala.Text))
        {
            PokazBlad("Wpisz numer sali.", Sala);
            return;
        }

        if (!int.TryParse(Stanowisko.Text, out int stanowisko) || stanowisko < 1 || stanowisko > 16)
        {
            PokazBlad("Numer stanowiska musi być liczbą od 1 do 16.", Stanowisko);
            return;
        }

        if (Opis.Text.Trim().Length < 10)
        {
            PokazBlad("Opisz usterkę dokładniej (co najmniej 10 znaków).", Opis);
            return;
        }

        // 2. Podsumowanie zgłoszenia
        MessageBox.Show($"Zgłaszający: {Osoba.Text.Trim()}\n" +
                        $"Miejsce: sala {Sala.Text.Trim()}, stanowisko {stanowisko}\n" +
                        $"Opis: {Opis.Text.Trim()}",
                        "Zgłoszenie wysłane", MessageBoxButton.OK, MessageBoxImage.Information);

        wyslane++;
        Licznik.Text = $"Wysłane: {wyslane}";

        // 3. Zgłaszający i sala zostają — zwykle zgłasza się kilka usterek naraz
        Stanowisko.Clear();
        Opis.Clear();
        Stanowisko.Focus();
    }

    private void Wyczysc_Click(object sender, RoutedEventArgs e)
    {
        Osoba.Clear();
        Sala.Clear();
        Stanowisko.Clear();
        Opis.Clear();
        Osoba.Focus();
    }

    private void PokazBlad(string komunikat, TextBox pole)
    {
        MessageBox.Show(komunikat, "Błąd w formularzu",
                        MessageBoxButton.OK, MessageBoxImage.Warning);
        pole.Focus();
        pole.SelectAll();
    }
}

Co dzieje się po kolei

  • Siatka ma 2 kolumny (Auto na etykiety, * na pola) i 5 wierszy. Tylko wiersz z opisem ma *, więc po powiększeniu okna rośnie wyłącznie pole opisu.
  • Każda etykieta ma klawisz dostępu: Alt + I, S, N, O przenosi kursor do odpowiedniego pola. Przycisk „Wyczyść” ma Alt + C, bo litera W pojawia się też w „Wyślij”.
  • Etykieta opisu ma VerticalAlignment="Top" — bez tego stałaby na środku wysokiego pola.
  • Pola sali i stanowiska mają własną szerokość i HorizontalAlignment="Left". Obok pola stanowiska podpowiedź „(1–16)” — dlatego pole i podpowiedź stoją razem w poziomym StackPanel w jednej komórce.
  • W dolnym wierszu licznik zajmuje kolumnę etykiet, a przyciski — kolumnę pól, dosunięte do prawej.
  • Wyslij_Click sprawdza pola w tej samej kolejności, w jakiej stoją w oknie. Pierwszy błąd kończy metodę, a PokazBlad przenosi kursor do złego pola.
  • Po wysłaniu czyścimy tylko stanowisko i opis, a kursor trafia do pola stanowiska — kolejne zgłoszenie z tej samej sali to dwa pola do wypełnienia.
  • MinWidth i MinHeight pilnują, by po zmniejszeniu okna przyciski nie nachodziły na pola.

Sprawdź trzy rzeczy

  1. Uruchom program i naciśnij Alt + O, potem Alt + I. Kursor przeskakuje między opisem i polem nazwiska. Przytrzymaj sam Alt — pod literami pojawią się kreski.
  2. Usuń z etykiety sali atrybut Target. Litera S nadal jest podkreślona, ale Alt + S nic nie robi.
  3. Zmień szerokość pierwszej kolumny z Auto na 80. Etykieta „Numer stanowiska:” zostanie ucięta.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTyp / wartościCo robi
Label.ContentobjectTekst etykiety. _ przed literą tworzy klawisz dostępu, __ wyświetla podkreślnik.
Label.TargetkontrolkaDokąd Alt + litera przenosi kursor. Wzór: {Binding ElementName=Nazwa}.
TabIndexintKolejność przechodzenia klawiszem Tab.
IsTabStopboolFalse — Tab pomija kontrolkę.
Window.SizeToContentManual, Width, Height, WidthAndHeightOkno dopasowuje rozmiar do zawartości.
MinWidth, MinHeightdoubleNajmniejszy dozwolony rozmiar okna (albo kontrolki).
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
<Label Text="Imię:"/>Błąd XAML — Label nie ma Text, ma Content.
Opis.Text = "…" dla etykietyCS1061 — z kodu też Content.
Content="_Imię:" bez TargetLitera jest podkreślana, ale Alt + litera nic nie robi.
Target="Imie"Błąd — potrzebny wzór {Binding ElementName=Imie}.
ta sama litera w dwóch etykietachAlt + litera skacze na zmianę między dwoma polami.
wszystkie wiersze *Pola jednowierszowe rozciągają się na wysokość, formularz wygląda dziwnie. Wiersze z polami — Auto.
SizeToContent="Height" z wierszem *Wiersz z gwiazdką zachowuje się jak Auto — pole wielowierszowe ma jedną linię.
długa etykieta w kolumnie o stałej szerokościTekst jest ucinany. Kolumna etykiet — Auto.
ZADANIA

Zadania

W każdym formularzu opisy pól to Label z klawiszem dostępu, a układ to Grid z kolumną etykiet Auto. Błędy sygnalizuj oknem MessageBox i przenoś kursor do złego pola. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Wypożyczenie książki★☆☆

Formularz biblioteki szkolnej: Numer karty (liczba czterocyfrowa 1000–9999), Tytuł książki, Liczba dni (1–30, domyślnie wpisane 14). Przycisk Wypożycz (pod Enter) pokazuje pod formularzem: „Karta 1234: „Lalka” — zwrot do 22.10.2026 (czwartek)”. Okno dopasowuje wysokość do zawartości i nie da się go zwęzić poniżej 360.

pokaż wskazówkę
  • Datę zwrotu da DateTime.Now.AddDays(dni), a format z nazwą dnia — ToString("dd.MM.yyyy (dddd)").
  • Formularz bez wiersza * to dobry przypadek dla SizeToContent.
ZAD 2Bilety do kina★☆☆

Pola: Imię, Bilety normalne, Bilety ulgowe (liczby całkowite od 0). Ceny: normalny 28 zł, ulgowy 20 zł. W jednej transakcji można kupić od 1 do 10 biletów łącznie. Przycisk Oblicz wpisuje do pola tylko do odczytu na dole: „Ola: 2 × normalny, 3 × ulgowy = 116,00 zł”. Pole wyniku jest pomijane przez Tab. Przycisk Anuluj zamyka okno i reaguje na Esc.

pokaż wskazówkę
  • Ceny zapisz jako stałe decimal.
  • Najpierw sprawdź każde pole osobno, a dopiero potem sumę biletów.
ZAD 3Kalkulator raty★★☆

Kalkulator kredytu na laptop: Kwota (100–20 000 zł), Oprocentowanie roczne w % (0–25, może być z przecinkiem), Liczba rat (3–48). Wynik: miesięczna rata, łączna kwota do spłaty i koszt odsetek, każda kwota z dwoma miejscami po przecinku. Raty liczysz ze wzoru na ratę równą: rata = K · r / (1 − (1 + r)−n), gdzie r to oprocentowanie miesięczne (procent roczny / 12 / 100), a n — liczba rat. Przy oprocentowaniu 0 rata to po prostu K / n.

pokaż wskazówkę
  • Potęgę policzysz przez Math.Pow(1 + r, -n).
  • Przypadek r == 0 musisz obsłużyć osobno — inaczej dzielisz przez zero.
  • Dla kontroli: 3000 zł, 10 %, 12 rat → rata 263,75 zł.
ZAD 4Wizytówka na żywo★★☆

Po lewej formularz: Imię i nazwisko, Stanowisko, Telefon, E-mail. Po prawej podgląd wizytówki, który zmienia się przy każdym naciśnięciu klawisza: imię i nazwisko dużą pogrubioną czcionką, pod nim stanowisko kursywą, niżej telefon i e-mail. Puste pole w podglądzie pokazuje szary tekst zastępczy, np. „Imię i nazwisko”. Wszystkie pola obsługuje jedna metoda TextChanged.

Wizytówka–□✕
Imię i nazwisko:
Anna Kowalska
Anna Kowalska
nauczycielka informatyki
tel. 600 100 200
E-mail
Stanowisko:
nauczycielka informatyki
Telefon:
600 100 200
E-mail:
 
pokaż wskazówkę
  • Trzecia kolumna siatki na podgląd, a podgląd — pionowy StackPanel z Grid.RowSpan na wszystkie wiersze formularza.
  • Tekst zastępczy i jego kolor ustaw w jednej metodzie pomocniczej z parametrami: pole, napis podglądu, tekst zastępczy.
  • Zdarzenie może przyjść, zanim powstanie podgląd. Na początku metody sprawdź, czy napis podglądu nie jest null.
ZAD 5Adres do wysyłki★★☆

Formularz adresu w dwóch parach kolumn (etykieta, pole, etykieta, pole): w pierwszym wierszu Ulica i Nr domu, w drugim Kod pocztowy i Miejscowość, w trzecim Odbiorca na całą szerokość. Kod pocztowy musi mieć postać NN-NNN (cyfry i myślnik na trzecim miejscu). Przycisk Drukuj etykietę pokazuje adres w oknie MessageBox w trzech liniach, tak jak na kopercie.

pokaż wskazówkę
  • Kolumny: Auto, *, Auto, *. Pole odbiorcy zaczyna w kolumnie 1 i zajmuje 3 kolumny.
  • Kod sprawdzisz w pętli po znakach: długość 6, znak o indeksie 2 to '-', pozostałe spełniają char.IsDigit.
ZAD 6Koszt wycieczki klasowej★★★

Wychowawca planuje wycieczkę. Pola: Liczba uczniów (10–40), Liczba dni (1–5), Autokar za dzień (zł), Nocleg za osobę i noc (zł), Bilety wstępu na osobę (zł). Zasady:

  • na każdych rozpoczętych 10 uczniów jedzie jeden opiekun (np. 21 uczniów → 3 opiekunów);
  • noclegów jest o jeden mniej niż dni; opiekunowie płacą za nocleg i bilety jak uczniowie;
  • koszt opiekunów rozkłada się na uczniów, a kwotę na ucznia zaokrągla się w górę do pełnych złotych.

Wynik w polu tylko do odczytu: liczba opiekunów, koszt całkowity, koszt na ucznia. Przycisk Wyczyść ma klawisz dostępu, a Oblicz — Enter. Wpisz w komentarzu dwa zestawy danych, które sprawdziłeś ręcznie.

pokaż wskazówkę
  • Opiekunowie: (uczniowie + 9) / 10 na liczbach całkowitych daje zaokrąglenie w górę.
  • Kwotę w górę do pełnych złotych da Math.Ceiling.
  • Najpierw policz osobno autokar, noclegi i bilety — łatwiej znaleźć błąd.
PODSUMOWANIE

Co trzeba zapamiętać

  • Label opisuje pole: treść w Content, wbudowany odstęp 5, klawisz dostępu przez _ i Target.
  • TextBlock — dłuższy tekst, wyniki i komunikaty.
  • Formularz w Grid: kolumny Auto + *, wiersze pól Auto, wiersz pola wielowierszowego *.
  • Krótkie pola: własny Width i HorizontalAlignment="Left". Przyciski na dole po prawej.
  • Kolejność Tab = kolejność w XAML; zmienisz ją TabIndex, wyłączysz IsTabStop="False".
  • SizeToContent dopasowuje okno do formularza, MinWidth/MinHeight chronią przed zbytnim zmniejszeniem.

Dokumentacja: Microsoft Learn — klasa Label.

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