Lekcja 9. Obsługa zdarzeń — anatomia handlera i okna DisplayAlert
ŚredniPo co się tego uczymy?
W aplikacji mobilnej mamy dwie części: XAML (wygląd) i C# (logika). Żeby aplikacja zareagowała na kliknięcie czy wpisanie tekstu, trzeba połączyć kontrolkę z metodą w C# — to złączenie zdarzenia z metodą. Ta lekcja rozkłada na czynniki pierwsze anatomię takiej metody i pokazuje dwa najważniejsze okna dialogowe: DisplayAlert i DisplayPromptAsync.
Teoria
Czym jest złączenie zdarzenia?
W XAML: <Button Text="Kliknij mnie" Clicked="PrzyciskKlik_Click"/>. W C#: metoda o dokładnie tej samej nazwie, wykonywana po kliknięciu. Złączenie = wskazanie, że dana kontrolka ma uruchomić konkretną metodę, gdy wydarzy się określone zdarzenie (np. Clicked).
Anatomia metody obsługującej zdarzenie
Weźmy przykład: private async void Zaloguj_Click(object sender, EventArgs e). Rozbijmy to na części:
| Element | Znaczenie |
|---|---|
private |
Metoda widoczna tylko w tej klasie — wystarcza w naszych projektach. |
async |
Pozwala użyć await — np. czekać na wyświetlenie okna dialogowego. Dzięki temu aplikacja się NIE zawiesza. |
void |
Metoda nic nie zwraca — handlery zdarzeń prawie zawsze są void. |
Zaloguj_Click |
Nazwa metody — konwencja: nazwa kontrolki + rodzaj zdarzenia (np. Przycisk_Click, Suwak_ValueChanged). |
object sender |
Obiekt, który wywołał zdarzenie — np. konkretny przycisk, który kliknięto. |
EventArgs e |
Dodatkowe dane o zdarzeniu — dla różnych kontrolek różne typy (np. ValueChangedEventArgs dla suwaka, DateChangedEventArgs dla DatePickera). |
DisplayAlert — komunikat dla użytkownika
await DisplayAlert("Tytuł", "Treść", "OK") pokazuje proste okno z komunikatem i jednym przyciskiem. Wersja z DWOMA przyciskami (DisplayAlert("Tytuł", "Treść", "Tak", "Nie")) zwraca bool — true, gdy kliknięto pierwszy przycisk ("Tak"), false, gdy drugi ("Nie"). To najczęstszy sposób pytania użytkownika o potwierdzenie ważnej akcji (np. usunięcia danych).
DisplayPromptAsync — szybkie okno wprowadzania danych
string wynik = await DisplayPromptAsync("Tytuł", "Treść pytania") to odpowiednik klasycznego "InputBox" — otwiera okno z polem tekstowym i zwraca wpisany string (albo null, gdy użytkownik anuluje).
Dlaczego async/await w interfejsie użytkownika?
Operacje takie jak DisplayAlert czy DisplayPromptAsync są asynchroniczne — nie blokują wątku odpowiedzialnego za rysowanie interfejsu. Dzięki async/await aplikacja pozostaje płynna, a użytkownik widzi okno dialogowe natychmiast, zamiast czekać na "zamrożoną" aplikację.
Dobra praktyka: opakowuj kod w handlerze w try/catch, żeby ewentualny błąd pokazał krótki komunikat zamiast wywalić całą aplikację:
private async void Handler(object sender, EventArgs e)
{
try
{
await DisplayAlert("Tytuł", "Treść", "OK");
}
catch (Exception ex)
{
await DisplayAlert("Błąd", ex.Message, "OK");
}
}
Kiedy sender, a kiedy x:Name?
Masz jedną kontrolkę i używasz jej często → nadaj jej x:Name i odwołuj się po nazwie (prościej, czytelniej). Jeden wspólny handler dla WIELU kontrolek → czytaj sender i rzutuj do właściwego typu: if (sender is Button b) b.Text = "OK";
Schemat
Anatomia metody obslugujacej zdarzenie: private async void Zaloguj_Click (object sender, EventArgs e) | | | | | | dostep pozwala nic nie nazwa: kto wywolal dane o tylko uzyc zwraca kontrolka_ zdarzenie zdarzeniu w klasie await Zdarzenie (np. Button)
Przykład z życia
Kliknięcie "Usuń konto" w dowolnej aplikacji ZAWSZE pokazuje okno potwierdzenia ("Czy na pewno chcesz usunąć konto? Tak/Nie") — to dokładnie DisplayAlert z dwoma przyciskami. Gdyby aplikacja usuwała dane od razu po jednym kliknięciu, bez potwierdzenia, byłby to poważny błąd projektowy UX.
PotwierdzeniePage.xaml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ZdarzeniaApp.PotwierdzeniePage"
Title="Potwierdzenie">
<VerticalStackLayout Padding="20" Spacing="12">
<Label Text="Potwierdzenie usunięcia" FontSize="20" />
<Button Text="Usuń element" Clicked="Usun_Click" />
<Button Text="Podaj swoje imię" Clicked="PodajImie_Click" />
<Label x:Name="StatusLabel" FontSize="16" />
</VerticalStackLayout>
</ContentPage>
PotwierdzeniePage.xaml.cs
namespace ZdarzeniaApp;
public partial class PotwierdzeniePage : ContentPage
{
public PotwierdzeniePage() => InitializeComponent();
private async void Usun_Click(object sender, EventArgs e)
{
// DisplayAlert z dwoma przyciskami zwraca bool:
// true = kliknieto pierwszy ("Tak"), false = drugi ("Nie")
bool decyzja = await DisplayAlert(
"Usuwanie", "Czy na pewno chcesz usunąć?", "Tak", "Nie");
StatusLabel.Text = decyzja ? "Usunięto." : "Anulowano.";
}
private async void PodajImie_Click(object sender, EventArgs e)
{
// DisplayPromptAsync to szybkie okno wprowadzania tekstu
string imie = await DisplayPromptAsync("Wpisz imię", "Podaj swoje imię:");
if (!string.IsNullOrWhiteSpace(imie))
StatusLabel.Text = $"Witaj, {imie}!";
}
}
Komentarz i wyjaśnienie kodu
Usun_Click pokazuje wzorzec potwierdzenia akcji: DisplayAlert z dwoma przyciskami ("Tak"/"Nie") zwraca bool, który wykorzystujemy w operatorze warunkowym ?:, żeby ustawić odpowiedni komunikat w StatusLabel bez pisania pełnego if/else.
PodajImie_Click pokazuje DisplayPromptAsync — zwraca wpisany tekst jako string. Sprawdzamy !string.IsNullOrWhiteSpace(imie), żeby nie wypisać pustego powitania, gdy użytkownik kliknie "Anuluj" (wtedy metoda zwraca null) albo nic nie wpisze.
Obie metody są oznaczone async void — bo obie korzystają z await przy wywołaniu okna dialogowego.
Ćwiczenie samodzielne
Utwórz stronę z powyższym kodem. Sprawdź działanie obu przycisków — kliknij "Tak" i "Nie" w oknie potwierdzenia, a potem wpisz imię (i osobno spróbuj kliknąć "Anuluj") w oknie DisplayPromptAsync.
Zadania do pracy własnej
Zmień tekst pytania w DisplayAlert na własny (np. dotyczący usunięcia zdjęcia zamiast 'elementu').
Dodaj trzeci przycisk 'Wyloguj', który pokazuje DisplayAlert z komunikatem 'Zostałeś wylogowany' i JEDNYM przyciskiem 'OK' (bez pytania o potwierdzenie).
Zbuduj mini-quiz: przycisk 'Zadaj pytanie' pokazuje DisplayPromptAsync z pytaniem matematycznym (np. 'Ile to 7 razy 8?'), a po otrzymaniu odpowiedzi (przez int.TryParse) sprawdza, czy jest poprawna, i pokazuje DisplayAlert z komunikatem 'Brawo!' albo 'Niestety, spróbuj ponownie' — wszystko opakowane w try/catch na wypadek niepoprawnego formatu odpowiedzi.
Typowe błędy
Brak async przy używaniu await — jeśli metoda korzysta z await, musi być oznaczona async, inaczej kod się nie skompiluje.
Brak sprawdzenia null po DisplayPromptAsync — gdy użytkownik kliknie "Anuluj", metoda zwraca null; użycie tego wyniku bez sprawdzenia (np. wywołanie na nim metody) rzuci wyjątek.
Niezgodność nazwy metody w XAML i code-behind — literówka w Clicked="Usun_Click" względem faktycznej nazwy metody w C# uniemożliwi kompilację.
Blokujące operacje bez await — próba wykonania czegoś czasochłonnego bez async/await zamrozi interfejs użytkownika na czas trwania operacji.
Nawiązanie do egzaminu zawodowego
To realizacja INF.04.6.2 w zakresie łączenia interfejsu z logiką aplikacji. Umiejętność poprawnego napisania handlera zdarzenia (sygnatura, async/await, obsługa DisplayAlert) jest sprawdzana niemal w każdym zadaniu praktycznym z aplikacją mobilną na egzaminie.