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

CheckBox — pole wyboru

Zgoda na regulamin, dodatki do zamówienia, opcje w ustawieniach — wszędzie tam, gdzie użytkownik odpowiada „tak” albo „nie” na każde pytanie osobno, stoi CheckBox. To pierwsza kontrolka wyboru w kursie. Nauczysz się odczytywać jej stan, reagować na zmianę na żywo i obsłużyć trzeci stan — „częściowo”, który w C# zapisuje się jako null.

XAML C# CheckBox IsChecked bool? Checked / Unchecked 90 min
CEL LEKCJI

Czego się dziś nauczysz

  • Dodasz pole wyboru z opisem i ustawisz jego stan początkowy.
  • Odczytasz stan pola i wyjaśnisz, dlaczego IsChecked ma typ bool?, a nie bool.
  • Zareagujesz na zaznaczenie i odznaczenie jedną metodą podpiętą pod Checked i Unchecked.
  • Rozróżnisz zdarzenie Click (tylko użytkownik) od Checked (także zmiana z kodu).
  • Zbudujesz pole „Zaznacz wszystkie” z trzecim stanem „częściowo”.
  • Przejdziesz pętlą po grupie pól i policzysz zaznaczone.
TEORIA

CheckBox w XAML

MainWindow.xaml
<StackPanel Margin="15">
    <CheckBox x:Name="Newsletter" Content="Chcę otrzymywać newsletter" Margin="0,4"/>
    <CheckBox x:Name="Powiadomienia" Content="Powiadomienia o ocenach"
              IsChecked="True" Margin="0,4"/>
    <CheckBox Content="Tryb ciemny (niedostępny)" IsEnabled="False" Margin="0,4"/>
</StackPanel>
Ustawienia–□✕
Chcę otrzymywać newsletter
Powiadomienia o ocenach
Tryb ciemny (niedostępny)

kliknięcie w kwadrat albo w opis zmienia stan pola

Opis pola to Content — jak w przycisku. Kliknięcie w tekst opisu też przełącza pole, dlatego nie potrzebujesz osobnej etykiety. Pola w grupie są od siebie niezależne: zaznaczenie jednego nie zmienia pozostałych.

TEORIA

IsChecked i typ bool?

Stan pola zapisuje właściwość IsChecked. Ma ona typ bool? — „bool, który może być pusty”. Oprócz true i false może więc mieć wartość null, oznaczającą stan nieokreślony. Przez to nie da się jej wstawić wprost do if:

MainWindow.xaml.cs
if (Newsletter.IsChecked)            // ✗ CS0266: nie można zamienić bool? na bool
{
}

if (Newsletter.IsChecked == true)    // ✓ prawda tylko dla zaznaczonego pola
{
    Info.Text = "Zapisano do newslettera.";
}

bool zapisany = Newsletter.IsChecked == true;   // zamiana na zwykły bool

Newsletter.IsChecked = false;        // odznaczenie z kodu

Porównanie == true daje zwykły bool: true dla zaznaczonego pola, false dla odznaczonego i dla null. To najprostszy i najczęściej używany zapis.

TEORIA

Checked, Unchecked i Click

Gdy wynik ma się zmieniać od razu, bez przycisku „Oblicz”, reagujesz na zdarzenia pola:

ZdarzenieKiedyUwaga
Checkedpole stało się zaznaczonetakże po IsChecked = true w kodzie
Uncheckedpole stało się odznaczonetakże po zmianie z kodu
Clickużytkownik kliknął polenie wywołuje się przy zmianie z kodu
MainWindow.xaml
<CheckBox x:Name="Pogrubienie" Content="Pogrubienie"
          Checked="Styl_Zmiana" Unchecked="Styl_Zmiana"/>
MainWindow.xaml.cs
// jedna metoda dla obu zdarzeń — po prostu sprawdza aktualny stan
private void Styl_Zmiana(object sender, RoutedEventArgs e)
{
    Podglad.FontWeight = Pogrubienie.IsChecked == true ? FontWeights.Bold : FontWeights.Normal;
}

Najprostszy i najbezpieczniejszy wzór to jedna metoda pod oba zdarzenia, która nie pyta, co się zmieniło, tylko przelicza wszystko od nowa na podstawie aktualnych stanów pól.

IsChecked="True" w XAML i zdarzenie Checked

Jeśli pole ma w XAML IsChecked="True" i Checked="…", zdarzenie wywoła się już podczas InitializeComponent() — zanim powstaną kontrolki zapisane niżej w pliku. Metoda, która zmienia taką kontrolkę, zakończy się wtedy błędem NullReferenceException. Ustaw stan początkowy w konstruktorze, po InitializeComponent(), albo sprawdź na początku metody, czy kontrolka nie jest null.

TEORIA

Trzeci stan — „częściowo”

Pole z IsThreeState="True" ma trzy stany: zaznaczone (true), odznaczone (false) i nieokreślone (null), rysowane jako wypełniony kwadrat. Najczęściej spotkasz je w polu „Zaznacz wszystkie”: gdy zaznaczona jest tylko część pozycji, pole pokazuje stan pośredni.

Trzy stany–□✕
IsChecked = true
IsChecked = false
IsChecked = null

kliknięcia przełączają pole trójstanowe w kolejności: odznaczone → zaznaczone → nieokreślone → odznaczone

TEORIA

Wiele pól naraz

Gdy pól jest kilka, nie trzeba nadawać każdemu nazwy. Wkładasz je do jednego panelu z nazwą i przechodzisz po nim pętlą — tak jak po przyciskach w lekcji 12. Dane potrzebne do obliczeń (cenę, punkty) trzymasz w Tag:

MainWindow.xaml.cs
int zaznaczone = 0;
foreach (CheckBox pole in Opcje.Children)
{
    if (pole.IsChecked == true)
    {
        zaznaczone++;
    }
}
Info.Text = $"Wybrano {zaznaczone} z {Opcje.Children.Count}";
PRZYKŁAD

Zamówienie pizzy z dodatkami

Klient wybiera dodatki do pizzy. Cena przelicza się przy każdym kliknięciu, a pole „Zaznacz wszystkie dodatki” pokazuje, czy zaznaczono wszystkie, żadnego, czy tylko część. Przycisk Zamów jest aktywny dopiero po zaakceptowaniu regulaminu.

Pizzeria „Pod Dębem”–□✕
Margherita 32 cm — 24,00 zł
Zaznacz wszystkie dodatki
Podwójny ser (+4 zł)
Pieczarki (+3 zł)
Szynka (+5 zł)
Oliwki (+4 zł)
Jalapeño (+3 zł)
Cebula (+2 zł)
Akceptuję regulamin dostawy
Do zapłaty: 32,00 zł
Zamów

dwa dodatki — „Zaznacz wszystkie” w stanie częściowym

MainWindow.xaml — pełny plik
<Window x:Class="Lekcja14.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Pizzeria „Pod Dębem”" Height="420" Width="440"
        ResizeMode="CanMinimize" WindowStartupLocation="CenterScreen">

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

        <TextBlock Grid.Row="0" Text="Margherita 32 cm — 24,00 zł" FontSize="18"
                   FontWeight="Bold" Margin="0,0,0,10"/>

        <!-- Dodatki w ramce -->
        <Border Grid.Row="1" BorderBrush="LightGray" BorderThickness="1"
                CornerRadius="6" Padding="10">
            <StackPanel>
                <CheckBox x:Name="Wszystkie" Content="Zaznacz wszystkie dodatki"
                          IsThreeState="True" FontWeight="Bold" Margin="0,0,0,8"
                          Click="Wszystkie_Click"/>

                <UniformGrid x:Name="Dodatki" Columns="2">
                    <CheckBox Content="Podwójny ser (+4 zł)" Tag="4" Margin="0,4"
                              Checked="Dodatek_Zmiana" Unchecked="Dodatek_Zmiana"/>
                    <CheckBox Content="Pieczarki (+3 zł)" Tag="3" Margin="0,4"
                              Checked="Dodatek_Zmiana" Unchecked="Dodatek_Zmiana"/>
                    <CheckBox Content="Szynka (+5 zł)" Tag="5" Margin="0,4"
                              Checked="Dodatek_Zmiana" Unchecked="Dodatek_Zmiana"/>
                    <CheckBox Content="Oliwki (+4 zł)" Tag="4" Margin="0,4"
                              Checked="Dodatek_Zmiana" Unchecked="Dodatek_Zmiana"/>
                    <CheckBox Content="Jalapeño (+3 zł)" Tag="3" Margin="0,4"
                              Checked="Dodatek_Zmiana" Unchecked="Dodatek_Zmiana"/>
                    <CheckBox Content="Cebula (+2 zł)" Tag="2" Margin="0,4"
                              Checked="Dodatek_Zmiana" Unchecked="Dodatek_Zmiana"/>
                </UniformGrid>
            </StackPanel>
        </Border>

        <CheckBox x:Name="Regulamin" Grid.Row="2" Margin="0,12,0,0"
                  Content="Akceptuję regulamin dostawy"
                  Checked="Regulamin_Zmiana" Unchecked="Regulamin_Zmiana"/>

        <Grid Grid.Row="3" Margin="0,12,0,0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <TextBlock x:Name="Suma" Text="Do zapłaty: 24,00 zł" FontSize="16"
                       VerticalAlignment="Center"/>
            <Button x:Name="Zamow" Grid.Column="1" Content="Zamów" Width="100" Height="30"
                    IsEnabled="False" Click="Zamow_Click"/>
        </Grid>
    </Grid>
</Window>
MainWindow.xaml.cs — pełny plik
using System.Windows;
using System.Windows.Controls;

namespace Lekcja14;

public partial class MainWindow : Window
{
    private const decimal CenaPizzy = 24m;

    public MainWindow()
    {
        InitializeComponent();
    }

    // Wywoływana przy zaznaczeniu i przy odznaczeniu każdego dodatku
    private void Dodatek_Zmiana(object sender, RoutedEventArgs e)
    {
        Przelicz();
    }

    // „Zaznacz wszystkie” — tylko kliknięcie użytkownika, nie zmiana z kodu
    private void Wszystkie_Click(object sender, RoutedEventArgs e)
    {
        bool zaznacz = Wszystkie.IsChecked == true;

        foreach (CheckBox dodatek in Dodatki.Children)
        {
            dodatek.IsChecked = zaznacz;
        }
    }

    private void Regulamin_Zmiana(object sender, RoutedEventArgs e)
    {
        Zamow.IsEnabled = Regulamin.IsChecked == true;
    }

    private void Zamow_Click(object sender, RoutedEventArgs e)
    {
        string lista = "";

        foreach (CheckBox dodatek in Dodatki.Children)
        {
            if (dodatek.IsChecked == true)
            {
                lista += $"\n• {dodatek.Content}";
            }
        }

        if (lista == "")
        {
            lista = "\n(bez dodatków)";
        }

        MessageBox.Show($"Margherita{lista}\n\n{Suma.Text}", "Zamówienie przyjęte",
                        MessageBoxButton.OK, MessageBoxImage.Information);

        foreach (CheckBox dodatek in Dodatki.Children)
        {
            dodatek.IsChecked = false;
        }
        Regulamin.IsChecked = false;
    }

    private void Przelicz()
    {
        decimal suma = CenaPizzy;
        int zaznaczone = 0;

        foreach (CheckBox dodatek in Dodatki.Children)
        {
            if (dodatek.IsChecked == true)
            {
                suma += decimal.Parse($"{dodatek.Tag}");   // cenę wpisaliśmy sami w XAML
                zaznaczone++;
            }
        }

        // stan „Zaznacz wszystkie”: tak / nie / częściowo (null)
        if (zaznaczone == 0)
        {
            Wszystkie.IsChecked = false;
        }
        else if (zaznaczone == Dodatki.Children.Count)
        {
            Wszystkie.IsChecked = true;
        }
        else
        {
            Wszystkie.IsChecked = null;
        }

        Suma.Text = $"Do zapłaty: {suma:F2} zł";
    }
}

Co dzieje się po kolei

  • Sześć dodatków stoi w UniformGrid z dwiema kolumnami. Panel ma nazwę Dodatki, więc kod przechodzi po nich pętlą — same pola nazw nie potrzebują.
  • Cena dodatku jest w Tag. decimal.Parse jest tu bezpieczne, bo te wartości wpisaliśmy sami, a nie użytkownik.
  • Każdy dodatek ma Checked i Unchecked podpięte pod tę samą metodę. Ona tylko wywołuje Przelicz, które liczy wszystko od zera.
  • Przelicz ustawia też stan pola „Zaznacz wszystkie”: false, true albo null — w zależności od liczby zaznaczonych dodatków.
  • „Zaznacz wszystkie” reaguje na Click, a nie na Checked. Gdy Przelicz zmienia jego stan z kodu, Click się nie wywołuje, więc pole nie zaczyna samo przestawiać dodatków.
  • Po kliknięciu „Zaznacz wszystkie” w stanie częściowym pole przechodzi na „odznaczone” i odznacza wszystkie dodatki.
  • Zamow.IsEnabled = Regulamin.IsChecked == true; — jedna linia włącza i wyłącza przycisk.
  • Po zamówieniu kod odznacza wszystkie pola. Każde odznaczenie wywołuje Unchecked, więc cena i przycisk wracają do stanu początkowego bez dodatkowego kodu.

Sprawdź trzy rzeczy

  1. Zmień w „Zaznacz wszystkie” zdarzenie Click na Checked (i nazwę metody). Zaznacz dwa dodatki, a potem kliknij trzeci — zaczną się dziać dziwne rzeczy, bo zmiana z kodu uruchamia metodę zaznaczającą wszystko.
  2. Usuń Unchecked="Dodatek_Zmiana" przy szynce. Po odznaczeniu szynki cena się nie zmniejszy.
  3. Usuń IsThreeState="True". Przypisanie null z kodu nadal pokaże stan pośredni, ale użytkownik nie będzie mógł go wybrać kliknięciem.
ELEMENTY WBUDOWANE

Zestawienie elementów

ElementTypCo robi
ContentobjectOpis pola; kliknięcie w opis też przełącza pole.
IsCheckedbool?Stan pola: true, false lub null. W if porównuj z true.
IsThreeStateboolCzy użytkownik może kliknięciem wybrać stan null.
Checked, UncheckedzdarzeniaPo zaznaczeniu / odznaczeniu — także z kodu.
IndeterminatezdarzeniePo przejściu w stan null.
ClickzdarzenieTylko po kliknięciu przez użytkownika.
FontWeights.Bold, FontWeights.NormalwartościGrubość czcionki ustawiana z kodu.
CZĘSTE BŁĘDY

Zanim utkniesz

ZapisProblem
if (Zgoda.IsChecked)CS0266 — bool? to nie bool. Napisz == true.
bool b = Zgoda.IsChecked;CS0266 — to samo. bool b = Zgoda.IsChecked == true;
if (Zgoda.IsChecked != false)Prawda także dla null — przy polu trójstanowym to zwykle nie to, o co chodzi.
tylko Checked, bez UncheckedPo odznaczeniu wynik się nie aktualizuje.
IsChecked="True" + Checked zmieniające kontrolkę niżej w XAMLNullReferenceException przy starcie okna.
„Zaznacz wszystkie” na CheckedZmiana stanu z kodu znów zaznacza wszystkie pola — pętla zależności.
<CheckBox Text="…"/>Błąd XAML — opis to Content.
ZADANIA

Zadania

Każde zadanie wykonaj w nowym projekcie Aplikacja WPF. Tam, gdzie to możliwe, wynik zmieniaj na żywo, bez przycisku „Oblicz”. Wskazówkę otwórz dopiero wtedy, gdy utkniesz.

ZAD 1Koła zainteresowań★☆☆

Uczeń zapisuje się na koła pozalekcyjne. W oknie osiem pól (np. robotyka, e-sport, chór, siatkówka, fotografia, szachy, teatr, programowanie gier) w dwóch kolumnach. Pod nimi napis „Wybrano 2 z 8” zmieniany na żywo. Można wybrać najwyżej 3 koła — gdy uczeń zaznaczy czwarte, program od razu je odznacza i wyświetla czerwony komunikat. Przycisk Zapisz pokazuje w MessageBox listę wybranych kół; przy braku wyboru pokazuje ostrzeżenie.

pokaż wskazówkę
  • Kliknięte pole to (CheckBox)sender — to je odznaczysz po przekroczeniu limitu.
  • Odznaczenie z kodu wywoła Unchecked, więc licznik sam się poprawi.
ZAD 2Podgląd stylu tekstu★☆☆

Pole tekstowe na napis i duży podgląd pod spodem. Pola wyboru: Pogrubienie, Kursywa, Duża czcionka (28 zamiast 16), Ciemne tło (czarne tło, biały tekst), WIELKIE LITERY. Każda zmiana pola albo tekstu od razu odświeża podgląd. Przycisk Resetuj odznacza wszystkie pola.

pokaż wskazówkę
  • Jedna metoda OdswiezPodglad() ustawia wszystko na podstawie stanów pól; wywołuj ją z Checked, Unchecked i TextChanged.
  • Kursywa z kodu: FontStyles.Italic i FontStyles.Normal.
ZAD 3Wypożyczalnia rowerów★★☆

Rower kosztuje 12 zł za godzinę. Pole Liczba godzin (1–12) i opcje: kask (+5 zł jednorazowo), koszyk (+3 zł), fotelik dla dziecka (+4 zł za godzinę), ubezpieczenie (+10% od całej kwoty). Koszt przelicza się przy każdej zmianie pola godzin i każdej opcji. Przy niepoprawnej liczbie godzin zamiast kwoty widać „Podaj liczbę godzin od 1 do 12”. Wynik pokazuje rozbicie: „Rower: 36,00 zł, dodatki: 20,00 zł, ubezpieczenie: 5,60 zł, razem: 61,60 zł”.

pokaż wskazówkę
  • Ubezpieczenie liczysz na końcu, od sumy roweru i dodatków.
  • TextChanged może przyjść przed utworzeniem napisu z wynikiem — sprawdź null albo nie wpisuj tekstu startowego w XAML.
ZAD 4Lista kontrolna przed egzaminem★★☆

Lista ośmiu czynności przed egzaminem praktycznym (np. „Sprawdzono numer stanowiska”, „Utworzono folder z numerem PESEL”, „Uruchomiono Visual Studio”…). Nad listą napis „Gotowe: 5 / 8”. Gdy wszystkie są zaznaczone, pod listą pojawia się zielona ramka „Możesz zaczynać. Powodzenia!”. Pole Ukryj wykonane chowa zaznaczone pozycje (i pokazuje je po odznaczeniu tej opcji).

Przed egzaminem–□✕
Gotowe: 8 / 8
Sprawdzono numer stanowiska
Utworzono folder z numerem PESEL
…
Możesz zaczynać. Powodzenia!
Ukryj wykonane
pokaż wskazówkę
  • Pozycje listy i pole Ukryj wykonane muszą być w różnych panelach — inaczej pętla policzy też to pole.
  • Widoczność każdej pozycji: Collapsed, gdy jest zaznaczona i opcja ukrywania jest włączona; w przeciwnym razie Visible.
ZAD 5Zgody przy rejestracji★★☆

Formularz rejestracji w sklepie internetowym: pole e-mail i cztery zgody — regulamin (wymagana), polityka prywatności (wymagana), newsletter i oferty partnerów (opcjonalne). Wymagane mają czerwoną gwiazdkę. Pole „Zaznacz wszystkie zgody” działa jak w przykładzie (z trzecim stanem). Przycisk Załóż konto jest aktywny tylko przy obu wymaganych zgodach i e-mailu zawierającym @. Po kliknięciu MessageBox podsumowuje, na co klient się zgodził.

pokaż wskazówkę
  • Gwiazdkę zrobisz w Content pola z TextBlock i Run (lekcja 02) — Content może być kontrolką, nie tylko tekstem.
  • Stan przycisku licz w jednej metodzie wywoływanej przez zgody i TextChanged pola e-mail.
ZAD 6Konfigurator komputera★★★

Zestaw bazowy kosztuje 2500 zł. Opcje: karta graficzna RTX (+1800 zł), zasilacz 750 W (+350 zł), dysk 2 TB (+420 zł), chłodzenie wodne (+500 zł), chłodzenie powietrzne (+150 zł), Windows 11 Pro (+600 zł). Zasady:

  • karta RTX wymaga zasilacza 750 W — po jej zaznaczeniu zasilacz zaznacza się sam i nie da się go odznaczyć, dopóki karta jest wybrana;
  • chłodzenie wodne i powietrzne wykluczają się — zaznaczenie jednego odznacza drugie;
  • pod opcjami lista uwag, np. „Zasilacz dodany automatycznie (wymaga go karta RTX)”.

Cena aktualizuje się na żywo. Przycisk Kopiuj specyfikację pokazuje pełną listę komponentów z cenami w MessageBox.

pokaż wskazówkę
  • Zasilacz „nie do odznaczenia” = IsEnabled = false przy zaznaczonej karcie.
  • Wykluczanie zrób w osobnej metodzie podpiętej pod Checked obu rodzajów chłodzenia: jeśli sender to chłodzenie wodne, odznacz powietrzne, i odwrotnie.
  • Kolejność ma znaczenie: najpierw zastosuj zasady, na końcu policz cenę.
PODSUMOWANIE

Co trzeba zapamiętać

  • CheckBox to niezależne „tak / nie” — każde pole osobno. Opis w Content.
  • IsChecked ma typ bool?; w warunku pisz IsChecked == true.
  • Checked i Unchecked podpinaj pod jedną metodę, która przelicza wszystko od nowa.
  • Click reaguje tylko na użytkownika; Checked — także na zmiany z kodu.
  • IsThreeState i wartość null to stan „częściowo” — typowo w „Zaznacz wszystkie”.
  • Grupa pól w nazwanym panelu + foreach + dane w Tag = kod bez powtórzeń.

Dokumentacja: Microsoft Learn — klasa CheckBox.

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