Dataczwartek, 13 sierpnia 2026 Czas18:20:27
← Aplikacje mobilne (.NET MAUI)

Lekcja 10. Obsługa zdarzeń kontrolek — Switch, Slider, Picker, Date/TimePicker

Średni

Po co się tego uczymy?

Każda kontrolka wyboru ma swoje WŁASNE zdarzenie i swój WŁASNY typ EventArgs z konkretnymi danymi. Ta lekcja systematycznie przechodzi przez zdarzenia Switch, Slider, Stepper, Picker, DatePicker, TimePicker i SearchBar — a na koniec pokazuje "wzorzec egzaminacyjny" łączący to wszystko w jeden przykład logowania.

Teoria

Tabela zdarzeń poszczególnych kontrolek

Kontrolka Zdarzenie Typ EventArgs / dane
Switch Toggled ToggledEventArgse.Value to nowy stan (bool).
Slider ValueChanged ValueChangedEventArgse.NewValue (double), czasem też e.OldValue.
Stepper ValueChanged ValueChangedEventArgse.NewValue (double).
DatePicker DateSelected DateChangedEventArgse.NewDate (DateTime).
TimePicker PropertyChanged (brak własnego eventu) PropertyChangedEventArgs — trzeba sprawdzić e.PropertyName == nameof(TimePicker.Time).
Picker SelectedIndexChanged Zwykłe EventArgs — wartość odczytujesz z SelectedItem/SelectedIndex kontrolki.
SearchBar SearchButtonPressed Zwykłe EventArgs — wartość odczytujesz z Text kontrolki (albo z sender).

Przykłady zdarzeń — Switch, Slider, DatePicker

// Switch - tryb nocny
private void TrybNocny_Toggled(object sender, ToggledEventArgs e)
{
    BackgroundColor = e.Value ? Colors.Black : Colors.White;
}

// Slider - odczyt wartosci
private void JasnoscSlider_ValueChanged(object sender, ValueChangedEventArgs e)
{
    JasnoscLabel.Text = $"Jasność: {e.NewValue:F0}%";
}

// DatePicker - wybor daty
private void DataWybor_DateSelected(object sender, DateChangedEventArgs e)
{
    WynikLabel.Text = $"Wybrana data: {e.NewDate:d}";
}

Szczególny przypadek: TimePicker

W przeciwieństwie do DatePickera, TimePicker NIE MA własnego zdarzenia w rodzaju "TimeSelected" — trzeba nasłuchiwać ogólnego PropertyChanged i sprawdzić, czy zmieniła się WŁAŚNIE właściwość Time:

private void GodzinaWybor_PropertyChanged(object sender,
    System.ComponentModel.PropertyChangedEventArgs e)
{
    if (e.PropertyName == nameof(TimePicker.Time))
        WynikLabel.Text = $"Wybrana godzina: {GodzinaWybor.Time}";
}

Użycie nameof(TimePicker.Time) zamiast wpisania na sztywno stringa "Time" to dobry nawyk — kompilator sam sprawdzi poprawność pisowni.

SearchBar — wyszukiwanie

private void SearchBar_SearchButtonPressed(object sender, EventArgs e)
{
    var pasekWyszukiwania = (SearchBar)sender;
    WynikLabel.Text = $"Wyszukano: {pasekWyszukiwania.Text}";
}

Tu sender rzutujemy na konkretny typ (SearchBar), żeby odczytać jego właściwość Text — przydatne, gdy chcemy uniknąć dodatkowego x:Name.

Minimalny "wzorzec egzaminacyjny"

Formularz logowania łączący nazwane kontrolki, odczyt wartości, handler, async/await, komunikat i prostą walidację pokazuje wszystko, czego zwykle oczekuje się w zadaniu egzaminacyjnym z obsługą zdarzeń — zobacz pełny przykład w sekcji kodu tej lekcji.

Schemat

Ktore zdarzenie i jaki typ danych dla kazdej kontrolki:

Switch      -> Toggled              -> e.Value (bool)
Slider      -> ValueChanged         -> e.NewValue (double)
Stepper     -> ValueChanged         -> e.NewValue (double)
DatePicker  -> DateSelected         -> e.NewDate (DateTime)
TimePicker  -> PropertyChanged      -> sprawdz e.PropertyName == nameof(TimePicker.Time)
Picker      -> SelectedIndexChanged -> czytaj z SelectedItem kontrolki
SearchBar   -> SearchButtonPressed  -> czytaj z Text kontrolki

Przykład z życia

Aplikacja pogodowa, w której przesuwasz suwak czasu, żeby zobaczyć prognozę na różne godziny dnia, wykorzystuje dokładnie zdarzenie ValueChanged Slidera — za każdym przesunięciem palca aplikacja aktualizuje wyświetlaną temperaturę i ikonę pogody na podstawie e.NewValue.

LogowaniePage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ZdarzeniaApp2.LogowaniePage"
             Title="Logowanie">

    <VerticalStackLayout Padding="20" Spacing="12">
        <Entry x:Name="PoleLogin" Placeholder="Login" />
        <Entry x:Name="PoleHaslo" Placeholder="Hasło" IsPassword="True" />

        <HorizontalStackLayout Spacing="10">
            <Switch x:Name="PrzelacznikZapamietaj" Toggled="Zapamietaj_Toggled" />
            <Label Text="Zapamiętaj mnie" VerticalOptions="Center" />
        </HorizontalStackLayout>

        <Button Text="Zaloguj" Clicked="Zaloguj_Click" />
        <Label x:Name="StatusLabel" />
    </VerticalStackLayout>
</ContentPage>

LogowaniePage.xaml.cs

namespace ZdarzeniaApp2;

public partial class LogowaniePage : ContentPage
{
    private bool _zapamietajZaznaczone = false;

    public LogowaniePage() => InitializeComponent();

    private void Zapamietaj_Toggled(object sender, ToggledEventArgs e)
    {
        // e.Value to aktualny stan przelacznika (true/false)
        _zapamietajZaznaczone = e.Value;
    }

    private async void Zaloguj_Click(object sender, EventArgs e)
    {
        string login = PoleLogin.Text?.Trim();
        string haslo = PoleHaslo.Text;

        if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(haslo))
        {
            await DisplayAlert("Błąd", "Uzupełnij login i hasło.", "OK");
            return;
        }

        // Prosta "logika" logowania - w prawdziwej aplikacji tu bylby
        // dostep do bazy danych albo zapytanie do serwera (REST API)
        bool ok = (login == "admin" && haslo == "1234");

        StatusLabel.Text = ok
            ? $"Zalogowano{(_zapamietajZaznaczone ? " (zapamiętano)" : "")}"
            : "Nieprawidłowe dane";
    }
}

Komentarz i wyjaśnienie kodu

To jest właśnie "wzorzec egzaminacyjny" — pokazuje WSZYSTKO, czego zwykle oczekuje zadanie z obsługą zdarzeń: nazwane kontrolki (x:Name), odczyt wartości z pól tekstowych, osobny handler dla przełącznika (Zapamietaj_Toggled) zapamiętujący stan w polu prywatnym klasy, główny handler async z walidacją pustych pól, prosty komunikat błędu przez DisplayAlert i finalną zmianę tekstu w zależności od wyniku.

Zwróć uwagę na pole prywatne _zapamietajZaznaczone — przechowuje stan między wywołaniami różnych metod tej samej klasy. To prosty, ale ważny wzorzec: zdarzenie jednej kontrolki (Switch) wpływa na zachowanie zupełnie innej metody (obsługa przycisku Zaloguj).

Ćwiczenie samodzielne

Utwórz stronę z powyższym kodem. Zaloguj się loginem "admin" i hasłem "1234" — raz z zaznaczonym przełącznikiem "Zapamiętaj mnie", raz bez — i sprawdź, czy komunikat się różni.

Zadania do pracy własnej

  1. Dodaj Slider (zakres 0-100) reprezentujący 'siłę hasła' i etykietę pokazującą jego wartość na bieżąco.

  2. Dodaj DatePicker 'Data urodzenia' z DateSelected wypisującym wybraną datę, oraz Picker z listą 3 pytań pomocniczych do odzyskiwania hasła (SelectedIndexChanged wypisujący wybrane pytanie).

  3. Zbuduj formularz rejestracji z: TimePicker 'Preferowana godzina kontaktu' (obsłuż przez PropertyChanged i nameof), SearchBar do wyszukania miasta z zaszytej na sztywno listy 5 miast (SearchButtonPressed pokazujący, czy miasto jest na liście), oraz Stepper 'Wiek' (zakres 13-99) z etykietą aktualizowaną na bieżąco. Po kliknięciu 'Zarejestruj' wszystkie dane powinny się pojawić w jednym DisplayAlert jako podsumowanie.

Typowe błędy

Szukanie nieistniejącego zdarzenia TimeSelected — TimePicker nie ma takiego zdarzenia; trzeba użyć PropertyChanged i sprawdzić nazwę właściwości.

Odczyt e.NewValue jako int bez rzutowania — Slider i Stepper przekazują wartości typu double; przypisanie wprost do zmiennej int wymaga jawnego rzutowania albo zaokrąglenia.

Zapominanie o polu prywatnym do przechowania stanu między zdarzeniami — jeśli jeden handler potrzebuje wiedzieć, co ustawił inny (jak w przykładzie z Switch i przyciskiem logowania), potrzebne jest pole klasy, a nie zmienna lokalna wewnątrz jednej metody.

Wpisywanie stringa "Time" na sztywno zamiast nameof(TimePicker.Time) — działa, ale jest podatne na literówki niewykrywalne przez kompilator.

Nawiązanie do egzaminu zawodowego

To dopełnienie realizacji INF.04.6.2 w zakresie obsługi zdarzeń różnych kontrolek. Znajomość SPECYFICZNYCH nazw zdarzeń i typów EventArgs (Toggled/ValueChanged/DateSelected/PropertyChanged) jest regularnie sprawdzana w zadaniach egzaminacyjnych wymagających reagowania na konkretną kontrolkę.