Dataniedziela, 9 sierpnia 2026 Czas06:36:45
← Aplikacje mobilne (.NET MAUI)

Lekcja 12. Nawigacja II — Shell, trasy i przekazywanie parametrów

Trudny / egzaminacyjny

Po co się tego uczymy?

Shell to dziś rekomendowany, nowoczesny standard nawigacji w .NET MAUI — działa jak mapa całej aplikacji z zarejestrowanymi trasami (adresami), do których przechodzisz PO NAZWIE, a nie przez tworzenie obiektów stron ręcznie. Dodatkowo daje "za darmo" menu boczne i dolne zakładki. W większych aplikacjach (więcej niż 2-3 ekrany) Shell jest zdecydowanie wygodniejszy niż NavigationPage z poprzedniej lekcji.

Teoria

Rejestracja tras w AppShell

W AppShell.xaml.cs rejestrujesz każdą stronę (poza startową) pod unikalną nazwą — zwykle nazwą jej klasy przez nameof:

Routing.RegisterRoute(nameof(SecondPage), typeof(SecondPage));

Od tego momentu możesz przechodzić do tej strony PO NAZWIE, z dowolnego miejsca aplikacji:

await Shell.Current.GoToAsync(nameof(SecondPage));

Powrót do poprzedniego ekranu w Shell wygląda inaczej niż PopAsync — używa się specjalnej "kropki-kropki": await Shell.Current.GoToAsync("..");

Przekazywanie parametrów przez Shell

Zamiast konstruktora, dane przekazujesz w SŁOWNIKU dołączonym do wywołania GoToAsync:

await Shell.Current.GoToAsync(nameof(SecondPage),
    new Dictionary<string, object> { ["Imie"] = "Ania" });

Odbiór po stronie docelowej ma DWA sposoby:

Sposób Jak działa
[QueryProperty] Atrybut nad klasą strony, np. [QueryProperty(nameof(Imie), "Imie")] — Shell automatycznie wstrzykuje wartość parametru "Imie" do właściwości Imie. Najprostszy sposób dla pojedynczych, prostych parametrów.
IQueryAttributable Strona implementuje interfejs z metodą ApplyQueryAttributes(IDictionary<string, object> query), w której ręcznie odczytujesz wszystkie parametry ze słownika naraz. Wygodniejsze, gdy przekazujesz kilka parametrów albo cały obiekt.

Żeby wartość wyświetlona przez {Binding Imie} w XAML odświeżyła się po ustawieniu właściwości, klasa strony (albo jej model) musi implementować INotifyPropertyChanged i wywoływać zdarzenie PropertyChanged przy każdej zmianie — do tego mechanizmu wrócimy szerzej w lekcji o modelach danych.

Kiedy wybrać Shell zamiast NavigationPage?

  • Gdy planujesz menu boczne (Flyout) albo dolne zakładki (Tabs);
  • Gdy chcesz proste, czytelne trasy tekstowe (przydatne też przy tzw. deep linkach);
  • Gdy aplikacja ma więcej niż 2-3 ekrany i ręczne zarządzanie stosem stron zaczyna być niewygodne.

Dla bardzo prostych, dwu-trzy-ekranowych aplikacji (typowych na start w tym dziale) NavigationPage z poprzedniej lekcji jest równie poprawny i czasem szybszy do napisania.

Schemat

Nawigacja Shell - rejestracja i przejscie:

AppShell.xaml.cs:
  Routing.RegisterRoute(nameof(SecondPage), typeof(SecondPage));
                          |
                          v
  gdziekolwiek w aplikacji:
  await Shell.Current.GoToAsync(nameof(SecondPage),
      new Dictionary<string,object> { ["Imie"] = "Ania" });
                          |
                          v
  SecondPage odbiera parametr:
  [QueryProperty(nameof(Imie), "Imie")]  <- najprostszy sposob
  albo IQueryAttributable                <- gdy parametrow jest wiecej

  powrot:  await Shell.Current.GoToAsync("..");

Przykład z życia

Aplikacja sklepu internetowego z dolnymi zakładkami "Produkty", "Koszyk", "Profil" — dokładnie to, co Shell daje "z pudełka". Kliknięcie produktu na liście i przejście do jego szczegółów z przekazaniem ID produktu w parametrze trasy to typowe zastosowanie GoToAsync z parametrami.

AppShell.xaml

<!-- AppShell.xaml -->
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:local="clr-namespace:NawigacjaShellApp"
       x:Class="NawigacjaShellApp.AppShell">

    <!-- Startowa pozycja -->
    <ShellContent Title="Start" ContentTemplate="{DataTemplate local:MainPage}" />

</Shell>

AppShell.xaml.cs + MainPage/SecondPage

// AppShell.xaml.cs - rejestracja trasy
namespace NawigacjaShellApp;

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        Routing.RegisterRoute(nameof(SecondPage), typeof(SecondPage));
    }
}

// MainPage.xaml.cs - przejscie z parametrem
namespace NawigacjaShellApp;

public partial class MainPage : ContentPage
{
    public MainPage() => InitializeComponent();

    private async void OnGoClicked(object sender, EventArgs e)
    {
        var imie = PoleImie.Text ?? "";
        await Shell.Current.GoToAsync(nameof(SecondPage),
            new Dictionary<string, object> { ["Imie"] = imie });
    }
}

// SecondPage.xaml.cs - odbior parametru przez QueryProperty
namespace NawigacjaShellApp;

[QueryProperty(nameof(Imie), "Imie")]
public partial class SecondPage : ContentPage
{
    private string imie = "";
    public string Imie
    {
        get => imie;
        set { imie = value; OnPropertyChanged(); }
    }

    public SecondPage()
    {
        InitializeComponent();
        BindingContext = this;
    }

    private async void OnBackClicked(object sender, EventArgs e)
    {
        await Shell.Current.GoToAsync("..");
    }
}

Komentarz i wyjaśnienie kodu

AppShell.xaml definiuje TYLKO stronę startową jako ShellContentSecondPage nie musi tam być wymieniona, bo jest zarejestrowana PROGRAMOWO jako "trasa" w konstruktorze AppShell.xaml.cs przez Routing.RegisterRoute.

MainPage wywołuje Shell.Current.GoToAsync z nazwą trasy i słownikiem parametrów — dokładnie tak, jak przekazuje się parametry w adresie URL strony internetowej.

SecondPage ma atrybut [QueryProperty(nameof(Imie), "Imie")] nad definicją klasy — mówi to Shellowi: "gdy dostaniesz parametr o kluczu 'Imie', wstrzyknij go do właściwości Imie". Właściwość Imie wywołuje OnPropertyChanged() przy każdej zmianie (dziedziczone z ContentPage, która implementuje INotifyPropertyChanged), dzięki czemu {Binding Imie} w XAML od razu pokazuje nową wartość. Konstruktor ustawia BindingContext = this — strona "wiąże się sama ze sobą", żeby binding w XAML miał dostęp do jej własnych właściwości.

Ćwiczenie samodzielne

Utwórz projekt z Shell, zarejestruj trasę do SecondPage, i sprawdź przekazywanie parametru "Imie" dokładnie jak w przykładzie. Następnie zmień odbiór na IQueryAttributable zamiast [QueryProperty] i porównaj oba podejścia.

Zadania do pracy własnej

  1. Dodaj drugi parametr 'Wiek' (typu int) przekazywany razem z 'Imie' i wyświetl oba w SecondPage.

  2. Zaimplementuj odbiór parametrów przez IQueryAttributable zamiast [QueryProperty] w SecondPage — porównaj, który sposób wydaje Ci się czytelniejszy przy dwóch parametrach naraz.

  3. Zbuduj aplikację 'Katalog produktów' z Shell: ListaProduktowPage z zaszytą na sztywno listą 5 produktów (nazwa + cena jako para w prostej klasie Produkt) wyświetloną w CollectionView (poznanej wcześniej — na razie wystarczy ItemsSource z listy obiektów), gdzie kliknięcie produktu (SelectionChanged) przechodzi do SzczegolyProduktuPage z przekazaniem nazwy i ceny jako dwóch osobnych parametrów Shell, a strona szczegółów ma przycisk 'Wróć do listy' (GoToAsync('..')).

Typowe błędy

Brak Routing.RegisterRoute dla strony docelowej — próba GoToAsync(nameof(StronaX)) bez wcześniejszej rejestracji trasy rzuci wyjątek "nie znaleziono trasy".

Zapominanie o OnPropertyChanged w setterze właściwości QueryProperty — bez tego binding w XAML nie odświeży się, mimo że wartość właściwości faktycznie się zmieniła "pod spodem".

Mylenie GoToAsync("..") z Navigation.PopAsync() — to DWA różne systemy nawigacji (Shell kontra klasyczny NavigationPage) i nie należy ich mieszać w jednej aplikacji bez dobrego powodu.

Próba przekazania bardzo złożonego obiektu przez słownik GoToAsync między różnymi uruchomieniami aplikacji — słownik parametrów działa dobrze w ramach jednej sesji aplikacji, ale nie nadaje się do trwałego zapisywania stanu (do tego służą Preferences albo baza danych, omówione w dalszych lekcjach).

Nawiązanie do egzaminu zawodowego

To realizacja INF.04.6.2 w zakresie nowoczesnej nawigacji aplikacji mobilnej. Shell jest dziś standardem zalecanym przez Microsoft dla nowych projektów .NET MAUI — znajomość rejestrowania tras i przekazywania parametrów jest przydatna zarówno w zadaniach egzaminacyjnych, jak i w prawdziwych projektach.