Aplikacje mobilne · Układy
Układy I: VerticalStackLayout, HorizontalStackLayout i Grid
Układ (layout) decyduje, jak kontrolki rozmieszczają się na ekranie. Bez niego przyciski i pola nakładałyby się na siebie. MAUI musi dobrze działać na bardzo różnych ekranach, od małego telefonu po tablet, dlatego układy są tu szczególnie ważne. Zaczynamy od dwóch najczęstszych: układów stosowych i Grid.
- około 45 minut
- .NET MAUI · VerticalStackLayout · Grid
- INF.04.6.2
Po tej lekcji: ułożyć formularz w VerticalStackLayout i HorizontalStackLayout, zbudować go też w Grid z wierszami i kolumnami (Auto, *) oraz użyć ColumnSpan.
Zobacz, zanim przeczytasz
Zobacz cztery ekrany, zanim zaczniesz
Każdy ekran to prawdziwy podgląd z kodu XAML. Poniżej rozbijemy je na cztery etapy. W każdym najpierw krótka wiedza, potem wyzwanie, a na końcu szybkie sprawdzenie.
Stos układa elementy w linii. Grid daje wiersze i kolumny. Zanim napiszesz kod, powiedz sobie: czy to jedna linia, czy tabela?
Etap 1 z 4
VerticalStackLayout: jedno pod drugim
Stos pionowy układa elementy w kolumnie. Używasz go do formularzy, kart i prostych ekranów. Dwie właściwości decydują o wyglądzie: Spacing (odstęp między elementami) i Padding (odstęp od krawędzi).
Wyzwanie 1 · stos pionowy
Zbuduj kartę powitalną
Odtwórz ekran. Cele odhaczają się na żywo.
Sprawdź, czy zapamiętałeś
Dwa szybkie pytania
Etap 2 z 4
HorizontalStackLayout: obok siebie
Stos poziomy ustawia elementy w jednym rzędzie, od lewej do prawej. Pasuje do pary przycisków, rzędu ikon albo pola z etykietą. Uwaga: gdy zabraknie miejsca, elementy nie zawijają się, tylko wychodzą poza ekran.
Wyzwanie 2 · czytanie kodu
Który ekran zobaczysz?
Wyzwanie 3 · stos poziomy
Dwa przyciski obok siebie
Sprawdź, czy zapamiętałeś
Szybkie pytanie
Etap 3 z 4
Grid: wiersze i kolumny
Grid dzieli ekran na komórki. Każdy element dostaje adres: Grid.Row i Grid.Column (numeracja od 0). Rozmiar toru ustawiasz tak: Auto dopasowuje do zawartości, liczba daje stałe piksele, * bierze resztę miejsca, a 2* bierze dwa razy więcej niż *.
4 · Siatka
Jak opisać siatkę
| Właściwość | Opis |
|---|---|
Grid.RowDefinitions / Grid.ColumnDefinitions | Liczba i rozmiar wierszy i kolumn. |
Height="Auto" / Width="Auto" | Wiersz lub kolumna zajmuje tyle, ile potrzebuje zawartość. |
Height="*" / Width="*" | Elastycznie wypełnia resztę miejsca. Możliwe też 2*, 3* dla proporcji. |
Grid.Row / Grid.Column | Ustawiane na dziecku: w której komórce ma być element. |
Grid.ColumnSpan / Grid.RowSpan | Rozciąga element na kilka kolumn lub wierszy. |
RowSpacing / ColumnSpacing | Odstępy między wierszami i kolumnami. |
Wyzwanie 4 · czytanie kodu
Dlaczego widać tylko jedno pole?
Wyzwanie 5 · siatka
Siatka 2×2
Wyzwanie 6 · cudzy kod
Dlaczego ten formularz jest zepsuty?
Sprawdź, czy zapamiętałeś
Trzy szybkie pytania
Etap 4 z 4
ColumnSpan: element na kilka kolumn
Nagłówek albo duży przycisk często mają zająć całą szerokość formularza. Służy do tego Grid.ColumnSpan (w poziomie) i Grid.RowSpan (w pionie).
Wyzwanie 7 · rozciąganie
Nagłówek i przycisk na całą szerokość
Wyzwanie 8 · kod za widokiem
Ułóż plik `.xaml.cs`
Wyzwanie końcowe
Odtwórz ekran logowania
Połącz wszystko: Grid, kolumny, ColumnSpan.
Powtórka końcowa
Cztery pytania z całej lekcji
7 · Sprawdź się
Który układ?
Cztery sytuacje z ekranami.
Dla chętnych · po pierwszej stroniePracownia w Visual Studio: zbuduj ten formularz w prawdziwym projekcie
Rozwiń dodatkowe warsztaty ↓
Wyzwania powyżej uczą układów w przeglądarce. Tu masz pełną instrukcję krok po kroku, jak zrobić to samo w Visual Studio i uruchomić na emulatorze, tak jak na egzaminie praktycznym. Otwórz, gdy będziesz gotowy do pracy z projektem.
1. Przygotuj miejsce pracy
Co dodajesz, a co podmieniasz?
Stronę dodaj przez Projekt → Dodaj → Nowy element → .NET MAUI ContentPage (XAML). Powstaje para .xaml i .xaml.cs. Dla ContentView wybierz szablon ContentView; dla modeli i serwisów zwykłą klasę. Jeśli plik już istnieje, podmień wskazaną zawartość — nie twórz drugiej klasy o tej samej nazwie.
| Plik | Rola w ćwiczeniu |
|---|---|
| RejestracjaStosPage.xaml | Wygląd strony lub kontrolki |
| RejestracjaGridPage.xaml | Wygląd strony lub kontrolki |
| Grid — fragment XAML | Fragment w istniejącym pliku |
| Fragment.xaml | Fragment w istniejącym pliku |
Zanim uruchomisz: trzy połączenia
x:Class="MojaAplikacja.NazwaStrony" w XAML musi zgadzać się z namespace MojaAplikacja i nazwą klasy C#. InitializeComponent() tworzy kontrolki opisane w XAML.
x:Name="PoleImie" daje kodowi C# dostęp przez PoleImie.Text. Z kolei Clicked="Zapisz_Clicked" wskazuje metodę o tej nazwie w pliku .xaml.cs. Sam napis przycisku w Text niczego nie podpina.
Gdy dodajesz XAML z obsługą zdarzeń, od razu uzupełnij też odpowiadający C#. Uruchamiaj dopiero po skończeniu oznaczonej pary plików; brakujący handler nie jest błędem środowiska.
2. Uruchom sam wygląd — jeszcze bez logiki
W świeżym projekcie zastąp MainPage.xaml poniższym ekranem. W MainPage.xaml.cs zachowaj konstruktor z InitializeComponent(); usuń szablonowy licznik i OnCounterClicked, bo nie ma ich już w XAML. Na tym etapie App i AppShell pozostają z szablonu. To pełny, tymczasowy XAML — później świadomie go podmienisz, nie dokleisz do drugiego ContentPage.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.MainPage">
<VerticalStackLayout Padding="24" Spacing="14">
<Label Text="Rejestracja" FontSize="24" />
<Entry Placeholder="Login" />
<Entry Placeholder="Hasło" IsPassword="True" />
</VerticalStackLayout>
</ContentPage>
- Pierwsza próba Uruchom. Zobaczysz kontrolki, ale przyciski bez Clicked jeszcze nic nie robią. Sprawdź rozmiar i kolejność elementów. Zatrzymaj aplikację przed następnym etapem.
3. Wskaż stronę startową ćwiczenia
Po dodaniu docelowych stron podmień App.xaml.cs na poniższy. App.xaml zostaje z szablonu. Od tej chwili startuje RejestracjaStosPage, a nie wcześniejszy tymczasowy MainPage z AppShell. Uzupełnij jej pliki w kolejnych etapach, zanim ponownie uruchomisz projekt.
namespace MojaAplikacja;
public partial class App : Application
{
public App() => InitializeComponent();
protected override Window CreateWindow(IActivationState? activationState)
=> new Window(new NavigationPage(new RejestracjaStosPage()));
}
CreateWindowTworzy okno aplikacji. NavigationPage jest kontenerem zwykłych stron i daje przycisk powrotu; nie łączymy go z Shell.new RejestracjaStosPage()Tworzy instancję docelowej strony. Aby testować drugi niezależny przykład, zatrzymaj program, zmień tylko ten typ i uruchom ponownie.
4. Połącz wygląd z działaniem
Teraz zbuduj docelowy przykład. Pełne pliki poniżej zastępują swoje wcześniejsze wersje. Przeczytaj opis nad kodem i objaśnienia pod nim; potem sprawdź przepływ danych, nie tylko brak czerwonych podkreśleń.
Gdy przykład ma kilka plików zależnych od siebie, najpierw dodaj wszystkie wskazane typy, a potem uruchom całość. Ustaw breakpoint w obsłudze głównego przycisku i prześledź jedną próbę.
Budowa krok po kroku
RejestracjaStosPage.xaml
Dodaj ContentPage (XAML) RejestracjaStosPage i podmień jej XAML. Zostaw wygenerowany konstruktor w pliku .xaml.cs. Ten ekran pokazuje rozmieszczenie; przyciski nie mają jeszcze handlerów.
Najszybszy sposób na prosty formularz: nagłówek, dwa pola i poziomy rząd przycisków na dole.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.RejestracjaStosPage">
<VerticalStackLayout Padding="20" Spacing="15">
<Label Text="Rejestracja"
FontSize="24"
FontAttributes="Bold"
HorizontalOptions="Center" />
<Entry x:Name="PoleEmail" Placeholder="E-mail" />
<Entry x:Name="PoleHaslo" Placeholder="Hasło" IsPassword="True" />
<!-- Poziomy rząd przycisków na dole formularza -->
<HorizontalStackLayout Spacing="10" HorizontalOptions="Center">
<Button Text="Anuluj" BackgroundColor="Gray" TextColor="White" WidthRequest="100" />
<Button Text="Zarejestruj" BackgroundColor="RoyalBlue" TextColor="White" WidthRequest="100" />
</HorizontalStackLayout>
</VerticalStackLayout>
</ContentPage>
Padding="20" Spacing="15"Margines wewnętrzny kontenera i odstęp między elementami.IsPassword="True"Ukrywa wpisywane znaki wEntry.HorizontalStackLayoutw środku Zagnieżdżony poziomy układ z dwoma przyciskami obok siebie.HorizontalOptions="Center"Wyśrodkowanie elementu w poziomie.WidthRequest="100"Żądana szerokość przycisku.
Budowa krok po kroku
RejestracjaGridPage.xaml
Dodaj osobną RejestracjaGridPage. Nie wklejaj jej jako drugiego korzenia do poprzedniego pliku. Wiersze i kolumny ustalają wspólne pozycje etykiet i pól.
Trzy wiersze (dwa Auto i jeden *) oraz dwie kolumny (Auto na etykiety, * na pola).
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.RejestracjaGridPage">
<Grid Padding="20" RowSpacing="12" ColumnSpacing="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="E-mail:" Grid.Row="0" Grid.Column="0" VerticalTextAlignment="Center" />
<Entry Placeholder="podaj e-mail" Grid.Row="0" Grid.Column="1" />
<Label Text="Hasło:" Grid.Row="1" Grid.Column="0" VerticalTextAlignment="Center" />
<Entry IsPassword="True" Placeholder="********" Grid.Row="1" Grid.Column="1" />
<!-- ColumnSpan="2" rozciąga przycisk na całą szerokość formularza -->
<Button Text="Zarejestruj"
Grid.Row="2" Grid.ColumnSpan="2"
BackgroundColor="RoyalBlue" TextColor="White" />
</Grid>
</ContentPage>
<RowDefinition Height="Auto" />Wiersz dopasowany do pola. Ostatni wiersz*bierze resztę miejsca.<ColumnDefinition Width="Auto" />i"*"Kolumna etykiet jest wąska, a kolumna pól zajmuje resztę szerokości.Grid.Row="0" Grid.Column="1"Adres komórki: wiersz i kolumna liczone od zera.Grid.ColumnSpan="2"Przycisk obejmuje obie kolumny, więc jest wyśrodkowany względem całego formularza.VerticalTextAlignment="Center"Etykieta wyrównana w pionie do pola obok.
Budowa krok po kroku
Ten sam Grid zapisany krócej
To alternatywny fragment zawartości strony, nie nowy plik projektu. Podmień nim dotychczasowy kontener wewnątrz ContentPage; nie wklejaj całego Grid do wnętrza innego Grid bez potrzeby.
RowDefinitions="Auto,*,Auto" odpowiada trzem RowDefinition z Height Auto, * i Auto. ColumnDefinitions="*,2*" dzieli pozostałą szerokość w proporcji 1:2. Stała liczba oznacza jednostki niezależne od gęstości, nie zawsze fizyczne piksele.
<Grid RowDefinitions="Auto,*,Auto" ColumnDefinitions="*,2*"
RowSpacing="12" ColumnSpacing="12">
<Label Text="Nagłówek" Grid.ColumnSpan="2" />
<Label Text="Menu" Grid.Row="1" />
<Label Text="Treść" Grid.Row="1" Grid.Column="1" />
<Button Text="Zapisz" Grid.Row="2" Grid.ColumnSpan="2" />
</Grid>
Grid.Row="1"Indeksy zaczynają się od zera; to drugi wiersz.Auto/*/2*Rozmiar zawartości / udział dostępnego miejsca / podwójny udział. Gwiazdka potrzebuje skończonego miejsca od rodzica.
Budowa krok po kroku
Klasyczny StackLayout: odczytaj Orientation
To porównanie z klasycznym StackLayout. Orientation określa kierunek. Dwa przyciski są zawartością jednej kontrolki układu.
StackLayout spotkasz w starszych projektach i poleceniach. Orientation="Horizontal" układa wiersz, Vertical kolumnę. W nowych prostych układach używaj wyspecjalizowanych HorizontalStackLayout/VerticalStackLayout.
<StackLayout Orientation="Horizontal" Spacing="12">
<Button Text="Tak" />
<Button Text="Nie" />
</StackLayout>
- Przeczytaj powiązanie x:Name identyfikuje kontrolkę, a Binding odczytuje właściwość obiektu ustawionego jako BindingContext. To dwa różne mechanizmy.
Plik .xaml.cs też musi istnieć
Szablon strony lub ContentView generuje poniższe klasy. Nie dodawaj ich drugi raz, jeśli Visual Studio już je utworzyło. W każdej parze musi pozostać InitializeComponent().
RejestracjaStosPage — minimalny kod za widokiem
namespace MojaAplikacja;
public partial class RejestracjaStosPage : ContentPage
{
public RejestracjaStosPage() => InitializeComponent();
}
partialDruga część klasy powstaje z XAML. Nie twórz ręcznie pól kontrolek ani drugiej metody InitializeComponent.
RejestracjaGridPage — minimalny kod za widokiem
namespace MojaAplikacja;
public partial class RejestracjaGridPage : ContentPage
{
public RejestracjaGridPage() => InitializeComponent();
}
partialDruga część klasy powstaje z XAML. Nie twórz ręcznie pól kontrolek ani drugiej metody InitializeComponent.
5. Odbiór: czy naprawdę działa?
Wykonaj próby w swojej aplikacji, nie tylko w modelu na stronie. Zaznaczaj dopiero po sprawdzeniu wyniku. Funkcje urządzenia wymagają testu na właściwej platformie.
6 · Czego unikać
Cztery błędy z układami
Stos do bardzo długiej listy
<VerticalStackLayout>
<!-- 500 etykiet w pętli -->
</VerticalStackLayout>Aplikacja zwalnia, a przewijanie szarpie.
<CollectionView ItemsSource="{Binding Pozycje}" />CollectionView tworzy tylko widoczne elementy.
Grid bez definicji wierszy i kolumn
<Grid>
<Label Text="A" Grid.Row="1" />
<Label Text="B" Grid.Row="2" />
</Grid>Grid ma jeden wiersz i jedną kolumnę, więc wszystko nakłada się w jednej komórce.
<Grid>
<Grid.RowDefinitions>
<RowDefinition /> <RowDefinition /> <RowDefinition />
</Grid.RowDefinitions>
...
</Grid>Numery Grid.Row odnoszą się do zdefiniowanych wierszy.
Pomylenie `Auto` z `*`
<ColumnDefinition Width="Auto" /> <!-- na długi tekst -->Kolumna pęcznieje i spycha resztę poza ekran.
<ColumnDefinition Width="*" />Auto dopasowuje się do treści, * zajmuje resztę dostępnego miejsca.
Test tylko na jednym ekranie
Sprawdzony wyłącznie na dużym telefonieNa małym ekranie układ się „rozjeżdża”.
Uruchom na kilku wirtualnych urządzeniach o różnych rozmiarachAplikacja mobilna musi działać na różnych ekranach.
Ćwiczenie z pomocą · samodzielna praca
6. Mała zmiana, którą zrobisz samodzielnie
Dodaj do siatki wiersz z polem Miasto, zachowując wyrównanie kolumn.
Dodaj do siatki wiersz z polem Miasto, zachowując wyrównanie kolumn. Dotychczasowe działanie ma pozostać poprawne.
? Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź
Dodaj RowDefinition przed wierszem przycisku.
Krok 2 — pokaż podpowiedź
Etykieta i Entry dostają ten sam nowy Grid.Row i różne Grid.Column.
Krok 3 — pokaż podpowiedź
Przesuń przycisk o jeden wiersz; indeksy zaczynają się od zera.
Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.
Podsumowanie
Co warto zapamiętać
VerticalStackLayoutukłada w pionie,HorizontalStackLayoutw poziomie. MająPaddingiSpacing.- Układy stosowe łączysz, ale nie używasz ich do długich list. Tam jest
CollectionView. Grid: definiujeszRowDefinitionsiColumnDefinitions, a dziecku wskazujeszGrid.RowiGrid.Column.Autodopasowuje do treści,*wypełnia resztę,2*to proporcje.ColumnSpaniRowSpanrozciągają element.- Układ sprawdzaj na kilku rozmiarach ekranu.
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ę →