Grid – układ siatki
Poznasz Grid – najważniejszy layout w WPF. Nauczysz się dzielić okno na wiersze i kolumny oraz precyzyjnie rozmieszczać w nich kontrolki. Grid jest podstawą niemal każdego okna WPF.
Czym jest Grid?
Grid to layout WPF, który dzieli przestrzeń okna na wiersze i kolumny – jak tabela w Excelu. Każdą kontrolkę umieszczasz w konkretnej komórce tej tabeli.
Wyobraź sobie arkusz kalkulacyjny. Masz wiersze (poziome) i kolumny (pionowe). W każdą komórkę wstawiasz coś innego. Grid działa dokładnie tak samo – określasz ile ma być wierszy i kolumn, a potem mówisz każdej kontrolce „idź do wiersza 2, kolumny 1″.
Grid to domyślny layout w WPF – każdy nowy projekt zaczyna się od pustego Grida wewnątrz okna. Bez zdefiniowania wierszy i kolumn Grid zachowuje się jak jeden wielki panel, w którym wszystko leży na sobie.
Wiersze i kolumny
Definicja w XAML
Wiersze definiujesz w Grid.RowDefinitions,
a kolumny w Grid.ColumnDefinitions.
Każdy wiersz to <RowDefinition />,
każda kolumna to <ColumnDefinition />.
<Grid> <!-- Definicja wierszy --> <Grid.RowDefinitions> <RowDefinition /> <!-- wiersz 0 --> <RowDefinition /> <!-- wiersz 1 --> <RowDefinition /> <!-- wiersz 2 --> </Grid.RowDefinitions> <!-- Definicja kolumn --> <Grid.ColumnDefinitions> <ColumnDefinition /> <!-- kolumna 0 --> <ColumnDefinition /> <!-- kolumna 1 --> </Grid.ColumnDefinitions> <!-- Tu umieszczamy kontrolki --> </Grid>
Wiersze i kolumny numerowane są od 0, nie od 1. Pierwszy wiersz to wiersz 0, drugi to wiersz 1 itd. To standard w programowaniu – tak jak indeksy w tablicach.
Umieszczanie kontrolek w komórkach
Każdej kontrolce mówisz, w którym wierszu i kolumnie ma się znaleźć
za pomocą attached properties:
Grid.Row i Grid.Column.
<Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <!-- wiersz 0, kolumna 0 (lewy górny róg) --> <TextBlock Grid.Row="0" Grid.Column="0" Text="Imię:" /> <!-- wiersz 0, kolumna 1 --> <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtImie" /> <!-- wiersz 1, kolumna 0 --> <TextBlock Grid.Row="1" Grid.Column="0" Text="Wiek:" /> <!-- wiersz 1, kolumna 1 --> <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtWiek" /> <!-- wiersz 2, kolumna 1 (przycisk po prawej) --> <Button Grid.Row="2" Grid.Column="1" Content="Zatwierdź" x:Name="btnZatwierdz" /> </Grid>
Jeśli nie podasz Grid.Row ani Grid.Column,
kontrolka trafi do wiersza 0, kolumny 0.
Możesz pominąć Grid.Row="0" – ale dla czytelności
kodu lepiej zawsze pisać explicite.
Rodzaje rozmiarów wierszy i kolumn
To jest najważniejsza rzecz do zrozumienia w Grid.
Właściwość Height (dla wierszy) i Width (dla kolumn)
może przyjąć trzy różne typy wartości.
Stały rozmiar – liczba w pikselach
Podajesz dokładną liczbę pikseli. Rozmiar nie zmienia się gdy użytkownik zmienia rozmiar okna.
<Grid.RowDefinitions> <RowDefinition Height="50" /> <!-- zawsze dokładnie 50px --> <RowDefinition Height="200" /> <!-- zawsze dokładnie 200px --> <RowDefinition Height="30" /> <!-- zawsze dokładnie 30px --> </Grid.RowDefinitions>
Kiedy używać: pasek tytułu, pasek stanu, przyciski o stałej wysokości.
Auto – dopasowanie do zawartości
Wiersz lub kolumna dopasowuje rozmiar do swojej zawartości. Jeśli wewnątrz jest przycisk o wysokości 35px – wiersz będzie miał 35px.
<Grid.RowDefinitions> <RowDefinition Height="Auto" /> <!-- tyle ile potrzeba --> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <!-- szerokość etykiety --> <ColumnDefinition Width="Auto" /> <!-- szerokość pola --> </Grid.ColumnDefinitions>
Kiedy używać: etykiety, przyciski o zmiennej treści, pasek narzędzi.
Proporcjonalny – gwiazdka (*)
Gwiazdka * oznacza: zajmij całe dostępne miejsce.
Jeśli kilka wierszy/kolumn ma *, dzielą przestrzeń równo.
Możesz też podać liczbę przed gwiazdką – 2* zajmie dwa razy
tyle co 1*.
<Grid.RowDefinitions> <RowDefinition Height="Auto" /> <!-- nagłówek – tyle ile potrzeba --> <RowDefinition Height="*" /> <!-- główna treść – reszta przestrzeni --> <RowDefinition Height="Auto" /> <!-- stopka – tyle ile potrzeba --> </Grid.RowDefinitions> <!-- ————————————————————————————————————————————————————— --> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <!-- 1 część (np. 200px) --> <ColumnDefinition Width="*" /> <!-- 1 część (np. 200px) --> <!-- Obie kolumny mają po 50% szerokości --> </Grid.ColumnDefinitions> <!-- ————————————————————————————————————————————————————— --> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <!-- 1 część ~33% --> <ColumnDefinition Width="2*" /> <!-- 2 części ~67% --> <!-- lewa kolumna 1/3, prawa 2/3 szerokości okna --> </Grid.ColumnDefinitions>
| Wartość | Znaczenie | Kiedy |
|---|---|---|
50 | Zawsze dokładnie 50px | Stałe paski, przyciski |
Auto | Tyle ile zajmuje zawartość | Etykiety, nagłówki |
* | Reszta dostępnej przestrzeni | Główna treść, pola formularza |
Praktyczny przykład – typowy formularz
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <!-- wiersz etykiety --> <RowDefinition Height="Auto" /> <!-- wiersz pola --> <RowDefinition Height="*" /> <!-- główny obszar --> <RowDefinition Height="Auto" /> <!-- przyciski na dole --> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <!-- etykieta --> <ColumnDefinition Width="*" /> <!-- pole wejściowe --> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="Imię:" Margin="5" /> <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtImie" Margin="5" /> <TextBlock Grid.Row="1" Grid.Column="0" Text="Email:" Margin="5" /> <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtEmail" Margin="5" /> <!-- Główny obszar zajmuje całą pozostałą przestrzeń --> <TextBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" AcceptsReturn="True" TextWrapping="Wrap" Margin="5" /> <Button Grid.Row="3" Grid.Column="1" Content="Wyślij" HorizontalAlignment="Right" Margin="5" /> </Grid>
RowSpan i ColumnSpan – łączenie komórek
Tak jak w tabeli HTML można scalać komórki, w Grid możesz sprawić, żeby element zajął więcej niż jedną komórkę.
| Właściwość | Co robi |
|---|---|
Grid.ColumnSpan="2" | Element zajmuje 2 kolumny (od bieżącej) |
Grid.RowSpan="3" | Element zajmuje 3 wiersze (od bieżącego) |
<Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <!-- Nagłówek rozciągnięty na 3 kolumny --> <TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Text="Nagłówek całej szerokości" FontSize="18" HorizontalAlignment="Center" /> <!-- Panel boczny zajmuje 2 wiersze --> <Border Grid.Row="1" Grid.Column="0" Grid.RowSpan="2" Background="#2a2a3a" /> <!-- Główna treść --> <TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" AcceptsReturn="True" /> <!-- Przyciski w ostatnim wierszu, dwie prawe kolumny --> <StackPanel Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right"> <Button Content="Anuluj" Margin="5" /> <Button Content="Zapisz" Margin="5" /> </StackPanel> </Grid>
Margin i Padding – odstępy
Domyślnie elementy w Grid przylegają do krawędzi komórek – wyglądają jak
stłoczone do siebie. Żeby dodać przestrzeń, używasz Margin
i Padding.
Margin to przestrzeń na zewnątrz elementu –
odstęp między nim a innymi elementami lub krawędzią komórki.
Padding to przestrzeń wewnątrz elementu –
odstęp między jego krawędzią a zawartością.
<!-- Jedna liczba – taki sam margines ze wszystkich stron --> <Button Margin="10" Content="OK" /> <!-- Dwie liczby – lewo/prawo, góra/dół --> <Button Margin="20,5" Content="OK" /> <!-- lewo=20, prawo=20, góra=5, dół=5 --> <!-- Cztery liczby – lewo, góra, prawo, dół --> <Button Margin="10,5,10,15" Content="OK" /> <!-- lewo=10, góra=5, prawo=10, dół=15 -->
Cztery wartości w Margin idą w kolejności
L, G, P, D (Lewo, Góra, Prawo, Dół).
To odwrotnie niż w CSS (góra, prawo, dół, lewo)!
Łatwy sposób to zapamiętać: w WPF zaczynamy od lewej strony.
<!-- Padding – przestrzeń wewnątrz przycisku --> <Button Content="Kliknij" Padding="20,10" <!-- tekst ma 20px od lewej/prawej, 10px od góry/dołu --> Margin="5" /> <!-- 5px przestrzeni od innych elementów -->
Wyrównanie elementów w komórce
Element w komórce Grid domyślnie rozciąga się na całą komórkę (Stretch). Możesz to zmienić właściwościami wyrównania.
| Właściwość | Możliwe wartości |
|---|---|
HorizontalAlignment | Left · Center · Right · Stretch |
VerticalAlignment | Top · Center · Bottom · Stretch |
<!-- Przycisk w prawym dolnym rogu komórki --> <Button Grid.Row="2" Grid.Column="1" Content="Zapisz" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="5" /> <!-- Etykieta wyśrodkowana pionowo (przy polu tekstowym) --> <TextBlock Grid.Row="0" Grid.Column="0" Text="Imię:" VerticalAlignment="Center" Margin="5" /> <!-- Pole tekstowe rozciągnięte na całą komórkę (domyślne) --> <TextBox Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" Margin="5" />
Stretch jest domyślną wartością obu właściwości.
Oznacza to, że kontrolka rozciąga się, aby wypełnić
całą dostępną przestrzeń w komórce.
Właśnie dlatego TextBox zajmuje pełną szerokość
kolumny bez żadnych dodatkowych ustawień.
Zagnieżdżanie Gridów
W komórce Grid możesz umieścić kolejny Grid. To pozwala tworzyć dowolnie złożone układy.
<Grid> <!-- zewnętrzny Grid --> <Grid.RowDefinitions> <RowDefinition Height="50" /> <!-- pasek nagłówka --> <RowDefinition Height="*" /> <!-- zawartość --> <RowDefinition Height="40" /> <!-- pasek przycisków --> </Grid.RowDefinitions> <!-- Nagłówek --> <TextBlock Grid.Row="0" Text="Rejestracja" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" /> <!-- Wewnętrzny Grid z formularzem --> <Grid Grid.Row="1" Margin="10"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <TextBlock Grid.Row="0" Grid.Column="0" Text="Login:" Margin="0,0,0,8" VerticalAlignment="Center" /> <TextBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8" /> <TextBlock Grid.Row="1" Grid.Column="0" Text="Hasło:" Margin="0,0,0,8" VerticalAlignment="Center" /> <PasswordBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,8" /> <TextBlock Grid.Row="2" Grid.Column="0" Text="Email:" VerticalAlignment="Center" /> <TextBox Grid.Row="2" Grid.Column="1" /> </Grid> <!-- Pasek przycisków na dole --> <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="5"> <Button Content="Anuluj" Margin="5,0" Width="80" /> <Button Content="Zarejestruj" Margin="5,0" Width="100" /> </StackPanel> </Grid>
Linie siatki – pomocna wskazówka przy projektowaniu
Gdy uczysz się Grida, pomocne jest wyświetlenie linii siatki
– żeby zobaczyć gdzie kończą się wiersze i kolumny.
Służy do tego właściwość ShowGridLines="True".
<!-- ShowGridLines tylko podczas tworzenia – usuń przed oddaniem! --> <Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="50" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> </Grid>
ShowGridLines="True" służy tylko do nauki i debugowania.
W gotowej aplikacji linie siatki muszą być wyłączone.
Usuń ten atrybut lub zmień na False przed oddaniem projektu.
Typowy układ okna aplikacji
Większość aplikacji WPF ma ten sam szkielet – pasek tytułu lub menu na górze, główna treść w środku, przyciski lub pasek stanu na dole. Oto gotowy szablon, z którego możesz korzystać:
<Window Title="Moja Aplikacja" Width="600" Height="450" WindowStartupLocation="CenterScreen"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <!-- 0: Menu / pasek narzędzi --> <RowDefinition Height="*" /> <!-- 1: Główna zawartość --> <RowDefinition Height="Auto" /> <!-- 2: Pasek stanu / przyciski --> </Grid.RowDefinitions> <!-- WIERSZ 0: Menu --> <Menu Grid.Row="0"> <MenuItem Header="_Plik"> <MenuItem Header="_Nowy" InputGestureText="Ctrl+N" /> <MenuItem Header="_Otwórz" InputGestureText="Ctrl+O" /> <Separator /> <MenuItem Header="_Zamknij" /> </MenuItem> </Menu> <!-- WIERSZ 1: Główna zawartość (tu wstawiasz własne elementy) --> <Grid Grid.Row="1" Margin="10"> <!-- Tutaj Twoje kontrolki --> </Grid> <!-- WIERSZ 2: Pasek stanu --> <StatusBar Grid.Row="2"> <StatusBarItem> <TextBlock x:Name="lblStatus" Text="Gotowy" /> </StatusBarItem> </StatusBar> </Grid> </Window>
Częste błędy w Grid
❌ Błąd 1: Odwołanie do nieistniejącego wiersza / kolumny
❌ Źle
<Grid>
<Grid.RowDefinitions>
<RowDefinition /> <!-- wiersz 0 -->
<RowDefinition /> <!-- wiersz 1 -->
</Grid.RowDefinitions>
<!-- Błąd! Wiersz 2 nie istnieje -->
<Button Grid.Row="2" Content="OK" />
</Grid>
✅ Dobrze
<Grid>
<Grid.RowDefinitions>
<RowDefinition /> <!-- wiersz 0 -->
<RowDefinition /> <!-- wiersz 1 -->
<RowDefinition /> <!-- wiersz 2 -->
</Grid.RowDefinitions>
<Button Grid.Row="2" Content="OK" />
</Grid>
❌ Błąd 2: Zapomniany Margin – kontrolki przylegają do siebie
❌ Wygląda źle – brak odstępów
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Imię:" />
<TextBox Grid.Row="0" Grid.Column="1" />
<!-- Elementy przylegają do krawędzi -->
✅ Wygląda dobrze – z Margin
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Imię:"
Margin="5"
VerticalAlignment="Center" />
<TextBox Grid.Row="0" Grid.Column="1"
Margin="5" />
❌ Błąd 3: Wiele elementów w tej samej komórce (nieświadomie)
❌ Efekt: elementy leżą na sobie
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Imię:" />
<TextBox Grid.Row="0"
<!-- brak Grid.Column –
domyślnie kolumna 0!
leżą na sobie --> />
✅ Każdy ma swoją komórkę
<TextBlock Grid.Row="0" Grid.Column="0"
Text="Imię:" />
<TextBox Grid.Row="0" Grid.Column="1" />
<!-- Zawsze podawaj obie
właściwości Grid.Row i
Grid.Column -->
❌ Błąd 4: Mylenie gwiazdek – * w stałej kolumnie
❌ Mylące – wszystko Auto
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<!-- Jeśli zawartość jest mała,
kolumny są małe – pole formularza
nie rozciągnie się -->
✅ Etykieta Auto, pole *
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<!-- Etykieta ma tyle ile potrzeba,
pole TextBox zajmuje resztę -->
Zadania do wykonania
Stwórz okno z Gridem 3×3 (3 wiersze, 3 kolumny).
Włącz ShowGridLines="True". W każdej komórce umieść
TextBlock z napisem wskazującym jej pozycję,
np. „Wiersz 0, Kolumna 1″.
Ustaw wszystkie wiersze i kolumny na *.
Zbuduj prosty formularz rejestracyjny z polami:
Imię, Nazwisko, Email, Wiek. Użyj Grida z 4 wierszami i 2 kolumnami.
Lewa kolumna (Auto) – etykiety, prawa (*) – pola TextBox.
Na dole (5. wiersz, ColumnSpan=2) przycisk „Zarejestruj”
wyrównany do prawej.
Stwórz kalkulator w układzie 5×4. Użyj Grida z:
- Wiersz 0 – wyświetlacz (
ColumnSpan=4,Height=60) - Wiersze 1–4 – przyciski cyfr i operatorów (każdy
Width=*iHeight=*)
Przyciski nie muszą jeszcze działać – chodzi o poprawny układ.
Zbuduj layout typowej aplikacji używając zagnieżdżonych Gridów:
- Zewnętrzny Grid: wiersz 0 (
Auto) – pasek tytułu, wiersz 1 (*) – zawartość, wiersz 2 (30) – pasek stanu - W wierszu 1 – drugi Grid z dwiema kolumnami: lewa (
200) – panel boczny z listą opcji, prawa (*) – obszar główny - Panel boczny: inny kolor tła, trzy
Buttonjeden pod drugim - Obszar główny: duże pole
TextBoxna całą dostępną przestrzeń