Przejdź do treści
School IT / Przestrzeń do nauki

ProgressBar — pasek postępu

Pasek postępu pokazuje „ile już zrobiono”: wypełnienie formularza, zebraną kwotę, punkty życia w grze, zajęte miejsce na dysku. Działa jak suwak, którego użytkownik nie może przesunąć — wartość ustawia tylko program. W tej lekcji nauczysz się przeliczać dane na pasek, pokazywać procent na pasku i zmieniać jego kolor zależnie od stanu.

XAML C# ProgressBar Value / Maximum IsIndeterminate 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Dodasz pasek postępu z zakresem dopasowanym do danych (np. 0–5000 zł zamiast 0–100).
  • Policzysz procent postępu i pokażesz go napisem na pasku.
  • Zmienisz kolor paska zależnie od stanu.
  • Włączysz pasek „nieokreślony” dla operacji o nieznanym czasie trwania.
  • Wyjaśnisz, dlaczego pasek nie odświeża się w trakcie długiej pętli.
TEORIA

Pasek w oknie

MainWindow.xaml
<StackPanel Margin="20">
    <TextBlock Text="Wypełnienie ankiety"/>
    <ProgressBar x:Name="Postep" Minimum="0" Maximum="10" Value="3"
                 Height="20" Margin="0,4,0,0"/>
</StackPanel>
Ankieta–□✕
Wypełnienie ankiety

3 z 10 pytań — pasek wypełniony w 30%

Pasek ma tę samą „rodzinę” właściwości co suwak: Minimum, Maximum, Value, wszystkie typu double. Domyślny zakres to 0–100. W pionowym StackPanel pasek ma bardzo małą wysokość, dlatego zwykle dostaje Height. Orientation="Vertical" stawia go pionowo, jak termometr.

TEORIA

Wartość, zakres, procent

Nie trzeba przeliczać danych na procenty — ustaw Maximum na wartość docelową, a Value na bieżącą. Pasek sam wylicza, jaką część wypełnić. Procent do napisu liczysz osobno:

MainWindow.xaml.cs
// 14 z 20 zadań zrobionych
Postep.Maximum = 20;
Postep.Value = 14;

double procent = Postep.Value / Postep.Maximum * 100;    // 70
Opis.Text = $"{Postep.Value} z {Postep.Maximum} ({procent:F0}%)";

Jak w suwaku, wartość spoza zakresu jest przycinana: Value = 25 przy Maximum = 20 daje 20. Jeśli liczba może przekroczyć cel (np. zbiórka zebrała więcej), trzymaj ją we własnym polu klasy, a pasek traktuj tylko jako widok.

TEORIA

Kolor i napis na pasku

Kolor wypełnienia to Foreground, kolor tła — Background. Napis z procentem kładziesz na pasek tak, jak w lekcji 09 nakładały się kontrolki: oba elementy w jednej komórce Grid, napis niżej w pliku, więc na wierzchu:

MainWindow.xaml
<Grid Height="26">
    <ProgressBar x:Name="Zycie" Maximum="100" Value="35" Foreground="IndianRed"/>
    <TextBlock x:Name="ZycieOpis" Text="35 / 100 HP"
               HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold"/>
</Grid>
Gracz–□✕
35 / 100 HP

napis leży na pasku, bo stoi w tej samej komórce i jest później w pliku

TEORIA

Postęp nieokreślony

Gdy program nie wie, ile potrwa operacja (łączenie z serwerem, wyszukiwanie), pokazuje pasek z IsIndeterminate="True": zamiast wypełnienia przesuwa się po nim w kółko pasmo — „coś się dzieje, czekaj”. Value jest wtedy pomijana.

Pasek w długiej pętli się nie odświeża

Okno odświeża się dopiero wtedy, gdy metoda obsługi zdarzenia się zakończy. Jeśli w Click wykonasz pętlę liczącą 10 sekund i w środku ustawiasz Postep.Value, użytkownik zobaczy zamrożone okno, a na końcu od razu pełny pasek. Pasek, który rośnie w trakcie pracy, zrobisz z czasomierzem w lekcji 27 i pracą w tle w lekcji 51. Na razie pasek zmienia się po każdej akcji użytkownika.

PRZYKŁAD

Zbiórka na schronisko

Klasa zbiera 5000 zł na karmę dla schroniska. Wpłaty dodaje się przyciskami szybkich kwot albo wpisując własną. Pasek pokazuje postęp z procentem na środku, zmienia kolor z czerwonego przez pomarańczowy na zielony, a lista pod spodem zbiera historię wpłat — najnowsza na górze. Po osiągnięciu celu przyciski wpłat się wyłączają.

Zbiórka na schronisko–□✕
Karma dla schroniska „Azorek”
Zebrano 3270,00 zł z 5000 zł — brakuje 1730,00 zł
65%
10 zł
20 zł
50 zł
100 zł
 
Wpłać
11:42 +50,00 zł
11:40 +20,00 zł
11:31 +200,00 zł

65% celu — pasek pomarańczowy

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja20.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Zbiórka na schronisko" Height="460" Width="460"
        MinWidth="400" MinHeight="400" WindowStartupLocation="CenterScreen">

    <Grid Margin="15">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="0" Text="Karma dla schroniska „Azorek”" FontSize="18" FontWeight="Bold"/>
        <TextBlock x:Name="Stan" Grid.Row="1" Margin="0,4,0,8"/>

        <!-- pasek i napis z procentem w jednej komórce: napis leży na pasku -->
        <Grid Grid.Row="2" Height="28">
            <ProgressBar x:Name="Pasek"/>
            <TextBlock x:Name="Procent" HorizontalAlignment="Center" VerticalAlignment="Center"
                       FontWeight="Bold"/>
        </Grid>

        <!-- wpłaty: szybkie kwoty albo własna -->
        <StackPanel Grid.Row="3" Margin="0,12,0,8">
            <WrapPanel x:Name="Szybkie">
                <Button Content="10 zł" Tag="10" Width="60" Margin="0,0,6,6" Click="Szybka_Click"/>
                <Button Content="20 zł" Tag="20" Width="60" Margin="0,0,6,6" Click="Szybka_Click"/>
                <Button Content="50 zł" Tag="50" Width="60" Margin="0,0,6,6" Click="Szybka_Click"/>
                <Button Content="100 zł" Tag="100" Width="60" Margin="0,0,6,6" Click="Szybka_Click"/>
            </WrapPanel>
            <DockPanel>
                <Button x:Name="Wplac" DockPanel.Dock="Right" Content="Wpłać" Width="80"
                        Margin="6,0,0,0" IsDefault="True" Click="Wplac_Click"/>
                <TextBox x:Name="Kwota" VerticalContentAlignment="Center"/>
            </DockPanel>
        </StackPanel>

        <ListBox x:Name="Historia" Grid.Row="4"/>
    </Grid>
</Window>
MainWindow.xaml.cs — pełny plik
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Lekcja20;

public partial class MainWindow : Window
{
    private const double Cel = 5000;
    private double zebrano = 0;

    public MainWindow()
    {
        InitializeComponent();
        Pasek.Maximum = Cel;        // cel zapisany w jednym miejscu — w stałej
        Odswiez();
    }

    private void Szybka_Click(object sender, RoutedEventArgs e)
    {
        Button przycisk = (Button)sender;
        Dodaj(double.Parse($"{przycisk.Tag}"));     // kwoty wpisaliśmy sami w XAML
    }

    private void Wplac_Click(object sender, RoutedEventArgs e)
    {
        if (!double.TryParse(Kwota.Text, out double kwota) || kwota <= 0 || kwota > 1000)
        {
            MessageBox.Show("Wpisz kwotę od 0,01 do 1000 zł.", "Zbiórka",
                            MessageBoxButton.OK, MessageBoxImage.Warning);
            Kwota.SelectAll();
            Kwota.Focus();
            return;
        }

        Dodaj(Math.Round(kwota, 2));
        Kwota.Clear();
    }

    private void Dodaj(double kwota)
    {
        zebrano += kwota;
        Pasek.Value = zebrano;      // wartość powyżej Maximum pasek sam przytnie do 100%
        Historia.Items.Insert(0, $"{DateTime.Now:HH:mm}  +{kwota:F2} zł");
        Odswiez();

        if (zebrano >= Cel)
        {
            MessageBox.Show("Cel osiągnięty! Dziękujemy wszystkim darczyńcom.", "Zbiórka",
                            MessageBoxButton.OK, MessageBoxImage.Information);
        }
    }

    private void Odswiez()
    {
        double procent = zebrano / Cel * 100;
        double brakuje = Cel - zebrano;

        Procent.Text = $"{procent:F0}%";
        Stan.Text = brakuje > 0
            ? $"Zebrano {zebrano:F2} zł z {Cel:F0} zł — brakuje {brakuje:F2} zł"
            : $"Zebrano {zebrano:F2} zł — cel osiągnięty!";

        // kolor paska zależy od postępu
        if (procent < 30)
        {
            Pasek.Foreground = Brushes.IndianRed;
        }
        else if (procent < 100)
        {
            Pasek.Foreground = Brushes.Orange;
        }
        else
        {
            Pasek.Foreground = Brushes.SeaGreen;
        }

        // po osiągnięciu celu kolejne wpłaty są wyłączone
        bool otwarta = brakuje > 0;
        Szybkie.IsEnabled = otwarta;
        Wplac.IsEnabled = otwarta;
        Kwota.IsEnabled = otwarta;
    }
}

Co dzieje się po kolei

  • Cel zbiórki jest w stałej Cel; konstruktor ustawia z niej Maximum paska. Zebraną kwotę pamięta pole zebrano — pasek tylko ją pokazuje i nie zgubi nadwyżki powyżej celu.
  • Pasek i napis z procentem stoją w jednym Grid o wysokości 28 — napis, zapisany później, leży na pasku.
  • Szybkie kwoty mają wartość w Tag i jedną metodę Szybka_Click. Własna kwota przechodzi przez TryParse i sprawdzenie zakresu.
  • Obie drogi kończą się w Dodaj: dopisanie kwoty, przesunięcie paska, wpis na górę historii (Items.Insert(0, …)) i odświeżenie napisów.
  • Odswiez liczy procent i brakującą kwotę z pola zebrano, ustawia kolor paska (Foreground) i wyłącza wpłaty po osiągnięciu celu — cały panel szybkich kwot jednym IsEnabled.
  • Operator ? : wybiera jeden z dwóch napisów stanu.

Sprawdź trzy rzeczy

  1. Zmień w kodzie Pasek.Value = zebrano; na Pasek.Value += kwota; i licz procent z Pasek.Value. Wpłać kwotę ponad cel — nadwyżka zniknie, bo pasek przyciął wartość.
  2. Dodaj do paska IsIndeterminate="True". Pasek zacznie przesuwać pasmo w kółko i przestanie pokazywać zebraną kwotę.
  3. Zmień stałą Cel na 1000. Pasek, procent i napisy dopasują się same.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTypCo robi
Minimum, MaximumdoubleZakres (domyślnie 0–100).
ValuedoubleBieżący postęp; spoza zakresu jest przycinany.
ForegroundpędzelKolor wypełnienia.
BackgroundpędzelKolor niewypełnionej części.
IsIndeterminateboolPasek nieokreślony — przesuwające się pasmo.
OrientationHorizontal, VerticalPasek poziomy / pionowy (wypełnia się od dołu).
ValueChangedzdarzenieJak w suwaku — rzadko potrzebne, bo wartość zmienia tylko kod.
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
pasek w StackPanel bez HeightCienka kreska — trudno ją zauważyć.
Postep.Value = 14; przy domyślnym Maximum 100, gdy zadań jest 20Pasek pokazuje 14% zamiast 70%. Ustaw Maximum = 20.
procent = zrobione / wszystkie * 100 na intDzielenie całkowite: 14 / 20 = 0, więc zawsze 0%. Licz na double albo mnóż przez 100 najpierw.
suma przechowywana tylko w ValueWartość powyżej Maximum ginie — przycięcie do maksimum.
pętla z Value++ w jednym ClickOkno nie odświeży się w trakcie — zobaczysz od razu pełny pasek.
napis zapisany w XAML przed paskiem w tej samej komórcePasek zakrywa napis.
ZADANIA

Zadania

Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Paski zmieniaj po akcjach użytkownika — czasomierze poznasz w lekcji 27. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Postęp wypełniania formularza★☆☆

Formularz zgłoszenia na zawody: imię, nazwisko, klasa (lista rozwijana), dyscyplina (przyciski opcji, bez wyboru domyślnego), zgoda rodzica (pole wyboru). Nad formularzem pasek „Wypełniono 3 z 5” zmieniający się na żywo. Przy komplecie pasek jest zielony, a przycisk Wyślij się włącza.

pokaż wskazówkę
  • Ustaw Maximum="5" i licz wypełnione pola w jednej metodzie wywoływanej ze wszystkich zdarzeń zmian.
ZAD 2Pasek życia★☆☆

Panel postaci w grze: pasek HP 0–100 z napisem „65 / 100 HP”, przyciski Otrzymaj cios (−15), Mikstura (+25), Odpoczynek (+5). HP nie spada poniżej 0 i nie rośnie ponad 100. Kolor: zielony powyżej 60, żółty 30–60, czerwony poniżej 30. Przy 0 HP napis „Koniec gry”, przyciski są wyłączone, a Od nowa przywraca 100 HP. Licznik mikstur: na start 3, przy 0 przycisk mikstury jest nieaktywny.

pokaż wskazówkę
  • Granice załatwi Math.Min i Math.Max — albo sam pasek, jeśli HP trzymasz tylko w Value.
ZAD 3Kreator w czterech krokach★★☆

Kreator zakładania konta w czterech krokach: dane osobowe, adres, hasło, podsumowanie. Na górze pasek „Krok 2 z 4” i nazwy kroków, z których bieżąca jest pogrubiona. Na dole przyciski ◀ Wstecz (nieaktywny w kroku 1) i Dalej ▶ (w kroku 4 zmienia się na Zakończ). Przejście dalej jest możliwe tylko przy wypełnionych polach bieżącego kroku. Podsumowanie wyświetla wszystkie dane (hasło jako gwiazdki).

pokaż wskazówkę
  • Cztery panele kroków w jednej komórce Grid; widoczny ten, którego numer zgadza się z polem krok.
  • Panele możesz zebrać w tablicę StackPanel[] w konstruktorze — wtedy widoczność ustawisz w pętli.
ZAD 4Kopiowanie na pendrive★★☆

Lista 10 plików z rozmiarami (np. „film.mp4 — 4,2 GB”) z wyborem wielu pozycji. Pasek pokazuje zajęte miejsce na pendrivie 16 GB: już zajęte 3,5 GB plus zaznaczone pliki, z napisem „12,8 / 16 GB”. Powyżej 90% pasek jest pomarańczowy, a gdy pliki się nie mieszczą — czerwony, napis mówi „Brakuje 1,3 GB”, a przycisk Kopiuj jest nieaktywny. Skopiowane pliki znikają z listy i zwiększają zajęte miejsce.

pokaż wskazówkę
  • Nazwy i rozmiary w dwóch tablicach. Na liście trzymaj samą nazwę, rozmiar znajdziesz przez Array.IndexOf.
  • Maximum ustaw na 16; wartość większą od pojemności i tak licz w zmiennej — pasek ją przytnie.
ZAD 5Tydzień czytania★★☆

Siedem pionowych pasków (pon.–niedz.) obok siebie, każdy 0–60 minut, z podpisem dnia pod spodem i liczbą minut nad paskiem. Lista rozwijana dnia, pole minut i przycisk Dodaj dopisują czytanie do wybranego dnia. Pod wykresem poziomy pasek celu tygodniowego 300 minut: „Tydzień: 185 / 300 min”. Dzień z co najmniej 30 minutami ma zielony pasek. Po starcie wybrany jest dzisiejszy dzień.

pokaż wskazówkę
  • Pionowe paski w UniformGrid z 7 kolumnami; minuty w tablicy int[7], a pasek dnia i to Children[i].
  • Paski pionowe potrzebują wysokości — np. Height="150".
ZAD 6Planer nauki do egzaminu★★★

Lista tematów do powtórki z planowaną liczbą godzin (np. „XAML i układy — 6 h”, „Zdarzenia — 4 h”, „Formularze — 8 h”…, razem 40 h). Zaznaczenie tematu pokazuje jego pasek: „2,5 / 6 h”. Pole i przycisk Dodaj godziny zapisują naukę do zaznaczonego tematu (krok 0,5 h). Ukończony temat dostaje na liście znak „✓ ” przed nazwą. Na dole pasek ogólny „Razem: 17,5 / 40 h (44%)” oraz napis, ile godzin dziennie trzeba się uczyć, żeby zdążyć przed datą egzaminu zapisaną w stałej.

pokaż wskazówkę
  • Nazwy, plan i godziny wykonane to trzy tablice. Pozycje listy budujesz z nich od nowa po każdej zmianie — pamiętaj, żeby potem przywrócić zaznaczenie.
  • Liczba dni do egzaminu: (dataEgzaminu - DateTime.Today).Days. Uważaj na dzielenie przez zero w dniu egzaminu.
PODSUMOWANIE

Co trzeba zapamiętać

  • ProgressBar pokazuje postęp, którego użytkownik nie zmienia — Value ustawia program.
  • Ustaw Maximum na wartość docelową — pasek sam przeliczy proporcję. Procent do napisu licz na double.
  • Wartość powyżej Maximum jest przycinana; prawdziwą sumę trzymaj w polu klasy.
  • Napis na pasku = pasek i TextBlock w jednej komórce Grid. Kolor wypełnienia to Foreground.
  • IsIndeterminate — „czekaj”, gdy czas nie jest znany. Pasek nie odświeża się w trakcie długiej pętli.

Dokumentacja: Microsoft Learn — klasa ProgressBar.

Postęp zapisuje się w tej przeglądarce.