1. Czym są panele w WPF?
Panele w WPF to specjalne kontrolki, których głównym zadaniem jest zarządzanie pozycją i rozmiarem innych kontrolek – swoich dzieci (children). Każdy panel implementuje własną strategię układania elementów – niektóre układają je jeden obok drugiego, inne w siatce, jeszcze inne pozwalają na dowolne pozycjonowanie.
Panele są fundamentem elastyczności WPF w kwestii interfejsów użytkownika. W przeciwieństwie do starszych technologii, gdzie pozycja każdej kontrolki musiała być określona w pikselach, panele WPF automatycznie obliczają optymalne pozycje i rozmiary swoich dzieci w zależności od dostępnej przestrzeni i preferencji każdej kontrolki.
Każdy panel ma swoje mocne strony i przypadki użycia. Grid doskonale sprawdza się do tworzenia formularzy i złożonych layoutów, StackPanel idealnie nadaje się do prostych list elementów, a Canvas pozwala na precyzyjne pozycjonowanie. Zrozumienie, kiedy używać którego panelu, to klucz do tworzenia funkcjonalnych i estetycznych interfejsów.
System layoutu w WPF działa w dwóch fazach: najpierw każda kontrolka informuje panel o swoich preferencjach dotyczących rozmiaru (faza „measure”), następnie panel przydziela każdemu dziecku konkretną przestrzeń i pozycję (faza „arrange”). Ten dwuetapowy proces pozwala na tworzenie interfejsów, które automatycznie dostosowują się do zmiany rozmiaru okna, różnych rozdzielczości ekranu czy zmiany zawartości.
2. Grid – najuniversalniejszy panel
Grid to bez wątpienia najważniejszy i najczęściej używany panel w WPF. Jego nazwa pochodzi od faktu, że organizuje przestrzeń w formie siatki – systemu wierszy (rows) i kolumn (columns). Każda kontrolka umieszczona w Grid może zajmować jedną lub więcej komórek tej siatki.
Podstawową zaletą Grid jest jego elastyczność. Może służyć zarówno jako prosty kontener dla kilku elementów, jak i jako zaawansowany system layoutu dla złożonych formularzy czy całych okien aplikacji. Grid automatycznie dostosowuje rozmiary swoich wierszy i kolumn w zależności od zawartości i dostępnej przestrzeni.
Grid różni się znacząco od tabeli HTML czy kontrolek typu DataGrid. Nie jest przeznaczony do wyświetlania danych tabelarycznych, ale do organizowania interfejsu użytkownika. Jego siatka jest niewidoczna dla użytkownika – służy jedynie jako system współrzędnych dla programisty.
Podstawowe użycie Grid
Najprostszy Grid bez zdefiniowanych wierszy i kolumn zachowuje się jak jeden duży kontener, gdzie wszystkie elementy są na siebie nakładane:
<Grid>
<TextBlock Text="Pierwszy element"/>
<Button Content="Drugi element"/>
</Grid>W tym przykładzie TextBlock i Button będą na siebie nałożone, co zwykle nie jest pożądane. Dlatego w praktyce zawsze definiujemy strukturę siatki.
3. Definiowanie struktury Grid – wiersze i kolumny
RowDefinitions – definiowanie wierszy
Aby podzielić Grid na wiersze, używamy sekcji Grid.RowDefinitions:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Pierwszy wiersz"/>
<TextBlock Grid.Row="1" Text="Drugi wiersz"/>
<TextBlock Grid.Row="2" Text="Trzeci wiersz"/>
</Grid>Ten przykład tworzy Grid z trzema wierszami o równych wysokościach. Każdy RowDefinition reprezentuje jeden wiersz siatki. Kontrolki umieszczamy w konkretnych wierszach używając właściwości Grid.Row.
ColumnDefinitions – definiowanie kolumn
Podobnie, kolumny definiujemy w sekcji Grid.ColumnDefinitions:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Lewa kolumna"/>
<TextBlock Grid.Column="1" Text="Prawa kolumna"/>
</Grid>Kombinowanie wierszy i kolumn
Prawdziwa siła Grid ujawnia się gdy łączymy wiersze z kolumnami:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Nagłówek zajmuje dwie kolumny -->
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
Text="Nagłówek aplikacji"
FontSize="18" FontWeight="Bold"
HorizontalAlignment="Center"/>
<!-- Panel boczny -->
<TextBlock Grid.Row="1" Grid.Column="0"
Text="Menu nawigacji"
Background="LightGray"/>
<!-- Główna zawartość -->
<TextBlock Grid.Row="1" Grid.Column="1"
Text="Główna zawartość aplikacji"/>
<!-- Stopka -->
<TextBlock Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
Text="Stopka aplikacji"
Background="DarkGray"
HorizontalAlignment="Center"/>
</Grid>4. Rozmiary wierszy i kolumn
Grid oferuje trzy sposoby określania rozmiarów wierszy i kolumn, każdy odpowiedni do różnych scenariuszy.
Auto – rozmiar dopasowany do zawartości
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Krótki tekst"/>
<TextBlock Grid.Row="1" Text="To jest znacznie dłuższy tekst, który zajmie więcej miejsca"/>
</Grid>Rozmiar Auto oznacza, że wiersz lub kolumna będzie dokładnie tak duża, jak wymaga tego zawartość. Pierwszy wiersz będzie niski (wysokość jednej linii tekstu), a drugi wyższy.
Stały rozmiar w pikselach
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="100px" Background="LightBlue"/>
<TextBlock Grid.Column="1" Text="200px" Background="LightGreen"/>
</Grid>Podając konkretną liczbę, ustalamy stały rozmiar w jednostkach niezależnych od urządzenia (zwykle odpowiadają pikselom).
Star sizing (*) – proporcjonalne dzielenie przestrzeni
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="1 część" Background="LightBlue"/>
<TextBlock Grid.Column="1" Text="2 części" Background="LightGreen"/>
<TextBlock Grid.Column="2" Text="1 część" Background="LightCoral"/>
</Grid>Oznaczenie * (star) dzieli pozostałą przestrzeń proporcjonalnie. W powyższym przykładzie środkowa kolumna będzie dwa razy szersza niż kolumny boczne. Jeśli Grid ma szerokość 400px, to kolumny będą miały 100px, 200px i 100px.
Kombinowanie różnych rozmiarów
W praktyce często łączymy różne sposoby określania rozmiarów:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Dopasuj do zawartości -->
<RowDefinition Height="*"/> <!-- Zajmij pozostałą przestrzeń -->
<RowDefinition Height="30"/> <!-- Stała wysokość 30px -->
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Nagłówek - dopasowany do tekstu"/>
<TextBlock Grid.Row="1" Text="Zawartość - elastyczna"/>
<Button Grid.Row="2" Content="Przycisk - stała wysokość" Height="25"/>
</Grid>5. Grid.ColumnSpan i Grid.RowSpan – łączenie komórek
Podobnie jak w tabelach, kontrolki w Grid mogą zajmować więcej niż jedną komórkę siatki.
ColumnSpan – rozciągnięcie na kolumny
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Nagłówek rozciągnięty na wszystkie kolumny -->
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
Text="Nagłówek na całą szerokość"
Background="DarkBlue"
Foreground="White"
HorizontalAlignment="Center"/>
<!-- Trzy elementy w drugim wierszu -->
<Button Grid.Row="1" Grid.Column="0" Content="Lewy"/>
<Button Grid.Row="1" Grid.Column="1" Content="Środkowy"/>
<Button Grid.Row="1" Grid.Column="2" Content="Prawy"/>
</Grid>RowSpan – rozciągnięcie na wiersze
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Panel boczny rozciągnięty na wszystkie wiersze -->
<TextBlock Grid.Row="0" Grid.Column="0" Grid.RowSpan="3"
Text="Menu boczne"
Background="LightGray"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<!-- Zawartość w prawej kolumnie -->
<TextBlock Grid.Row="0" Grid.Column="1" Text="Sekcja 1"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Sekcja 2"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="Sekcja 3"/>
</Grid>6. Praktyczne zastosowania Grid
Prosty formularz
Grid idealnie nadaje się do tworzenia formularzy:
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Nagłówek -->
<TextBlock Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
Text="Formularz rejestracji"
FontSize="18" FontWeight="Bold"
Margin="0,0,0,20"/>
<!-- Pola formularza -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="Imię:" VerticalAlignment="Center" Margin="0,0,10,5"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="25" Margin="0,0,0,5"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Nazwisko:" VerticalAlignment="Center" Margin="0,0,10,5"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="25" Margin="0,0,0,5"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Email:" VerticalAlignment="Center" Margin="0,0,10,5"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="25" Margin="0,0,0,15"/>
<!-- Przyciski -->
<Grid Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Content="Zapisz" Width="80" Margin="0,0,10,0"/>
<Button Grid.Column="2" Content="Anuluj" Width="80"/>
</Grid>
</Grid>Layout aplikacji z regionami
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Pasek narzędzi -->
<RowDefinition Height="*"/> <!-- Główna zawartość -->
<RowDefinition Height="Auto"/> <!-- Pasek stanu -->
</Grid.RowDefinitions>
<!-- Pasek narzędzi -->
<Grid Grid.Row="0" Background="DarkBlue" Height="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Moja Aplikacja"
Foreground="White" VerticalAlignment="Center" Margin="10,0"/>
<Button Grid.Column="2" Content="Zamknij" Width="60" Height="25" Margin="10,0"/>
</Grid>
<!-- Główna zawartość -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/> <!-- Panel boczny -->
<ColumnDefinition Width="*"/> <!-- Główna zawartość -->
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Menu" Background="LightGray"
HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,10,0,0"/>
<TextBlock Grid.Column="1" Text="Główna zawartość aplikacji"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<!-- Pasek stanu -->
<TextBlock Grid.Row="2" Text="Gotowy" Background="Gray"
Foreground="White" Padding="5"/>
</Grid>Siatka elementów
Grid może służyć do tworzenia regularnych siatek elementów:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="1" Margin="5"/>
<Button Grid.Row="0" Grid.Column="1" Content="2" Margin="5"/>
<Button Grid.Row="0" Grid.Column="2" Content="3" Margin="5"/>
<Button Grid.Row="1" Grid.Column="0" Content="4" Margin="5"/>
<Button Grid.Row="1" Grid.Column="1" Content="5" Margin="5"/>
<Button Grid.Row="1" Grid.Column="2" Content="6" Margin="5"/>
</Grid>7. Wizualizacja Grid – obramowania dla lepszej orientacji
Podczas projektowania layoutów często przydatne jest widzenie granic siatki Grid. Możemy to osiągnąć na kilka sposobów.
ShowGridLines – wbudowana opcja
WPF oferuje właściwość ShowGridLines, która wyświetla linie siatki:
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Komórka 1,1"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="Komórka 1,2"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Komórka 2,1"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Komórka 2,2"/>
</Grid>Uwaga: ShowGridLines="True" należy używać tylko podczas projektowania. W gotowej aplikacji należy ją usunąć, ponieważ linie nie są estetyczne.
Obramowania poszczególnych komórek
Dla bardziej kontrolowanego wyglądu możemy dodawać obramowania do poszczególnych elementów:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="Komórka z obramowaniem" Margin="5"/>
</Border>
<Border Grid.Row="0" Grid.Column="1" BorderBrush="Blue" BorderThickness="2">
<Button Content="Przycisk w obramowaniu"/>
</Border>
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
BorderBrush="Red" BorderThickness="1" Background="LightYellow">
<TextBlock Text="Komórka rozciągnięta na dwie kolumny" HorizontalAlignment="Center"/>
</Border>
</Grid>Border to kontrolka kontenerowa, która może zawierać jeden element potomny i dodaje mu obramowanie oraz opcjonalnie tło.
8. Sterowanie Grid z poziomu kodu C#
Tworzenie Grid programowo
public MainWindow()
{
InitializeComponent();
// Tworzenie Grid programowo
Grid dynamicGrid = new Grid();
// Dodawanie wierszy
dynamicGrid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
dynamicGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
dynamicGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(50) });
// Dodawanie kolumn
dynamicGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(100) });
dynamicGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) });
// Dodawanie elementów
TextBlock header = new TextBlock { Text = "Dynamiczny nagłówek", FontWeight = FontWeights.Bold };
Grid.SetRow(header, 0);
Grid.SetColumn(header, 0);
Grid.SetColumnSpan(header, 2);
dynamicGrid.Children.Add(header);
Button btn = new Button { Content = "Dynamiczny przycisk" };
Grid.SetRow(btn, 2);
Grid.SetColumn(btn, 1);
dynamicGrid.Children.Add(btn);
// Dodanie Grid do głównego okna (zakładając, że główny kontener to Grid o nazwie mainContainer)
mainContainer.Children.Add(dynamicGrid);
}Modyfikowanie rozmiarów z kodu
private void ZmienRozmiarKolumn()
{
// Zmiana szerokości pierwszej kolumny
if (myGrid.ColumnDefinitions.Count > 0)
{
myGrid.ColumnDefinitions[0].Width = new GridLength(150);
}
// Zmiana wysokości drugiego wiersza
if (myGrid.RowDefinitions.Count > 1)
{
myGrid.RowDefinitions[1].Height = GridLength.Auto;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Przełączanie między różnymi rozmiarami
if (myGrid.ColumnDefinitions[0].Width.Value == 100)
{
myGrid.ColumnDefinitions[0].Width = new GridLength(200);
}
else
{
myGrid.ColumnDefinitions[0].Width = new GridLength(100);
}
}Umieszczanie elementów na Grid programowo
private void DodajElementDoGrid(UIElement element, int row, int column, int rowSpan = 1, int columnSpan = 1)
{
Grid.SetRow(element, row);
Grid.SetColumn(element, column);
if (rowSpan > 1)
Grid.SetRowSpan(element, rowSpan);
if (columnSpan > 1)
Grid.SetColumnSpan(element, columnSpan);
myGrid.Children.Add(element);
}
private void Button_DodajElement_Click(object sender, RoutedEventArgs e)
{
// Tworzenie nowego przycisku
Button newButton = new Button
{
Content = $"Przycisk {myGrid.Children.Count + 1}",
Margin = new Thickness(5)
};
// Dodanie w określonej pozycji
DodajElementDoGrid(newButton, 1, 0, 1, 2); // wiersz 1, kolumna 0, rozciągnięty na 2 kolumny
}Dynamiczne dodawanie wierszy i kolumn
private void DodajWiersz()
{
RowDefinition newRow = new RowDefinition();
newRow.Height = GridLength.Auto;
myGrid.RowDefinitions.Add(newRow);
// Dodanie elementu do nowego wiersza
TextBlock newText = new TextBlock
{
Text = $"Nowy wiersz {myGrid.RowDefinitions.Count}",
Background = Brushes.LightBlue,
Padding = new Thickness(5)
};
Grid.SetRow(newText, myGrid.RowDefinitions.Count - 1);
Grid.SetColumn(newText, 0);
myGrid.Children.Add(newText);
}
private void DodajKolumne()
{
ColumnDefinition newColumn = new ColumnDefinition();
newColumn.Width = new GridLength(1, GridUnitType.Star);
myGrid.ColumnDefinitions.Add(newColumn);
// Dodanie elementu do nowej kolumny
Button newButton = new Button
{
Content = $"Kolumna {myGrid.ColumnDefinitions.Count}",
Margin = new Thickness(2)
};
Grid.SetRow(newButton, 0);
Grid.SetColumn(newButton, myGrid.ColumnDefinitions.Count - 1);
myGrid.Children.Add(newButton);
}Odnajdywanie i modyfikowanie istniejących elementów
private void ZmienElementWGrid()
{
// Znajdź element w określonej pozycji
foreach (UIElement element in myGrid.Children)
{
int row = Grid.GetRow(element);
int column = Grid.GetColumn(element);
if (row == 1 && column == 0)
{
if (element is Button btn)
{
btn.Content = "Zmieniony tekst";
btn.Background = Brushes.Yellow;
}
}
}
}
private void PrzenieElementWGrid(UIElement element, int newRow, int newColumn)
{
Grid.SetRow(element, newRow);
Grid.SetColumn(element, newColumn);
}
// Przykład kompletnej aplikacji z dynamicznym Grid
private void Button_UsunElement_Click(object sender, RoutedEventArgs e)
{
// Usuń ostatni dodany element
if (myGrid.Children.Count > 0)
{
myGrid.Children.RemoveAt(myGrid.Children.Count - 1);
}
}9. Dobre praktyki z Grid
Używanie Auto dla etykiet, * dla zawartości
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <!-- Etykiety -->
<ColumnDefinition Width="*"/> <!-- Pola edycji -->
</Grid.ColumnDefinitions>
<!-- Etykieta zajmuje tylko tyle miejsca, ile potrzebuje -->
<!-- Pole edycji wykorzystuje pozostałą przestrzeń -->
</Grid>Margin zamiast pustych wierszy/kolumn
Zamiast tworzyć puste wiersze lub kolumny dla odstępów, używaj właściwości Margin:
<!-- Źle -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/> <!-- Pusty wiersz -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Pierwszy"/>
<TextBlock Grid.Row="2" Text="Drugi"/>
</Grid>
<!-- Dobrze -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Pierwszy"/>
<TextBlock Grid.Row="1" Text="Drugi" Margin="0,10,0,0"/>
</Grid>Nazywanie wierszy i kolumn w złożonych layoutach
W bardzo złożonych Grid można używać nazw zamiast indeksów:
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="HeaderRow" Height="Auto"/>
<RowDefinition x:Name="ContentRow" Height="*"/>
<RowDefinition x:Name="FooterRow" Height="Auto"/>
</Grid.RowDefinitions>
<!-- Można odwoływać się po nazwie -->
<TextBlock Grid.Row="{x:Static Grid.HeaderRow}" Text="Nagłówek"/>
</Grid>Podsumowanie
Grid to najpotężniejszy i najuniversalniejszy panel w WPF. Jego znajomość jest kluczowa dla tworzenia profesjonalnych interfejsów użytkownika.
Kluczowe aspekty Grid do zapamiętania:
- RowDefinitions i ColumnDefinitions definiują strukturę siatki
- Auto, stały rozmiar i * (star) to trzy sposoby określania rozmiarów
- Grid.Row i Grid.Column pozycjonują elementy w siatce
- ColumnSpan i RowSpan pozwalają na łączenie komórek
- Grid idealnie nadaje się do formularzy i złożonych layoutów
- Używaj Margin zamiast pustych wierszy/kolumn