Aplikacje mobilne · Kontrolki
Kontrolki III: ProgressBar, ActivityIndicator, CollectionView i ListView
Dwie kontrolki informują, że coś trwa (ProgressBar, ActivityIndicator), a dwie wyświetlają listy (CollectionView, starsza ListView). To dopełnia pełny zestaw kontrolek UI i przygotowuje do lekcji o sieci, GPS i listach danych.
- około 50 minut
- .NET MAUI · ProgressBar · ActivityIndicator · CollectionView
- INF.04.6.2
Po tej lekcji: pokazać postęp operacji (ProgressTo), użyć spinnera przy operacjach o nieznanym czasie, nie blokować interfejsu (Task.Delay) i wyświetlić listę w CollectionView z EmptyView.
Zobacz, zanim zaczniesz
Jak aplikacja mówi: pracuję
Użytkownik musi widzieć, że aplikacja żyje. Poniżej trzy sposoby, każdy z kodem XAML.
Laboratorium · ćwiczenie w przeglądarce, do którego odsyłają rozgrzewki na końcu lekcji
Zacznij od zabawy
Postęp, spinner i lista
Uruchom ProgressTo z różnym czasem. Potem „pobieranie” z Task.Delay i z Thread.Sleep, klikając w tym czasie licznik obok przycisku. Na końcu zmień układ listy i opróżnij ją, aby zobaczyć EmptyView.
Najpierw przewidź, potem sprawdź: Uruchom symulację postępu i porównaj 0.5 w programie z 50% dla człowieka. Spinner oznacza pracę, ale nie podaje procentu wykonania.
await Task.Delay oddaje sterowanie interfejsowi, a Thread.Sleep blokuje wątek UI, więc aplikacja „zamarza”. Model pomaga zobaczyć mechanizm; właściwą aplikację uruchomisz z plików poniżej.
Etap 1 z 4
ProgressBar: kiedy znasz postęp
ProgressBar pokazuje, ile już zrobiono. Właściwość Progress przyjmuje liczbę od 0.0 do 1.0, a nie od 0 do 100. Wartość 0.75 to 75%.
2 · Informowanie o operacji
ProgressBar i ActivityIndicator
| Kontrolka | Właściwość | Opis |
|---|---|---|
ProgressBar | Progress | Wartość od 0.0 do 1.0: aktualny postęp. |
ProgressBar | ProgressColor | Kolor paska, może nieść znaczenie (zielony: OK, czerwony: błąd). |
ProgressBar | ProgressTo(wartość, czas, Easing) | Metoda animująca płynną zmianę, zamiast skoku. |
ActivityIndicator | IsRunning | Włącza animację „kręcącego się kółka”. |
ActivityIndicator | IsVisible, Color | Pokazuje i ukrywa spinner, ustawia kolor. |
Wyzwanie 1 · czytanie kodu
Ile pokaże pasek?
Wyzwanie 2 · pasek
Pasek postępu pobierania
Sprawdź, czy zapamiętałeś
Jedno szybkie pytanie
Etap 2 z 4
ActivityIndicator: gdy nie wiesz, ile to potrwa
Spinner pokazuje, że aplikacja pracuje, ale nie mówi, ile jeszcze. Włączasz go przez IsRunning="True", a ukrywasz przez IsVisible="False". Gdy postęp da się policzyć, lepszy jest ProgressBar.
Wyzwanie 3 · spinner
Spinner na czas łączenia
Sprawdź, czy zapamiętałeś
Jedno szybkie pytanie
Etap 3 z 4
CollectionView: lista z szablonem
CollectionView pokazuje kolekcję danych. Dane podajesz przez ItemsSource, a wygląd jednego elementu opisujesz w ItemTemplate. Gdy lista jest pusta, możesz pokazać EmptyView. W nowych projektach używasz CollectionView, a nie starszego ListView.
5 · Listy
CollectionView i ListView
| Właściwość | Opis |
|---|---|
ItemsSource | Źródło danych, zwykle kolekcja obiektów podpięta z kodu. |
ItemTemplate | Wygląd pojedynczego elementu (DataTemplate). |
ItemsLayout | VerticalList, HorizontalList albo siatka (GridItemsLayout). |
SelectionMode | None, Single lub Multiple. |
EmptyView | Co pokazać, gdy lista jest pusta. |
ListView | Starsza kontrolka: SelectedItem, SeparatorVisibility, HasUnevenRows. W nowych projektach wybieraj CollectionView. |
Wyzwanie 4 · lista
Ułóż XAML listy
Wyzwanie 5 · cudzy kod
Lista, która nic nie pokazuje
Sprawdź, czy zapamiętałeś
Dwa szybkie pytania
Etap 4 z 4
Spinner w metodzie: nigdy nie blokuj ekranu
Metoda przycisku wykonuje się w wątku interfejsu. Gdy użyjesz Thread.Sleep, ekran zamarznie i spinner się nie poruszy. Dlatego czekasz przez await Task.Delay(...), a metodę oznaczasz async.
Wyzwanie 6 · logika
Ułóż metodę pobierania
Wyzwanie końcowe
Ekran pobierania
Sprawdź, czy zapamiętałeś
Cztery pytania z całej lekcji
9 · Sprawdź się
Pasek, spinner czy lista?
Cztery sytuacje.
Dla chętnych · po pierwszej stroniePracownia w Visual Studio: to samo w prawdziwym projekcie
Rozwiń dodatkowe warsztaty ↓
Wyzwania powyżej uczą w przeglądarce. Tu jest pełna instrukcja krok po kroku, jak zrobić to w Visual Studio i uruchomić na emulatorze, tak jak na egzaminie praktycznym. Otwórz, gdy będziesz gotowy do pracy z projektem.
1. Przygotuj miejsce pracy
Co dodajesz, a co podmieniasz?
Stronę dodaj przez Projekt → Dodaj → Nowy element → .NET MAUI ContentPage (XAML). Powstaje para .xaml i .xaml.cs. Dla ContentView wybierz szablon ContentView; dla modeli i serwisów zwykłą klasę. Jeśli plik już istnieje, podmień wskazaną zawartość — nie twórz drugiej klasy o tej samej nazwie.
| Plik | Rola w ćwiczeniu |
|---|---|
| PostepPage.xaml | Wygląd strony lub kontrolki |
| PostepPage.xaml.cs | Logika i obsługa danych |
| ListaPlikowPage.xaml | Wygląd strony lub kontrolki |
| ListaPlikowPage.xaml.cs | Logika i obsługa danych |
Zanim uruchomisz: trzy połączenia
x:Class="MojaAplikacja.NazwaStrony" w XAML musi zgadzać się z namespace MojaAplikacja i nazwą klasy C#. InitializeComponent() tworzy kontrolki opisane w XAML.
x:Name="PoleImie" daje kodowi C# dostęp przez PoleImie.Text. Z kolei Clicked="Zapisz_Clicked" wskazuje metodę o tej nazwie w pliku .xaml.cs. Sam napis przycisku w Text niczego nie podpina.
Gdy dodajesz XAML z obsługą zdarzeń, od razu uzupełnij też odpowiadający C#. Uruchamiaj dopiero po skończeniu oznaczonej pary plików; brakujący handler nie jest błędem środowiska.
2. Uruchom sam wygląd — jeszcze bez logiki
W świeżym projekcie zastąp MainPage.xaml poniższym ekranem. W MainPage.xaml.cs zachowaj konstruktor z InitializeComponent(); usuń szablonowy licznik i OnCounterClicked, bo nie ma ich już w XAML. Na tym etapie App i AppShell pozostają z szablonu. To pełny, tymczasowy XAML — później świadomie go podmienisz, nie dokleisz do drugiego ContentPage.
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.MainPage">
<VerticalStackLayout Padding="24" Spacing="14">
<Label Text="Postęp: połowa" FontSize="24" />
<ProgressBar Progress="0.5" />
<ActivityIndicator IsRunning="True" />
</VerticalStackLayout>
</ContentPage>
- Pierwsza próba Uruchom. Zobaczysz kontrolki, ale przyciski bez Clicked jeszcze nic nie robią. Sprawdź rozmiar i kolejność elementów. Zatrzymaj aplikację przed następnym etapem.
3. Wskaż stronę startową ćwiczenia
Po dodaniu docelowych stron podmień App.xaml.cs na poniższy. App.xaml zostaje z szablonu. Od tej chwili startuje PostepPage, a nie wcześniejszy tymczasowy MainPage z AppShell. Uzupełnij jej pliki w kolejnych etapach, zanim ponownie uruchomisz projekt.
namespace MojaAplikacja;
public partial class App : Application
{
public App() => InitializeComponent();
protected override Window CreateWindow(IActivationState? activationState)
=> new Window(new NavigationPage(new PostepPage()));
}
CreateWindowTworzy okno aplikacji. NavigationPage jest kontenerem zwykłych stron i daje przycisk powrotu; nie łączymy go z Shell.new PostepPage()Tworzy instancję docelowej strony. Aby testować drugi niezależny przykład, zatrzymaj program, zmień tylko ten typ i uruchom ponownie.
4. Połącz wygląd z działaniem
Teraz zbuduj docelowy przykład. Pełne pliki poniżej zastępują swoje wcześniejsze wersje. Przeczytaj opis nad kodem i objaśnienia pod nim; potem sprawdź przepływ danych, nie tylko brak czerwonych podkreśleń.
Gdy przykład ma kilka plików zależnych od siebie, najpierw dodaj wszystkie wskazane typy, a potem uruchom całość. Ustaw breakpoint w obsłudze głównego przycisku i prześledź jedną próbę.
Budowa krok po kroku
PostepPage.xaml
Dodaj PostepPage. Progress to liczba0–1, nie0–100. Na tym ekranie oddzielnie testujemy znany postęp i oczekiwanie bez procentu.
Dwie sekcje: pasek postępu i spinner, każda z własnym przyciskiem.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MojaAplikacja.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>
ProgressBar Progress="0"Pasek startuje pusty.ActivityIndicator IsRunning="False"Spinner jest wyłączony, dopóki kod go nie uruchomi.BoxView HeightRequest="1"Cienka linia oddzielająca sekcje.
Budowa krok po kroku
PostepPage.xaml.cs
ProgressTo płynnie zmienia pasek, a Task.Delay tylko symuluje czas. Żaden z tych przycisków nie wysyła danych przez sieć. await pozwala wrócić do obsługi interfejsu.
Obie metody są async, bo korzystają z await.
namespace MojaAplikacja;
public partial class PostepPage : ContentPage
{
public PostepPage()
{
InitializeComponent();
}
private async void RozpocznijWysylanie_Clicked(object sender, EventArgs e)
{
// ProgressTo animuje płynne przejście od aktualnej wartości do 1.0
// w ciągu 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 odpowiedź serwera (bez blokowania UI)
await Task.Delay(2000);
Spinner.IsRunning = false;
await DisplayAlertAsync("Gotowe", "Dane zostały pobrane.", "OK");
}
}
await PasekPostepu.ProgressTo(1.0, 2000, Easing.Linear)Płynnie zwiększa wartość przez 2 sekundy. Bez animacji pasek skoczyłby od razu do końca.Spinner.IsRunning = trueWłącza spinner na czas operacji.await Task.Delay(2000)Sztuczne czekanie bez blokowania interfejsu. W prawdziwej aplikacji będzie tuawait httpClient.GetAsync(...).Spinner.IsRunning = falseZawsze wyłącz spinner po zakończeniu. Wieczny spinner sugeruje zawieszenie aplikacji.
Zatrzymaj się przed kolejnym wariantem
Nie zastępuj jeszcze działającej wersji. Wykonaj tę próbę i dopiero potem przejdź do dodatku.
Dopiero teraz: drugi wariant lub rozszerzenie
To następny etap tej lekcji, nie warunek pierwszego uruchomienia. Porównaj jego rolę z działającą bazą. Jeśli brakuje czasu, wróć do niego po sprawdzeniu podstawowej wersji.
5. Odbiór: czy naprawdę działa?
Wykonaj próby w swojej aplikacji, nie tylko w modelu na stronie. Zaznaczaj dopiero po sprawdzeniu wyniku. Funkcje urządzenia wymagają testu na właściwej platformie.
8 · Czego unikać
Cztery błędy z postępem i listami
`Thread.Sleep` zamiast `Task.Delay`
Thread.Sleep(2000);Interfejs zamarza, a spinner się nie kręci.
await Task.Delay(2000);await oddaje sterowanie wątkowi interfejsu na czas oczekiwania.
Spinner bez wyłączenia
Spinner.IsRunning = true;
await Task.Delay(2000);
// brak Spinner.IsRunning = falseSpinner kręci się w nieskończoność, jakby aplikacja się zawiesiła.
Spinner.IsRunning = false;Każda operacja, która włącza spinner, musi go też wyłączyć (także przy błędzie).
`ListView` w nowym projekcie
<ListView ItemsSource="{Binding Pozycje}" />Działa, ale to starsza kontrolka, mniej wydajna i elastyczna.
<CollectionView ItemsSource="{Binding Pozycje}" />CollectionView jest zalecany w nowych projektach.
Brak `EmptyView`
<CollectionView ItemsSource="{Binding Wyniki}" />Przy braku wyników widać pusty ekran, który wygląda jak błąd.
<CollectionView.EmptyView>
<Label Text="Brak wyników" />
</CollectionView.EmptyView>Użytkownik powinien wiedzieć, że lista jest pusta, a nie zepsuta.
Ćwiczenie z pomocą · samodzielna praca
6. Mała zmiana, którą zrobisz samodzielnie
Dodaj licznik wyświetlonych nazw plików pod listą.
Dodaj licznik wyświetlonych nazw plików pod listą. Dotychczasowe działanie ma pozostać poprawne.
? Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź
Dodaj Label z nazwą LicznikLabel.
Krok 2 — pokaż podpowiedź
Po ustawieniu ItemsSource ustal liczbę elementów pobranej kolekcji.
Krok 3 — pokaż podpowiedź
Nie wpisuj na stałe liczby, jeśli w następnym kroku zmienisz dane.
Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.
Podsumowanie
Co warto zapamiętać
- ProgressBar dla znanego postępu (0.0 do 1.0),
ProgressToanimuje zmianę. ActivityIndicator dla nieznanego czasu. - Spinner zawsze wyłączaj po zakończeniu (
IsRunning = false). await Task.DelayzamiastThread.Sleep, bo nie blokuje wątku interfejsu.- CollectionView:
ItemsSource,ItemTemplate,ItemsLayout,SelectionMode,EmptyView. ListViewrozpoznawaj w starszym kodzie, ale w nowych projektach wybierajCollectionView.
Dalej na swoim koncie
Rozgrzewki, quiz i zadania są w biurku ucznia.
Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.
Zaloguj się →