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.

Ta interakcja wymaga włączonego JavaScriptu.

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.

MainWindow.xamlxaml
<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>
  1. DockPanel.Dock="Top" Menu jest przypięte do górnej krawędzi i zostaje tam bez względu na rozmiar okna.
  2. GroupBox bez Dock To ostatni element DockPanelu, więc wypełnia całą pozostałą przestrzeń.
  3. RowDefinitions i ColumnDefinitions Siatka ma dwa wiersze i dwie kolumny. Pierwsza kolumna jest Auto, czyli tak szeroka jak najdłuższa etykieta. Druga to *, czyli cała reszta.
  4. Grid.Row i Grid.Column Każ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.

MainWindow.xamlxml
<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>
  1. DockPanel.Dock="Bottom" Przypina element do dolnej krawędzi. Podobnie Top, Left i Right.
  2. 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ą.
  3. LastChildFill Domyślnie True: ostatnie dziecko wypełnia pozostałe miejsce, więc GroupBox roś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.

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.

Ta interakcja wymaga włączonego JavaScriptu.

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 robiPrzykład
OrientationKierunek elementów w StackPanel i WrapPanel: Horizontal lub Vertical.Orientation="Horizontal"
MarginOdstęp na zewnątrz elementu. Kolejność: lewo, góra, prawo, dół.Margin="10,5,10,0"
PaddingOdstęp od środka, między krawędzią elementu a jego zawartością.Padding="12,4"
HorizontalAlignment / VerticalAlignmentWyrównanie w komórce lub panelu: Left, Center, Right, Stretch oraz Top, Bottom.HorizontalAlignment="Right"
DockPanel.DockPrzypina element do krawędzi DockPanelu: Top, Bottom, Left, Right.DockPanel.Dock="Bottom"
Grid.Row / Grid.ColumnWskazują komórkę siatki dla elementu. Liczymy od zera.Grid.Row="1"
HeaderTytuł w nagłówku GroupBox albo MenuItem.Header="Dane użytkownika"
Margin i Padding mylą się najczęściej: Margin to miejsce wokół, Padding to miejsce w środku.

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.

WPFOdpowiednik w Windows FormsCo robi
StackPanelFlowLayoutPanelUkłada elementy jeden po drugim, w rzędzie lub kolumnie.
GridTableLayoutPanelDzieli przestrzeń na wiersze i kolumny.
DockPanelwłaściwość Dock + MenuStrip / ToolStripPrzypina elementy do krawędzi formularza.
GroupBoxGroupBoxRamka z tytułem, ta sama nazwa w obu technologiach.
GridSplitterSplitContainerDzieli okno na dwie części z ruchomym uchwytem.
Wiedza o paniach WPF przenosi się na Windows Forms: zmieniają się nazwy, zasada zostaje.

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
Tak nie
<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ę.

Tak
<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
Tak nie
<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ź.

Tak
<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
Tak nie
<!-- 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ół.

Tak
<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ę
Tak nie
<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.

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

Ta interakcja wymaga włączonego JavaScriptu.

10 · Szybki przegląd

Pięć pytań do własnego okna

Przejdź po nich, zanim uznasz, że okno jest gotowe.

Podsumowanie

Co warto zapamiętać

  1. Panel układa elementy według jednej zasady i sam przelicza rozmieszczenie przy zmianie rozmiaru okna.
  2. StackPanel to kolejka, WrapPanel to kolejka z zawijaniem, DockPanel przypina do krawędzi, a Grid dzieli na wiersze i kolumny.
  3. Margin to odstęp na zewnątrz (lewo, góra, prawo, dół), Padding to odstęp w środku.
  4. W Grid wymiary zapisujesz w pikselach, jako Auto albo gwiazdką *. Numery wierszy i kolumn liczymy od zera.
  5. Panele się zagnieżdża: zwykle DockPanel na zewnątrz, a w środku Grid albo StackPanel.

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ę →
Źródła i dalsza nauka