CheckBox – pole wyboru
Poznasz CheckBox – kontrolkę z polem do zaznaczenia. Nauczysz się odczytywać jej stan w C#, korzystać z trzeciego, nieokreślonego stanu oraz grupować wiele checkboxów w jedną logiczną całość.
Czym jest CheckBox?
CheckBox to mały kwadratowy przełącznik, który użytkownik może zaznaczyć (✓) lub odznaczyć. Służy do włączania/wyłączania opcji – „Zapamiętaj mnie”, „Akceptuję regulamin”, „Pokaż hasło”.
CheckBox to jak lista zakupów na papierze, gdzie zaznaczasz
✓ przy kupionym produkcie. Każdy checkbox działa
niezależnie od innych – możesz zaznaczyć
jeden, wszystkie albo żaden.
To różni go od RadioButton, gdzie tylko jedna
opcja z grupy może być wybrana.
<CheckBox x:Name="chkZapamietaj" Content="Zapamiętaj mnie" />
IsChecked – stan zaznaczenia
Ustawianie domyślnego stanu w XAML
<!-- Domyślnie zaznaczony --> <CheckBox x:Name="chkNewsletter" Content="Wysyłaj mi newsletter" IsChecked="True" /> <!-- Domyślnie odznaczony (to też wartość domyślna, można pominąć) --> <CheckBox x:Name="chkRegulamin" Content="Akceptuję regulamin" IsChecked="False" />
Odczyt i zapis w C#
private void btnZapisz_Click(object sender, RoutedEventArgs e) { // IsChecked to bool? (nullable bool) – może być true, false lub null if (chkRegulamin.IsChecked == true) { lblWynik.Text = "Regulamin zaakceptowany"; } else { lblWynik.Text = "Musisz zaakceptować regulamin!"; } }
// Zaznaczenie / odznaczenie z kodu chkNewsletter.IsChecked = true; chkNewsletter.IsChecked = false; // Przełączenie na odwrotny stan chkNewsletter.IsChecked = !chkNewsletter.IsChecked;
IsChecked jest typu bool? (nullable bool) –
może mieć wartość true, false albo null.
Dlatego porównanie if (chkRegulamin.IsChecked == true)
jest bezpieczniejsze niż if (chkRegulamin.IsChecked)
– to drugie nie skompiluje się bez dodatkowej konwersji,
bo bool? nie jest tym samym co bool.
Trzy stany – IsThreeState
Domyślnie CheckBox ma dwa stany: zaznaczony i odznaczony.
Ustawiając IsThreeState="True" dodajesz
trzeci stan – nieokreślony (wyświetlany jako wypełniony kwadrat
bez ptaszka, czasem zwany „indeterminate”).
| Stan | IsChecked | Wygląd |
|---|---|---|
| Zaznaczony | true | ☑ ptaszek |
| Odznaczony | false | ☐ pusty kwadrat |
| Nieokreślony | null | ▪ wypełniony kwadrat |
<CheckBox x:Name="chkWybierzWszystko" Content="Zaznacz wszystkie" IsThreeState="True" /> <!-- Kliknięcia cyklicznie przechodzą: odznaczony → zaznaczony → nieokreślony → odznaczony → ... -->
Dlaczego bool? (nullable bool)?
Trzeci stan to właśnie powód, dla którego IsChecked
jest typu bool?, a nie zwykłego bool.
Zwykły bool może być tylko true lub false –
nie ma miejsca na trzecią opcję. bool? dodaje możliwość
null, reprezentującego stan „nieokreślony”.
Stan nieokreślony w praktyce
Najczęstsze zastosowanie trzeciego stanu to checkbox „Zaznacz wszystko” nad listą innych checkboxów. Gdy część jest zaznaczona, a część nie – główny checkbox pokazuje stan nieokreślony.
private void SprawdzStan() { if (chkWybierzWszystko.IsChecked == true) { lblInfo.Text = "Wszystkie zaznaczone"; } else if (chkWybierzWszystko.IsChecked == false) { lblInfo.Text = "Żadne nie zaznaczone"; } else // IsChecked == null { lblInfo.Text = "Część zaznaczona, część nie"; } }
Zdarzenia Checked / Unchecked / Indeterminate
CheckBox ma trzy osobne zdarzenia, odpowiadające trzem stanom.
Możesz podpiąć każde z osobna, albo użyć jednego wspólnego
zdarzenia Click, jeśli nie potrzebujesz rozróżniać stanów.
<CheckBox x:Name="chkOpcja" Content="Włącz funkcję" IsThreeState="True" Checked="chkOpcja_Checked" Unchecked="chkOpcja_Unchecked" Indeterminate="chkOpcja_Indeterminate" />
private void chkOpcja_Checked(object sender, RoutedEventArgs e) { lblInfo.Text = "Funkcja włączona"; lblInfo.Foreground = Brushes.LimeGreen; } private void chkOpcja_Unchecked(object sender, RoutedEventArgs e) { lblInfo.Text = "Funkcja wyłączona"; lblInfo.Foreground = Brushes.Gray; } private void chkOpcja_Indeterminate(object sender, RoutedEventArgs e) { lblInfo.Text = "Stan nieokreślony"; lblInfo.Foreground = Brushes.Gold; }
Jeśli CheckBox ma tylko dwa stany (bez IsThreeState),
wygodniej jest użyć jednego zdarzenia Click i sprawdzić
IsChecked w środku – tak jak robiliśmy w sekcji 2.
Trzy osobne zdarzenia mają sens głównie przy trzystanowych checkboxach.
Odczyt stanu wielu CheckBoxów
Typowy formularz ma kilka checkboxów – np. wybór zainteresowań, opcji dodatkowych. Oto jak odczytać je wszystkie razem.
<StackPanel> <TextBlock Text="Wybierz zainteresowania:" Margin="0,0,0,8" /> <CheckBox x:Name="chkProgramowanie" Content="Programowanie" Margin="0,4" /> <CheckBox x:Name="chkElektronika" Content="Elektronika" Margin="0,4" /> <CheckBox x:Name="chkGry" Content="Gry komputerowe" Margin="0,4" /> <CheckBox x:Name="chkSport" Content="Sport" Margin="0,4" /> <Button Content="Pokaż wybrane" Margin="0,12,0,0" Click="btnPokaz_Click" /> </StackPanel>
private void btnPokaz_Click(object sender, RoutedEventArgs e) { List<string> wybrane = new List<string>(); if (chkProgramowanie.IsChecked == true) wybrane.Add("Programowanie"); if (chkElektronika.IsChecked == true) wybrane.Add("Elektronika"); if (chkGry.IsChecked == true) wybrane.Add("Gry komputerowe"); if (chkSport.IsChecked == true) wybrane.Add("Sport"); if (wybrane.Count == 0) { MessageBox.Show("Nie wybrano żadnego zainteresowania"); return; } string tekst = string.Join(", ", wybrane); MessageBox.Show($"Wybrano: {tekst}"); }
string.Join(", ", wybrane) łączy elementy listy
w jeden tekst, oddzielając je podanym separatorem.
Dla listy ["Sport", "Gry"] wynikiem będzie
"Sport, Gry". To wygodniejsze niż ręczne sklejanie
tekstu w pętli.
Grupowanie CheckBoxów
W przeciwieństwie do RadioButton, CheckBoxy nie mają wbudowanego
mechanizmu grupowania (jak GroupName) – każdy działa
niezależnie. Ale możemy je „grupować” na dwa sposoby: wizualnie i logicznie.
Grupowanie wizualne – GroupBox
Najprostszy sposób na pogrupowanie powiązanych checkboxów
to umieszczenie ich w GroupBox z nagłówkiem.
<GroupBox Header="Powiadomienia" Padding="10"> <StackPanel> <CheckBox x:Name="chkEmail" Content="Powiadomienia e-mail" Margin="0,4" /> <CheckBox x:Name="chkSms" Content="Powiadomienia SMS" Margin="0,4" /> <CheckBox x:Name="chkPush" Content="Powiadomienia push" Margin="0,4" /> </StackPanel> </GroupBox>
Grupowanie logiczne – „Zaznacz wszystko”
Częsty wzorzec: jeden „nadrzędny” checkbox kontroluje wszystkie inne.
To tutaj trzeci stan (IsThreeState) okazuje się przydatny.
<StackPanel> <CheckBox x:Name="chkWszystko" Content="Zaznacz wszystko" FontWeight="Bold" IsThreeState="True" Click="chkWszystko_Click" Margin="0,0,0,8" /> <CheckBox x:Name="chkPozycja1" Content="Pozycja 1" Margin="20,4,0,0" Click="chkPozycja_Click" /> <CheckBox x:Name="chkPozycja2" Content="Pozycja 2" Margin="20,4,0,0" Click="chkPozycja_Click" /> <CheckBox x:Name="chkPozycja3" Content="Pozycja 3" Margin="20,4,0,0" Click="chkPozycja_Click" /> </StackPanel>
private bool _aktualizujeProgramowo = false; private void chkWszystko_Click(object sender, RoutedEventArgs e) { // Gdy użytkownik kliknie główny checkbox, ustawiamy wszystkie pozycje bool nowyStan = chkWszystko.IsChecked == true; _aktualizujeProgramowo = true; // flaga zapobiegająca pętli zdarzeń chkPozycja1.IsChecked = nowyStan; chkPozycja2.IsChecked = nowyStan; chkPozycja3.IsChecked = nowyStan; _aktualizujeProgramowo = false; } private void chkPozycja_Click(object sender, RoutedEventArgs e) { if (_aktualizujeProgramowo) return; // ignoruj zmiany wywołane programowo // Sprawdzamy ile pozycji jest zaznaczonych int zaznaczone = 0; if (chkPozycja1.IsChecked == true) zaznaczone++; if (chkPozycja2.IsChecked == true) zaznaczone++; if (chkPozycja3.IsChecked == true) zaznaczone++; _aktualizujeProgramowo = true; if (zaznaczone == 0) chkWszystko.IsChecked = false; // żadna nie zaznaczona else if (zaznaczone == 3) chkWszystko.IsChecked = true; // wszystkie zaznaczone else chkWszystko.IsChecked = null; // część zaznaczona – stan nieokreślony _aktualizujeProgramowo = false; }
Gdy w kodzie ustawiamy chkPozycja1.IsChecked = ...,
to też odpala zdarzenie Click tej kontrolki!
Bez flagi _aktualizujeProgramowo wpadlibyśmy w
nieskończoną pętlę wzajemnych aktualizacji.
Flaga mówi: „te zmiany są wynikiem mojego kodu, zignoruj zdarzenie”.
Wygląd CheckBox
<CheckBox Content="Zgadzam się z warunkami" FontSize="14" Foreground="White" Margin="0,8" Cursor="Hand" /> <!-- Content może być też złożonym elementem, jak w Button --> <CheckBox> <CheckBox.Content> <StackPanel Orientation="Horizontal"> <TextBlock Text="Akceptuję " /> <TextBlock Text="regulamin" TextDecorations="Underline" Foreground="SteelBlue" /> </StackPanel> </CheckBox.Content> </CheckBox>
Praktyczny przykład – konfigurator pizzy
<StackPanel Margin="20"> <TextBlock Text="Wybierz dodatki:" FontWeight="Bold" Margin="0,0,0,10" /> <CheckBox x:Name="chkSer" Content="Dodatkowy ser (+3 zł)" Tag="3" Margin="0,4" Checked="Dodatek_Changed" Unchecked="Dodatek_Changed" /> <CheckBox x:Name="chkPiecarka" Content="Pieczarki (+2 zł)" Tag="2" Margin="0,4" Checked="Dodatek_Changed" Unchecked="Dodatek_Changed" /> <CheckBox x:Name="chkOliwki" Content="Oliwki (+2 zł)" Tag="2" Margin="0,4" Checked="Dodatek_Changed" Unchecked="Dodatek_Changed" /> <CheckBox x:Name="chkSzynka" Content="Szynka (+4 zł)" Tag="4" Margin="0,4" Checked="Dodatek_Changed" Unchecked="Dodatek_Changed" /> <TextBlock x:Name="lblCena" Text="Cena bazowa: 25 zł" FontSize="16" FontWeight="Bold" Margin="0,16,0,0" /> </StackPanel>
private const int CENA_BAZOWA = 25; private void Dodatek_Changed(object sender, RoutedEventArgs e) { int sumaDodatkow = 0; // Przechodzimy po wszystkich CheckBoxach w panelu foreach (var dziecko in panelGlowny.Children) { if (dziecko is CheckBox chk && chk.IsChecked == true && chk.Tag != null) { sumaDodatkow += int.Parse(chk.Tag.ToString()); } } int cenaCalkowita = CENA_BAZOWA + sumaDodatkow; lblCena.Text = $"Cena: {cenaCalkowita} zł (baza: {CENA_BAZOWA} zł + dodatki: {sumaDodatkow} zł)"; }
if (dziecko is CheckBox chk) to nowoczesny C# –
sprawdza czy dziecko jest typu CheckBox
i jeśli tak, jednocześnie przypisuje je do zmiennej
chk tego typu. To wygodniejsze niż osobne sprawdzenie
i rzutowanie.
Częste błędy
❌ Błąd 1: Porównanie IsChecked bez == true
❌ Błąd kompilacji
if (chkRegulamin.IsChecked)
{
// Błąd! IsChecked to bool?,
// nie da się go użyć
// bezpośrednio w if
}
✅ Porównanie z true
if (chkRegulamin.IsChecked == true)
{
// Działa poprawnie
}
❌ Błąd 2: Pętla nieskończona przy „Zaznacz wszystko”
❌ Brak flagi zabezpieczającej
private void chkWszystko_Click(...)
{
chkPoz1.IsChecked = true; // odpala
chkPoz2.IsChecked = true; // Click tych
chkPoz3.IsChecked = true; // checkboxów!
}
// Bez flagi może dojść do
// nieoczekiwanych, trudnych
// do debugowania zapętleń
✅ Z flagą zabezpieczającą
private void chkWszystko_Click(...)
{
_aktualizujeProgramowo = true;
chkPoz1.IsChecked = true;
chkPoz2.IsChecked = true;
_aktualizujeProgramowo = false;
}
❌ Błąd 3: Mylenie CheckBox z RadioButton
❌ Zły wybór kontrolki
<!-- Płeć: tylko jedna opcja możliwa –
ale użyto CheckBox! -->
<CheckBox Content="Kobieta" />
<CheckBox Content="Mężczyzna" />
<!-- Można zaznaczyć OBIE! -->
✅ RadioButton dla wyboru jednej opcji
<RadioButton Content="Kobieta"
GroupName="Plec" />
<RadioButton Content="Mężczyzna"
GroupName="Plec" />
<!-- Tylko jedna może być
zaznaczona -->
❌ Błąd 4: Zapomniany Tag przy liczeniu sumy
❌ NullReferenceException
int wartosc = int.Parse(chk.Tag.ToString()); // Jeśli zapomniałeś dodać // Tag="3" w XAML – // chk.Tag jest null, wyjątek!
✅ Sprawdzenie czy Tag istnieje
if (chk.Tag != null)
{
int wartosc = int.Parse(chk.Tag.ToString());
}
Zadania do wykonania
Stwórz okno z 4 CheckBoxami reprezentującymi dni weekendu pracy
(sobota, niedziela – pracuję rano, pracuję wieczorem, jestem dostępny telefonicznie).
Przycisk „Pokaż grafik” wyświetla MessageBox z listą zaznaczonych opcji
oddzielonych przecinkiem (użyj string.Join).
Stwórz CheckBox „Pokaż hasło” obok pola PasswordBox.
Po zaznaczeniu, obok PasswordBox pojawia się (Visibility.Visible)
zwykły TextBox z tą samą wartością, a PasswordBox się chowa –
i odwrotnie po odznaczeniu. (Podpowiedź: użyj zdarzeń Checked/Unchecked).
Zbuduj listę 5 zadań (TODO) z CheckBoxami i nadrzędnym checkboxem
„Zaznacz wszystkie” z IsThreeState="True".
Zaimplementuj pełną logikę: zaznaczenie nadrzędnego zaznacza wszystkie,
odznaczenie wszystkich daje stan nieokreślony na nadrzędnym,
zaznaczenie wszystkich pozycji ręcznie też ustawia nadrzędny na zaznaczony.
Rozbuduj przykład konfiguratora pizzy z sekcji 8: dodaj ComboBox z wyborem rozmiaru pizzy (Mała – cena bazowa 20 zł, Średnia – 25 zł, Duża – 32 zł) oraz przycisk „Zamów”, który wyświetla MessageBox z pełnym podsumowaniem zamówienia: rozmiar, lista wybranych dodatków i cena całkowita.