Dataczwartek, 13 sierpnia 2026 Czas19:06:42
← Aplikacje mobilne (.NET MAUI)

Lekcja 8. Kontrolki III — ProgressBar, ActivityIndicator, CollectionView, ListView

Średni

Po co się tego uczymy?

Ta lekcja kończy przegląd kontrolek MAUI: dwie kontrolki informujące o trwającej operacji (ProgressBar, ActivityIndicator) i dwie kontrolki list — CollectionView (nowoczesna, zalecana) i ListView (starsza, wciąż spotykana). Pełne, praktyczne użycie list z bindowaniem danych poznasz w osobnej, dedykowanej lekcji dalej w tym dziale — tu poznajesz same kontrolki i ich właściwości.

Teoria

ProgressBar — pasek postępu

Właściwość Opis
Progress Wartość od 0.0 do 1.0 — aktualny stan postępu.
ProgressColor Kolor paska — może nieść znaczenie (zielony = OK, czerwony = błąd).

Metoda ProgressTo() wywołana w kodzie C# animuje płynną zmianę wartości zamiast skoku — daje ładniejszy efekt wizualny.

ActivityIndicator — animowany spinner

Właściwość Opis
IsRunning Włącza animację "kręcącego się kółka" — informacja "coś się dzieje".
IsVisible Pokazuje/ukrywa spinner — zwykle łączony z IsRunning.
Color Kolor spinnera — spójność z resztą aplikacji.

ProgressBar używaj, gdy ZNASZ dokładny postęp operacji (np. wgrano 3 z 10 plików); ActivityIndicator, gdy NIE wiesz, ile potrwa operacja (np. czekanie na odpowiedź serwera).

CollectionView — nowoczesna lista/siatka elementów

Właściwość Opis
ItemsSource Źródło danych — najczęściej kolekcja obiektów podpięta z kodu C#.
ItemTemplate Wygląd pojedynczego elementu listy — pełna kontrola nad kartą/wierszem.
ItemsLayout VerticalList, HorizontalList albo siatka (GridItemsLayout).
SelectionMode None/Single/Multiple — dopasowanie sposobu wyboru elementów.
EmptyView Co pokazać, gdy lista jest pusta — ważne dla dobrego doświadczenia użytkownika.

ListView — starsza kontrolka listy

Poprzedniczka CollectionView. W NOWYCH projektach zawsze wybieraj CollectionView (jest wydajniejszy i bardziej elastyczny) — ListView możesz spotkać w starszych zadaniach czy materiałach, więc warto rozpoznawać jego podstawowe właściwości: ItemsSource, ItemTemplate, SelectedItem, SeparatorVisibility/SeparatorColor (linie między wierszami), HasUnevenRows (różna wysokość wierszy).

Schemat

ProgressBar vs ActivityIndicator:

ProgressBar        -> ZNAM dokladny postep (np. wyslano 6 z 10 zdjec)
ActivityIndicator  -> NIE WIEM ile potrwa (np. czekam na serwer)

CollectionView (nowa) vs ListView (stara):
  Nowe projekty -> zawsze CollectionView
  Stare zadania/materialy -> mozesz spotkac ListView

Przykład z życia

Podczas wysyłania zdjęcia w komunikatorze widzisz pasek postępu wypełniający się procentowo (ProgressBar) — bo aplikacja zna dokładny rozmiar pliku. Natomiast po naciśnięciu "Wyślij wiadomość" na chwilę pojawia się kręcące kółko (ActivityIndicator) — bo aplikacja nie wie z góry, ile serwer będzie odpowiadał.

PostepPage.xaml

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="KontrolkiApp3.PostepPage">

    <VerticalStackLayout Padding="20" Spacing="16">

        <Label Text="Wysyłanie pliku:" FontAttributes="Bold" />
        <ProgressBar x:Name="PasekPostepu" Progress="0" />
        <Button Text="Rozpocznij wysyłanie" Clicked="RozpocznijWysylanie_Clicked" />

        <BoxView HeightRequest="1" Color="LightGray" />

        <Label Text="Ładowanie danych z serwera:" FontAttributes="Bold" />
        <ActivityIndicator x:Name="Spinner" IsRunning="False" Color="RoyalBlue" />
        <Button Text="Symuluj pobieranie" Clicked="SymulujPobieranie_Clicked" />

    </VerticalStackLayout>
</ContentPage>

PostepPage.xaml.cs

namespace KontrolkiApp3;

public partial class PostepPage : ContentPage
{
    public PostepPage()
    {
        InitializeComponent();
    }

    private async void RozpocznijWysylanie_Clicked(object sender, EventArgs e)
    {
        // ProgressTo animuje plynne przejscie od aktualnej wartosci do 1.0
        // w ciagu 2000 milisekund (2 sekundy)
        await PasekPostepu.ProgressTo(1.0, 2000, Easing.Linear);
    }

    private async void SymulujPobieranie_Clicked(object sender, EventArgs e)
    {
        Spinner.IsRunning = true;

        // Symulacja oczekiwania na odpowiedz serwera (bez blokowania UI)
        await Task.Delay(2000);

        Spinner.IsRunning = false;
        await DisplayAlert("Gotowe", "Dane zostały pobrane.", "OK");
    }
}

Komentarz i wyjaśnienie kodu

PasekPostepu.ProgressTo(1.0, 2000, Easing.Linear) to metoda ANIMUJĄCA — zamiast skokowo ustawić Progress = 1.0, płynnie zwiększa wartość przez 2000 milisekund, dając efekt realistycznego "wypełniania się" paska. To metoda asynchroniczna (zwraca Task), dlatego wywołanie poprzedzamy await.

W drugiej metodzie włączamy Spinner.IsRunning = true, czekamy sztucznie 2 sekundy przez await Task.Delay(2000) (w prawdziwej aplikacji byłoby to np. await httpClient.GetAsync(...) — poznasz w lekcji o REST API), a na końcu wyłączamy spinner i pokazujemy komunikat. Użycie await Task.Delay zamiast Thread.Sleep jest kluczowe — dzięki temu interfejs NIE zamarza podczas oczekiwania.

Ćwiczenie samodzielne

Utwórz stronę z powyższym kodem. Sprawdź, czy pasek postępu płynnie się wypełnia po kliknięciu pierwszego przycisku, a spinner kręci się przez około 2 sekundy po kliknięciu drugiego, po czym pokazuje się komunikat.

Zadania do pracy własnej

  1. Zmień czas trwania animacji ProgressTo z 2000 na 5000 milisekund i zaobserwuj różnicę.

  2. Dodaj drugi ProgressBar i przycisk 'Zresetuj', który natychmiast (bez animacji) ustawia oba paski z powrotem na 0.

  3. Zbuduj symulację pobierania listy plików: ActivityIndicator uruchamia się na starcie, po 'pobraniu' (Task.Delay) wyłącza się i pokazuje CollectionView z zaszytą na sztywno listą 5 nazw plików (string). Jeśli lista byłaby pusta, CollectionView powinien pokazać EmptyView z tekstem 'Brak plików do wyświetlenia'.

Typowe błędy

Użycie Thread.Sleep zamiast Task.Delay — Thread.Sleep blokuje wątek interfejsu, aplikacja "zamarza" na czas oczekiwania.

Zapominanie o wyłączeniu ActivityIndicator po zakończeniu operacji — spinner kręcący się w nieskończoność sugeruje użytkownikowi, że coś się zawiesiło.

Używanie ListView w nowym projekcie bez wyraźnego powodu — CollectionView jest wydajniejszy i bardziej elastyczny; ListView wybieraj tylko, gdy pracujesz ze starszym, istniejącym kodem.

Brak EmptyView w CollectionView — gdy lista jest pusta (np. brak wyników wyszukiwania), pusty ekran bez żadnego komunikatu wygląda jak błąd aplikacji.

Nawiązanie do egzaminu zawodowego

To dopełnienie realizacji INF.04.6.2 pełnym zestawem kontrolek UI wymaganych w podstawie programowej. ProgressBar i ActivityIndicator są niezbędne przy operacjach sieciowych (REST API, GPS) omawianych w dalszych lekcjach, a CollectionView to podstawa każdej listy danych w aplikacji mobilnej.