Aplikacje desktopowe · WPF
Layouty: jak okno samo układa swoje elementy
Narysować przycisk jest łatwo. Trudniej sprawić, żeby po rozciągnięciu okna nadal wyglądało dobrze. Od tego są panele układu: Ty mówisz, jak mają się ułożyć elementy, a WPF sam liczy piksele.
- około 35 minut
- WPF · XAML
- INF.04.5.3
Po tej lekcji: wybrać właściwy panel (StackPanel, WrapPanel, DockPanel albo Grid) i złożyć z nich formularz, który nie rozjeżdża się przy zmianie rozmiaru okna.
Zacznijmy od życia
Panel to sposób ustawiania mebli w pokoju
Gdybyś ustawiał każdy przycisk ręcznie, podając piksel po pikselu, każda zmiana rozmiaru okna zepsułaby cały wygląd. Dlatego w WPF nie podajesz współrzędnych. Wybierasz panel, czyli kontener z jedną zasadą ułożenia, i wkładasz do niego elementy.
Panel mówi na przykład „ustaw wszystko jeden pod drugim” albo „podziel okno na dwie kolumny”. Gdy użytkownik rozciągnie okno, panel sam przeliczy rozmieszczenie.
Laboratorium · ćwiczenie w przeglądarce, do którego odsyłają rozgrzewki na końcu lekcji
1 · Sprawdź w działaniu
Zmieniaj panel i patrz, co się dzieje
Po lewej jest kod XAML, po prawej okno. Wybierz gotowy przykład, a potem zmień jedną rzecz: Orientation, Margin, Width. Przesuń suwak szerokości okna i zobacz, który panel się dostosowuje, a który nie. Najedź na element w oknie, żeby zobaczyć jego znacznik.
Jeden panel to jedna zasada ułożenia. Okno, które się nie rozjeżdża, zwykle składa się z kilku paneli włożonych jeden w drugi.
2 · Poznaj narzędzia
Cztery panele i dwa dodatki
Najczęściej użyjesz czterech paneli. Do tego dochodzą Canvas i GroupBox, które dobrze znać z nazwy.
Gridsiatka
Dzieli okno na wiersze i kolumny, jak tabela w arkuszu. Najczęściej używany panel, idealny do formularzy.
Na przykład etykiety w pierwszej kolumnie, pola w drugiej
StackPanelkolejka
Ustawia elementy jeden za drugim: domyślnie w pionie, a z Orientation="Horizontal" w poziomie.
Na przykład rząd przycisków Dodaj, Edytuj, Usuń
WrapPanelzawijanie
Układa elementy obok siebie i zawija do nowego rzędu, gdy zabraknie miejsca, jak tekst na końcu linii.
Na przykład kafelki miast albo zdjęć w galerii
DockPanelprzypinanie
Przypina elementy do krawędzi: góry, dołu, lewej lub prawej. Ostatni element wypełnia resztę okna.
Na przykład menu u góry, pasek stanu na dole, treść pośrodku
Canvaswspółrzędne
Pozwala ustawić dokładne położenie X i Y. Nie dostosowuje się do rozmiaru okna, więc używa się go do rysowania i gier, a nie do formularzy.
Na przykład rysunek techniczny, plansza gry
GroupBoxramka z tytułem
To nie jest panel układu. To ramka z nagłówkiem, w której umieszczasz inny panel, żeby wizualnie zgrupować powiązane pola.
Na przykład ramka „Dane użytkownika” wokół formularza
3 · Całość w jednym oknie
DockPanel, GroupBox i Grid razem
W praktyce panele się zagnieżdża. Tu DockPanel trzyma menu u góry, a resztę okna zajmuje GroupBox z formularzem ułożonym w Gridzie.
<Window x:Class="UkladyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Formularz użytkownika" Height="300" Width="400">
<DockPanel>
<!-- Pasek menu przypięty do góry -->
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Plik">
<MenuItem Header="Nowy" />
<MenuItem Header="Zapisz" />
<Separator />
<MenuItem Header="Zamknij" />
</MenuItem>
</Menu>
<!-- Reszta okna: formularz w siatce, w ramce z tytułem -->
<GroupBox Header="Dane użytkownika" Margin="10">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="Imię:" Grid.Row="0" Grid.Column="0" />
<TextBox Grid.Row="0" Grid.Column="1" Margin="5" />
<Label Content="Nazwisko:" Grid.Row="1" Grid.Column="0" />
<TextBox Grid.Row="1" Grid.Column="1" Margin="5" />
</Grid>
</GroupBox>
</DockPanel>
</Window>
DockPanel.Dock="Top"Menu jest przypięte do górnej krawędzi i zostaje tam bez względu na rozmiar okna.GroupBoxbezDockTo ostatni element DockPanelu, więc wypełnia całą pozostałą przestrzeń.RowDefinitionsiColumnDefinitionsSiatka ma dwa wiersze i dwie kolumny. Pierwsza kolumna jestAuto, czyli tak szeroka jak najdłuższa etykieta. Druga to*, czyli cała reszta.Grid.RowiGrid.ColumnKażda kontrolka podaje swoją komórkę, jak współrzędne w tabeli. Liczymy od zera.
Do zadań
Pasek przycisków przypięty do dołu okna
Zadanie 2 prosi o pasek „Zapisz” i „Anuluj” na dole, a zadanie 3 o pasek narzędzi po lewej. W DockPanelu robi się to atrybutem DockPanel.Dock, a o wyniku decyduje kolejność zapisu.
<DockPanel>
<Menu DockPanel.Dock="Top">...</Menu>
<!-- pasek dolny zapisany PRZED treścią -->
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"
HorizontalAlignment="Right" Margin="10">
<Button Content="Zapisz" Margin="0,0,8,0" />
<Button Content="Anuluj" />
</StackPanel>
<!-- pasek narzędzi po lewej: pionowy WrapPanel -->
<WrapPanel DockPanel.Dock="Left" Orientation="Vertical" Width="90">
<Button Content="Pędzel" Margin="4" />
<Button Content="Gumka" Margin="4" />
</WrapPanel>
<!-- ostatni element zajmuje całą resztę okna -->
<GroupBox Header="Dane użytkownika" Margin="10">...</GroupBox>
</DockPanel>
DockPanel.Dock="Bottom"Przypina element do dolnej krawędzi. PodobnieTop,LeftiRight.- Kolejność w XAML Elementy są przypinane po kolei i każdy odcina swój kawałek okna. Ostatni element (bez
Dock) dostaje to, co zostało. Dlatego paski piszesz przed treścią. LastChildFillDomyślnieTrue: ostatnie dziecko wypełnia pozostałe miejsce, więcGroupBoxrośnie razem z oknem.
4 · Mała lupa
Jeden przycisk, pięć informacji
Spójrz na jeden przycisk w siatce i kliknij kolejne części, żeby zobaczyć, co każda z nich mówi panelowi.
Co to za element?
Nazwa kontrolki. Wszystko po niej to jej właściwości, zapisane jako atrybuty.
Powstaje przycisk.
W którym wierszu siatki?
Liczymy od zera, więc 1 to drugi wiersz. To tzw. właściwość dołączona: należy do Gridu, ale wpisujesz ją w dziecku.
Przycisk trafia do drugiego wiersza.
W której kolumnie?
Znowu od zera: 0 to pierwsza kolumna.
Przycisk stoi w lewej kolumnie.
Jaki odstęp wokół?
Cztery liczby to w kolejności: lewo, góra, prawo, dół. Tu: 10 z lewej, 5 z góry, 10 z prawej i 0 z dołu. Jedna liczba (Margin="8") oznacza ten sam odstęp ze wszystkich stron.
Wokół przycisku jest wolne miejsce.
Co jest napisane?
Content to zawartość przycisku, tu zwykły napis. Ukośnik przed > zamyka znacznik bez osobnego znacznika końcowego.
Na przycisku widnieje „Zapisz”.
5 · Wymiary w siatce
Trzy sposoby na szerokość kolumny
Kolumna (i wiersz) może mieć stałą liczbę pikseli, rozmiar Auto albo część pozostałego miejsca zapisaną gwiazdką. Zmień wartości i przesuń suwak szerokości okna.
Stałe piksele nie rosną razem z oknem, a gwiazdki tak. Dlatego formularz zwykle ma kolumnę Auto na etykiety i * na pola.
6 · Ściągawka
Właściwości, których używasz w każdym oknie
| Właściwość | Co robi | Przykład |
|---|---|---|
Orientation | Kierunek elementów w StackPanel i WrapPanel: Horizontal lub Vertical. | Orientation="Horizontal" |
Margin | Odstęp na zewnątrz elementu. Kolejność: lewo, góra, prawo, dół. | Margin="10,5,10,0" |
Padding | Odstęp od środka, między krawędzią elementu a jego zawartością. | Padding="12,4" |
HorizontalAlignment / VerticalAlignment | Wyrównanie w komórce lub panelu: Left, Center, Right, Stretch oraz Top, Bottom. | HorizontalAlignment="Right" |
DockPanel.Dock | Przypina element do krawędzi DockPanelu: Top, Bottom, Left, Right. | DockPanel.Dock="Bottom" |
Grid.Row / Grid.Column | Wskazują komórkę siatki dla elementu. Liczymy od zera. | Grid.Row="1" |
Header | Tytuł w nagłówku GroupBox albo MenuItem. | Header="Dane użytkownika" |
7 · Pytanie z egzaminu
To samo, tylko inaczej nazwane
Na egzaminie INF.04 może paść pytanie o Windows Forms, czyli starszą technologię okienkową w C#. Tam nie ma XAML. Układ projektujesz, przeciągając kontrolki myszą na formularz, a rolę paneli pełnią inne kontenery.
Najważniejsza różnica: WPF sam dopasowuje układ do rozmiaru okna, bo opisujesz go regułami. Windows Forms opiera się raczej na współrzędnych, a przy zmianie rozmiaru pomaga właściwość Anchor, która „przykleja” krawędź kontrolki do krawędzi formularza.
| WPF | Odpowiednik w Windows Forms | Co robi |
|---|---|---|
StackPanel | FlowLayoutPanel | Układa elementy jeden po drugim, w rzędzie lub kolumnie. |
Grid | TableLayoutPanel | Dzieli przestrzeń na wiersze i kolumny. |
DockPanel | właściwość Dock + MenuStrip / ToolStrip | Przypina elementy do krawędzi formularza. |
GroupBox | GroupBox | Ramka z tytułem, ta sama nazwa w obu technologiach. |
GridSplitter | SplitContainer | Dzieli okno na dwie części z ruchomym uchwytem. |
8 · Czego unikać
Cztery pułapki początkujących
Każdą z nich zobaczysz na egzaminie u kolegów z ławki. Warto ją rozpoznać u siebie.
Grid bez RowDefinitions
<Grid>
<Label Content="Imię:" Grid.Row="0" />
<TextBox Grid.Row="1" />
</Grid>Siatka ma domyślnie jeden wiersz i jedną kolumnę, więc oba elementy lądują w tej samej komórce i nakładają się.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="Imię:" Grid.Row="0" />
<TextBox Grid.Row="1" />
</Grid>Numer w Grid.Row ma sens dopiero wtedy, gdy wiersze zostały zdefiniowane.
Canvas jako układ całego okna
<Canvas Height="40">
<Button Content="OK" Canvas.Left="320" Canvas.Top="8" />
</Canvas>Przycisk stoi w stałym miejscu. Zwęź okno, a wyleci poza jego krawędź.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="OK" />
</StackPanel>Panel z wyrównaniem utrzymuje przycisk przy prawej krawędzi bez liczenia pikseli.
Pomylona kolejność w Margin
<!-- chcę 20 px odstępu od góry -->
<Button Margin="20,0,0,0" Content="OK" />Pierwsza liczba dotyczy lewej strony, więc przycisk odsunął się w prawo, a nie w dół.
<Button Margin="0,20,0,0" Content="OK" />Kolejność to lewo, góra, prawo, dół. Zapamiętaj ją jako „L-G-P-D” albo sprawdź w laboratorium.
Zapomniane, że ostatni element DockPanelu wypełnia resztę
<DockPanel>
<TextBox DockPanel.Dock="Left" Width="80" />
<Menu DockPanel.Dock="Top" />
</DockPanel>Pole tekstowe przypina się do lewej i zajmuje całą wysokość okna, a menu (jako ostatnie) rozciąga się na całą resztę zamiast stanąć cienkim paskiem u góry.
<DockPanel>
<Menu DockPanel.Dock="Top" />
<TextBox DockPanel.Dock="Left" Width="80" />
<Border />
</DockPanel>Elementy dokują się w kolejności zapisu. Na końcu zostaw ten, który ma zająć środek.
9 · Sprawdź się
Dobierz panel do sytuacji
Pięć krótkich scenariuszy. Wybierz panel, który rozwiąże problem najprościej.
10 · Szybki przegląd
Pięć pytań do własnego okna
Przejdź po nich, zanim uznasz, że okno jest gotowe.
Podsumowanie
Co warto zapamiętać
- Panel układa elementy według jednej zasady i sam przelicza rozmieszczenie przy zmianie rozmiaru okna.
StackPanelto kolejka,WrapPanelto kolejka z zawijaniem,DockPanelprzypina do krawędzi, aGriddzieli na wiersze i kolumny.Marginto odstęp na zewnątrz (lewo, góra, prawo, dół),Paddingto odstęp w środku.- W
Gridwymiary zapisujesz w pikselach, jakoAutoalbo gwiazdką*. Numery wierszy i kolumn liczymy od zera. - Panele się zagnieżdża: zwykle
DockPanelna zewnątrz, a w środkuGridalboStackPanel.
Dalej na swoim koncie
Rozgrzewki, quiz i zadania są w biurku ucznia.
Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.
Zaloguj się →