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.

Grid wiersze i kolumny rozmiary * RowSpan / ColumnSpan
1

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.

Analogia do Excela

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.

2

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 – 3 wiersze, 2 kolumny
<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>
Numerowanie od zera!

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.

Rozmieszczanie kontrolek w Gridzie
<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>
Domyślna komórka to (0, 0)

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.

3

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.

Stały rozmiar
<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.

Rozmiar Auto
<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*.

Rozmiar proporcjonalny *
<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>
Złota trójca – jak zapamiętać
WartośćZnaczenieKiedy
50Zawsze dokładnie 50pxStałe paski, przyciski
AutoTyle ile zajmuje zawartośćEtykiety, nagłówki
*Reszta dostępnej przestrzeniGłówna treść, pola formularza

Praktyczny przykład – typowy formularz

Formularz z mieszanymi rozmiarami
<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>
4

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)
ColumnSpan i RowSpan – przykład
<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>
5

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 vs 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ą.

Margin – cztery sposoby zapisu
<!-- 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 -->
Kolejność: Lewo, Góra, Prawo, Dół

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 na kontrolce
<!-- 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 -->
6

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
HorizontalAlignmentLeft · Center · Right · Stretch
VerticalAlignmentTop · Center · Bottom · Stretch
Wyrównanie – przykłady
<!-- 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 to domyślne

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

7

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 wewnątrz Grida
<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>
8

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

Wyświetlenie linii siatki – tylko do debugowania!
<!-- 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>
Pamiętaj – wyłącz przed oddaniem

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.

9

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ć:

Gotowy szablon układu okna
<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>
10

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ę -->
11

Zadania do wykonania

Zadanie 1 łatwe

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

Zadanie 2 łatwe

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.

Zadanie 3 średnie

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=* i Height=*)

Przyciski nie muszą jeszcze działać – chodzi o poprawny układ.

Zadanie 4 średnie

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 Button jeden pod drugim
  • Obszar główny: duże pole TextBox na całą dostępną przestrzeń