1. Co to znaczy „nawigacja między oknami”?
Wyobraź sobie aplikację na telefon lub komputer. Rzadko kiedy mamy tylko jedno okno – zwykle są różne ekrany:
- ekran logowania,
- ekran główny,
- ekran ustawień,
- ekran szczegółów np. ucznia, produktu czy zdjęcia.
Nawigacja to właśnie mechanizm, który pozwala przejść z jednego ekranu (np. MainPage) do innego (np. MainPage2).
2. Rodzaje nawigacji w MAUI
a) NavigationPage – stos stron
To najprostszy sposób. Działa jak stos kartek:
PushAsync()→ dokładamy nową kartkę (nową stronę).PopAsync()→ wracamy do poprzedniej strony (ściągamy kartkę ze stosu).
Zastosowanie:
– kiedy chcesz mieć klasyczne „przejdź dalej / wróć” w aplikacji, np. lista uczniów → szczegóły ucznia.
Przykład:
// Przejście do nowej strony
await Navigation.PushAsync(new MainPage2());
// Powrót
await Navigation.PopAsync();
b) Modalna nawigacja (okno na wierzchu)
To tak, jakbyś otworzył pełnoekranowe okno dialogowe.
Masz stronę, i na nią „nakładasz” inną stronę. Dopóki jej nie zamkniesz, użytkownik nie wróci do poprzedniej.
Zastosowanie:
– formularze, logowanie, potwierdzenia, okna wyboru.
Przykład:
// Otwórz okno modalne
await Navigation.PushModalAsync(new MainPage2());
// Zamknij
await Navigation.PopModalAsync();
c) Shell (rekomendowany dziś standard w MAUI)
Shell działa jak mapa stron w aplikacji.
Możesz rejestrować trasy (adresy) do różnych ekranów i przechodzić do nich po nazwie.
Daje też menu boczne, dolne zakładki itp.
Zastosowanie:
– większe aplikacje: np. sklep internetowy → zakładki: „Produkty”, „Koszyk”, „Profil”.
– kiedy chcesz łatwo przekazywać dane między stronami.
Przykład rejestracji i przejścia:
// AppShell.xaml.cs
Routing.RegisterRoute(nameof(MainPage2), typeof(MainPage2));
// przejście do strony
await Shell.Current.GoToAsync(nameof(MainPage2));
Można też przekazać dane:
await Shell.Current.GoToAsync(nameof(MainPage2),
new Dictionary<string, object>
{
["Imie"] = "Ania",
["Wiek"] = 18
});
3. Przekazywanie danych (np. imię ucznia)
To ważne, bo często nie chodzi tylko o przejście, ale też o to, by druga strona wiedziała co ma wyświetlić.
- W NavigationPage przekazujemy dane przez konstruktor:
await Navigation.PushAsync(new MainPage2("Ania"));
- W Shell można przesłać parametry w słowniku i odebrać je na drugiej stronie przez atrybut
[QueryProperty].
4. Wiązanie danych (Binding)
Żeby nie pisać kodu „ręcznie” dla każdego pola, używa się Bindingu.
To znaczy: wartość z obiektu w kodzie automatycznie łączy się z kontrolką na ekranie.
Np. obiekt Uczen { Imie="Ania" } jest połączony z Label, który pokazuje to imię.
Jak zmienisz wartość w obiekcie – zmienia się też na ekranie.
Zastosowanie:
– gdy chcesz, by dane w aplikacji aktualizowały się automatycznie.
– przy większych projektach i egzaminie INF.04 to standard (MVVM).
Przykład prosty:
// Code-behind
BindingContext = new Uczen { Imie = "Ania" };
<!-- XAML -->
<Label Text="{Binding Imie}" />
5. Zrozumieć obrazowo
- NavigationPage: książka, gdzie przewracasz strony do przodu i do tyłu.
- Modal: wyskakujące okno „na wierzchu”, dopóki go nie zamkniesz, nie wrócisz.
- Shell: mapa całej aplikacji z adresami i drogowskazami – idealna do dużych projektów.
6. Najprostszy przykład do klasy
Masz dwie strony: MainPage i MainPage2.
MainPage.xaml:
<VerticalStackLayout Padding="20">
<Button Text="Przejdź do drugiej strony"
Clicked="OnGoClicked"/>
</VerticalStackLayout>
MainPage.xaml.cs:
private async void OnGoClicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new MainPage2("Witaj uczniu!"));
}
MainPage2.xaml.cs:
public partial class MainPage2 : ContentPage
{
public MainPage2(string tekst)
{
InitializeComponent();
LabelWiadomosc.Text = tekst;
}
}
MainPage2.xaml:
<VerticalStackLayout Padding="20">
<Label x:Name="LabelWiadomosc"/>
<Button Text="Wróć" Clicked="OnBackClicked"/>
</VerticalStackLayout>
MainPage2.xaml.cs – powrót:
private async void OnBackClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
Podsumowanie.
W .NET MAUI można przechodzić między stronami (oknami). Najprostszy sposób to użycie NavigationPage:
PushAsync()– przejście do nowej strony,PopAsync()– powrót do poprzedniej.
Dane można przekazać np. przez konstruktor drugiej strony.
To wystarczy na egzaminie INF.04, żeby pokazać nawigację i wymianę danych między oknami.
Zadanie 1
Zadanie:
Utwórz aplikację mobilną w .NET MAUI z dwiema stronami:
- MainPage – zawiera pole tekstowe (Entry) do wpisania imienia i przycisk „Przejdź dalej”.
- SecondPage – wyświetla wpisane imię oraz przycisk „Wróć”.
Przy przejściu z pierwszej do drugiej strony imię wpisane w polu tekstowym ma zostać przekazane i wyświetlone.
Rozwiązanie krok po kroku
1. Utwórz nową stronę. Dodaj do projektu plik SecondPage.xaml (ContentPage).
2. Ustaw NavigationPage w App.xaml.cs
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
3. Kod MainPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
x:Class="MauiApp.MainPage">
<VerticalStackLayout Padding="20">
<Entry x:Name="poleImie" Placeholder="Podaj imię"/>
<Button Text="Przejdź dalej" Clicked="OnGoClicked"/>
</VerticalStackLayout>
</ContentPage>
4. Kod MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void OnGoClicked(object sender, EventArgs e)
{
string imie = poleImie.Text ?? "";
await Navigation.PushAsync(new SecondPage(imie));
}
}
5. Kod SecondPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
x:Class="MauiApp.SecondPage">
<VerticalStackLayout Padding="20">
<Label x:Name="labelImie" FontSize="24"/>
<Button Text="Wróć" Clicked="OnBackClicked"/>
</VerticalStackLayout>
</ContentPage>
6. Kod SecondPage.xaml.cs
public partial class SecondPage : ContentPage
{
public SecondPage(string imie)
{
InitializeComponent();
labelImie.Text = $"Witaj {imie}!";
}
private async void OnBackClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
}
Efekt końcowy
- Na pierwszej stronie uczeń wpisuje swoje imię.
- Po kliknięciu przycisku otwiera się druga strona, gdzie imię jest wyświetlane w etykiecie.
- Kliknięcie przycisku „Wróć” przenosi z powrotem do pierwszej strony.
To jest najprostsze i wystarczające rozwiązanie na INF.04, bo pokazuje:
przekazanie danych między stronami (przez konstruktor).
odanie nowej strony
przejście między stronami (PushAsync, PopAsync),
Przekazanie danych bez konstruktora
Przebuduj program tak by ze strony SecondPage przekazał informacje do strony ThirdPage (Użytkownik na MainPage wpisuje imię, przechodzi do SecondPage, a tam klika przycisk, który otwiera ThirdPage i przekazuje do niej imię w inny sposób)
Sposób 1: Ustawienie właściwości strony (bez konstruktora)
ThirdPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NawigacjaApp.ThirdPage">
<VerticalStackLayout Padding="20">
<Label x:Name="labelDane" FontSize="24"/>
<Button Text="Wróć" Clicked="OnBackClicked"/>
</VerticalStackLayout>
</ContentPage>
ThirdPage.xaml.cs
namespace NawigacjaApp;
public partial class ThirdPage : ContentPage
{
// Właściwość do przekazania danych
public string? Imie { get; set; }
public ThirdPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
labelDane.Text = $"Dane przekazane: {Imie}";
}
private async void OnBackClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
}
SecondPage.xaml
Dodaj przycisk, który otworzy ThirdPage:
<VerticalStackLayout Padding="20">
<Label x:Name="labelImie" FontSize="24"/>
<Button Text="Przejdź do ThirdPage" Clicked="OnGoThirdPageClicked"/>
<Button Text="Wróć" Clicked="OnBackClicked"/>
</VerticalStackLayout>
SecondPage.xaml.cs
namespace NawigacjaApp;
public partial class SecondPage : ContentPage
{
private string _imie;
public SecondPage(string imie)
{
InitializeComponent();
_imie = imie;
labelImie.Text = $"Witaj {imie}!";
}
private async void OnBackClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
private async void OnGoThirdPageClicked(object sender, EventArgs e)
{
var third = new ThirdPage();
third.Imie = _imie; // przekazanie danych przez właściwość
await Navigation.PushAsync(third);
}
}
Jak to działa?
- Wpisujemy imię na MainPage i przechodzi do SecondPage.
- Na SecondPage jest przycisk „Przejdź do ThirdPage”.
- Po kliknięciu tworzona jest nowa strona ThirdPage, a wartość imienia przekazywana jest do niej poprzez właściwość
Imie. - W metodzie
OnAppearing()na ThirdPage wartość ta jest wyświetlana w etykiecie.
Porównanie dwóch sposobów
- Przez konstruktor (SecondPage): dane są przekazywane w momencie tworzenia strony.
- Przez właściwość (ThirdPage): najpierw tworzysz stronę, a potem ustawiasz jej dane.
Oba sposoby są proste i na egzaminie wystarczające.
***
Innym sposobem Przekazania danych pomiędzy stronami jest Binding opisany szerzej na mojej stronie w zakładce Binding
inny sposób” z użyciem bindingu i oddzielnej klasy modelu Dane.cs. Idea jest prosta: tworzysz jeden obiekt z danymi, ustawiasz go jako BindingContext w ThirdPage, a potem ten sam obiekt przekazujesz do FourthPage. Dzięki temu wszystko, co zmienisz na jednej stronie, jest od razu widoczne na drugiej (bo obie strony „patrzą” na ten sam model).
Poniżej kompletny, prosty przykład w stylu INF.04.
Założenia
Projekt: NawigacjaApp
Strony: ThirdPage.xaml, FourthPage.xaml
Model: Dane.cs (osobny plik)
1) Model z powiadamianiem o zmianach: Dane.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace NawigacjaApp;
public class Dane : INotifyPropertyChanged
{
private string imie = "";
private int wiek;
public string Imie
{
get => imie;
set { imie = value; OnPropertyChanged(); }
}
public int Wiek
{
get => wiek;
set { wiek = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
Wyjaśnienie: INotifyPropertyChanged sprawia, że gdy zmienisz Imie/Wiek w obiekcie, powiązane kontrolki w XAML automatycznie się odświeżą.
2) ThirdPage: wiążemy pola z modelem i nawigujemy dalej
ThirdPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NawigacjaApp.ThirdPage"
Title="ThirdPage">
<VerticalStackLayout Padding="20" Spacing="12">
<Label Text="Wpisz dane i przejdź dalej:" FontAttributes="Bold" />
<Entry Placeholder="Imię" Text="{Binding Imie}" />
<Entry Placeholder="Wiek" Keyboard="Numeric" Text="{Binding Wiek}" />
<Button Text="Przejdź do FourthPage"
Clicked="OnGoFourthClicked" />
</VerticalStackLayout>
</ContentPage>
ThirdPage.xaml.cs
namespace NawigacjaApp;
public partial class ThirdPage : ContentPage
{
private readonly Dane _dane;
public ThirdPage()
{
InitializeComponent();
// 1) Tworzymy obiekt modelu (możesz też go dostać z poprzedniej strony)
_dane = new Dane { Imie = "Ania", Wiek = 18 };
// 2) Ustawiamy model jako BindingContext tej strony
BindingContext = _dane;
}
private async void OnGoFourthClicked(object sender, EventArgs e)
{
// 3) Przekazujemy TEN SAM obiekt do FourthPage
await Navigation.PushAsync(new FourthPage(_dane));
}
}
3) FourthPage: używamy tego samego BindingContext
FourthPage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NawigacjaApp.FourthPage"
Title="FourthPage">
<VerticalStackLayout Padding="20" Spacing="12">
<Label Text="Dane z ThirdPage (ten sam model):" FontAttributes="Bold" />
<Label Text="Imię:" />
<Label Text="{Binding Imie}" FontSize="20" />
<Label Text="Wiek:" />
<Label Text="{Binding Wiek}" FontSize="20" />
<!-- pokażmy też, że tu można edytować te same dane -->
<Entry Placeholder="Zmień imię (zobacz, że działa dwukierunkowo)"
Text="{Binding Imie}" />
<Button Text="Wróć"
Clicked="OnBackClicked" />
</VerticalStackLayout>
</ContentPage>
FourthPage.xaml.cs
namespace NawigacjaApp;
public partial class FourthPage : ContentPage
{
// Dostajemy TEN SAM obiekt Dane
public FourthPage(Dane dane)
{
InitializeComponent();
BindingContext = dane; // kluczowe
}
private async void OnBackClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
}
Jak to działa (w skrócie dla ucznia)
ThirdPagetworzy obiektDanei ustawia go jakoBindingContext.- Pola
Entrysą połączone z właściwościamiImieiWiek. - Przycisk przejścia tworzy
FourthPage, przekazując ten sam obiektDane. FourthPageteż ustawia ten obiekt jakoBindingContext, więc widzi te same wartości.- Jeśli zmienisz imię na
FourthPage, po powrocie doThirdPagezobaczysz aktualizację — to zasługaINotifyPropertyChangedi tego, że obie strony „patrzą” na ten sam obiekt.
To jest „inny sposób” niż:
• konstruktor z prostym stringiem,
• ustawienie pojedynczej właściwości strony,
• Shell + QueryProperty.
Tu pracujemy na wspólnym modelu i bindingu, co jest bardzo czytelne i „egzaminowe”.
Bonus: wariant bez przekazywania w konstruktorze (singleton)
Jeśli chcesz nie przekazywać modelu w konstruktorze, możesz zarejestrować Dane jako singleton w MauiProgram.cs i wstrzyknąć go w obu stronach:
MauiProgram.cs
builder.Services.AddSingleton<Dane>(); // model współdzielony
builder.Services.AddTransient<ThirdPage>();
builder.Services.AddTransient<FourthPage>();
ThirdPage.xaml.cs
public ThirdPage(Dane dane)
{
InitializeComponent();
BindingContext = dane;
}
FourthPage.xaml.cs
public FourthPage(Dane dane)
{
InitializeComponent();
BindingContext = dane; // ten sam singleton
}
Nawigacja wtedy prosto:
// gdzieś: await Navigation.PushAsync(serviceProvider.GetRequiredService<FourthPage>());
Navigacja z AppShell
Założenia projektu
Nazwa: NawigacjaShellApp
Strony: MainPage.xaml, SecondPage.xaml
Nawigacja: Shell (GoToAsync) + przekazywanie parametrów
1) Utworzenie projektu
Visual Studio → Nowy projekt → .NET MAUI App → nazwa NawigacjaShellApp.
Domyślnie projekt ma AppShell.xaml i jest ustawiony jako start (w App.xaml.cs).
2) AppShell – mapowanie stron i tras
AppShell.xaml – dodaj pozycję dla MainPage (start) i zarejestruj SecondPage jako trasę:
<?xml version="1.0" encoding="utf-8" ?>
<Shell
x:Class="NawigacjaShellApp.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:NawigacjaShellApp">
<!-- Startowa zakładka/pozycja menu -->
<ShellContent
Title="Start"
ContentTemplate="{DataTemplate local:MainPage}" />
</Shell>
AppShell.xaml.cs – rejestracja trasy do SecondPage:
namespace NawigacjaShellApp;
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(SecondPage), typeof(SecondPage));
}
}
3) MainPage – wejście i przejście do SecondPage z parametrem
MainPage.xaml:
<ContentPage
x:Class="NawigacjaShellApp.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="MainPage">
<VerticalStackLayout Padding="20" Spacing="12">
<Entry x:Name="poleImie" Placeholder="Podaj imię" />
<Button Text="Idź do SecondPage" Clicked="OnGoClicked" />
</VerticalStackLayout>
</ContentPage>
MainPage.xaml.cs:
namespace NawigacjaShellApp;
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void OnGoClicked(object sender, EventArgs e)
{
var imie = poleImie.Text ?? "";
// Przekazywanie parametru "Imie" do SecondPage
await Shell.Current.GoToAsync(nameof(SecondPage), new Dictionary<string, object>
{
["Imie"] = imie
});
}
}
4) SecondPage – odbiór parametru przez QueryProperty (najprościej)
SecondPage.xaml:
<ContentPage
x:Class="NawigacjaShellApp.SecondPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="SecondPage">
<VerticalStackLayout Padding="20" Spacing="12">
<Label Text="Odebrane dane:" FontAttributes="Bold" />
<Label Text="{Binding Imie}" FontSize="24" />
<Button Text="Wróć" Clicked="OnBackClicked" />
</VerticalStackLayout>
</ContentPage>
SecondPage.xaml.cs:
namespace NawigacjaShellApp;
// Powiedz Shellowi: wstrzyknij wartość parametru "Imie" do właściwości Imie
[QueryProperty(nameof(Imie), "Imie")]
public partial class SecondPage : ContentPage
{
private string imie = "";
public string Imie
{
get => imie;
set
{
imie = value;
OnPropertyChanged(); // żeby Binding w Label się odświeżył
}
}
public SecondPage()
{
InitializeComponent();
BindingContext = this; // szybkie podejście na start
}
private async void OnBackClicked(object sender, EventArgs e)
{
// W Shell wracamy tak samo: ".." = o jeden ekran do tyłu
await Shell.Current.GoToAsync("..");
}
}
To wszystko. Masz AppShell, trasę, przejście i odbiór danych.
5) Alternatywny odbiór parametrów: IQueryAttributable
Jeśli wolisz ręcznie obsłużyć słownik parametrów (np. kilka na raz albo obiekt), możesz zamiast [QueryProperty] użyć interfejsu:
public partial class SecondPage : ContentPage, IQueryAttributable
{
public string Imie { get; set; } = "";
public SecondPage()
{
InitializeComponent();
BindingContext = this;
}
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.TryGetValue("Imie", out var val) && val is string s)
{
Imie = s;
OnPropertyChanged(nameof(Imie));
}
}
private async void OnBackClicked(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("..");
}
}
6) Przekazywanie bardziej złożonych danych (model)
Shell wygodnie przenosi typy proste (string, int itp.). Dla obiektów masz dwa wyjścia:
- Słownik w
GoToAsync(działa, dopóki nawigujesz w ramach tej samej instancji aplikacji):var model = new Uczen { Imie = "Ania", Klasa = "1TI" }; await Shell.Current.GoToAsync(nameof(SecondPage), new Dictionary<string, object> { ["ModelUczen"] = model });Odbiór:[QueryProperty(nameof(ModelUczen), "ModelUczen")] public Uczen ModelUczen { get; set; } - Wspólny model przez DI (singleton) – strony korzystają z tej samej instancji (polecane przy większych apkach).
Kiedy wybrać Shell
Gdy:
- planujesz menu boczne (Flyout) lub dolne zakładki (Tabs),
- chcesz proste, czytelne trasy i deeplinki,
- łatwo przekazywać parametry i wracać („..”).
Krótka treść zadania (styl INF.04)
Projekt: NawigacjaShellApp
Wymagania:
- W
AppShellzarejestruj trasę doSecondPage. - W
MainPagewpisz imię i przejdź doSecondPageprzyciskiem. SecondPagema odebrać parametrImiei wyświetlić go w etykiecie.- Dodaj przycisk „Wróć” (powrót do
MainPage).
Zaliczenie: działa nawigacja Shell i przekazywanie parametru.

