RadioButton – przycisk opcji
Poznasz RadioButton – kontrolkę do wyboru jednej opcji z grupy. Nauczysz się grupować przyciski, odczytywać który jest zaznaczony i budować formularze z wzajemnie wykluczającymi się opcjami.
Czym jest RadioButton?
RadioButton to okrągły przycisk wyboru – wybierając jedną opcję z grupy automatycznie odznaczasz wszystkie inne w tej samej grupie. Tylko jedna opcja może być wybrana naraz.
Nazwa pochodzi od starych radioodbiorników z przyciskami stacji – wciśnięcie jednego przycisku automatycznie wysuwało wcześniej wciśnięty. Nie można było słuchać dwóch stacji naraz. To dokładnie zachowanie RadioButton: płeć, rozmiar, metoda płatności – zawsze tylko jedna opcja.
RadioButton – tylko jedna opcja z grupy może być zaznaczona.
CheckBox – każda opcja działa niezależnie, można zaznaczyć wiele.
Pytanie testowe: czy opcje wykluczają się wzajemnie?
„Płeć: kobieta/mężczyzna” → RadioButton.
„Zainteresowania: sport, muzyka, książki” → CheckBox.
<RadioButton Content="Kobieta" GroupName="Plec" /> <RadioButton Content="Mężczyzna" GroupName="Plec" />
GroupName – grupowanie
GroupName mówi WPF, które RadioButtony wykluczają się
wzajemnie. RadioButtony z tym samym GroupName
należą do jednej grupy – zaznaczenie jednego odznacza resztę.
Bez GroupName – częsta pułapka dla początkujących
Jeśli nie podasz GroupName, WPF domyślnie grupuje
RadioButtony na podstawie ich wspólnego rodzica
(kontenera, w którym się znajdują). To może działać przypadkowo dobrze
albo wprowadzić w błąd, gdy masz więcej niż jedną grupę w tym samym kontenerze.
<StackPanel> <!-- Płeć --> <RadioButton Content="Kobieta" /> <RadioButton Content="Mężczyzna" /> <!-- Rozmiar koszulki --> <RadioButton Content="S" /> <RadioButton Content="M" /> <RadioButton Content="L" /> </StackPanel> <!-- BŁĄD! Wszystkie pięć RadioButtonów jest w tej samej grupie (bo mają tego samego rodzica StackPanel)! Zaznaczenie "S" odznaczy "Kobieta"! -->
Z GroupName – poprawne rozwiązanie
<StackPanel> <!-- Płeć – grupa "Plec" --> <RadioButton Content="Kobieta" GroupName="Plec" /> <RadioButton Content="Mężczyzna" GroupName="Plec" /> <!-- Rozmiar – grupa "Rozmiar" --> <RadioButton Content="S" GroupName="Rozmiar" /> <RadioButton Content="M" GroupName="Rozmiar" /> <RadioButton Content="L" GroupName="Rozmiar" /> </StackPanel> <!-- Teraz są dwie niezależne grupy – wybór płci nie wpływa na rozmiar -->
Zawsze ustawiaj GroupName jawnie –
nawet jeśli masz tylko jedną grupę w oknie. To jasno komunikuje intencję
w kodzie i zabezpiecza przed problemami, gdy później dodasz drugą grupę
do tego samego kontenera.
Alternatywa: grupowanie przez osobne kontenery
Zamiast (albo razem z) GroupName, możesz fizycznie
rozdzielić grupy do osobnych kontenerów – np. dwóch GroupBoxów.
To dodatkowo porządkuje wizualnie formularz.
<StackPanel> <GroupBox Header="Płeć" Margin="0,0,0,10"> <StackPanel> <RadioButton x:Name="rbKobieta" Content="Kobieta" GroupName="Plec" Margin="0,4" /> <RadioButton x:Name="rbMezczyzna" Content="Mężczyzna" GroupName="Plec" Margin="0,4" /> </StackPanel> </GroupBox> <GroupBox Header="Rozmiar koszulki"> <StackPanel Orientation="Horizontal"> <RadioButton x:Name="rbS" Content="S" GroupName="Rozmiar" Margin="0,0,12,0" /> <RadioButton x:Name="rbM" Content="M" GroupName="Rozmiar" Margin="0,0,12,0" /> <RadioButton x:Name="rbL" Content="L" GroupName="Rozmiar" /> </StackPanel> </GroupBox> </StackPanel>
Zwróć uwagę, że nawet w osobnych GroupBoxach wciąż podajemy
GroupName. To dobra praktyka – wizualne rozdzielenie
(GroupBox) nie zastępuje logicznego grupowania (GroupName),
a obie rzeczy razem dają najlepszy, najbardziej przewidywalny efekt.
IsChecked – ustawianie domyślnej opcji
Dobrą praktyką jest ustawienie jednej opcji jako domyślnie zaznaczonej – użytkownik nie zostaje z „pustym” wyborem.
<RadioButton x:Name="rbKurier" Content="Dostawa kurierem" GroupName="Dostawa" IsChecked="True" /> <!-- domyślnie wybrana --> <RadioButton x:Name="rbOdbior" Content="Odbiór osobisty" GroupName="Dostawa" /> <RadioButton x:Name="rbPaczkomat" Content="Paczkomat" GroupName="Dostawa" />
Jeśli ustawisz IsChecked="True" na więcej niż jednym
RadioButton w tej samej grupie, WPF respektuje tylko ostatni –
poprzednie automatycznie się odznaczają. Dla czytelności kodu
ustawiaj IsChecked="True" tylko na jednym.
Możesz też ustawić domyślną opcję z C#, np. wczytaną z zapisanych ustawień:
private void Window_Loaded(object sender, RoutedEventArgs e) { // Np. na podstawie zapisanych wcześniej preferencji użytkownika rbPaczkomat.IsChecked = true; }
Zdarzenie Checked
Podobnie jak CheckBox, RadioButton ma zdarzenie Checked,
które odpala się gdy dana opcja zostanie zaznaczona.
Przydaje się do natychmiastowej reakcji na zmianę wyboru.
<RadioButton x:Name="rbKurier" Content="Dostawa kurierem (15 zł)" GroupName="Dostawa" Checked="Dostawa_Checked" /> <RadioButton x:Name="rbOdbior" Content="Odbiór osobisty (0 zł)" GroupName="Dostawa" Checked="Dostawa_Checked" /> <RadioButton x:Name="rbPaczkomat" Content="Paczkomat (10 zł)" GroupName="Dostawa" Checked="Dostawa_Checked" />
private void Dostawa_Checked(object sender, RoutedEventArgs e) { // sender to konkretny RadioButton, który właśnie został zaznaczony RadioButton wybrany = (RadioButton)sender; string opis = wybrany.Content.ToString(); lblPodsumowanie.Text = $"Wybrano: {opis}"; }
Gdy zmieniasz wybór z „Kurier” na „Odbiór”, zdarzenie Checked
odpala się tylko dla „Odbiór” (ta, która staje się zaznaczona).
Dla „Kurier” odpaliłoby się zdarzenie Unchecked, jeśli byś
je podpiął – ale w praktyce przy RadioButton zwykle wystarczy obsłużyć
tylko Checked.
Odczyt zaznaczonej opcji w C#
Sprawdzanie każdego po kolei
Najprostszy sposób – sprawdzasz IsChecked każdego
RadioButton z grupy po kolei.
private void btnZamow_Click(object sender, RoutedEventArgs e) { string metodaDostawy = ""; int kosztDostawy = 0; if (rbKurier.IsChecked == true) { metodaDostawy = "Kurier"; kosztDostawy = 15; } else if (rbOdbior.IsChecked == true) { metodaDostawy = "Odbiór osobisty"; kosztDostawy = 0; } else if (rbPaczkomat.IsChecked == true) { metodaDostawy = "Paczkomat"; kosztDostawy = 10; } MessageBox.Show($"Dostawa: {metodaDostawy}, koszt: {kosztDostawy} zł"); }
Tak jak w CheckBox, IsChecked w RadioButton jest typu
bool?. Dlatego porównanie == true
jest konieczne (samo if (rbKurier.IsChecked) nie skompiluje się).
Pętla po kontenerze – gdy grupa jest duża
Gdy masz wiele opcji, sprawdzanie każdej z osobna staje się uciążliwe. Lepiej przejść pętlą po wszystkich dzieciach kontenera.
private void btnZamow_Click(object sender, RoutedEventArgs e) { string wybrana = null; foreach (var dziecko in panelDostawa.Children) { if (dziecko is RadioButton rb && rb.IsChecked == true) { wybrana = rb.Content.ToString(); break; // znaleźliśmy, nie musimy dalej szukać } } if (wybrana == null) { MessageBox.Show("Wybierz metodę dostawy!"); return; } MessageBox.Show($"Wybrano: {wybrana}"); }
Wiele grup w jednym oknie
Formularz może mieć dowolnie wiele niezależnych grup –
każda z własnym, unikalnym GroupName.
<StackPanel> <TextBlock Text="Płeć:" FontWeight="Bold" /> <RadioButton Content="Kobieta" GroupName="Plec" Margin="0,2" /> <RadioButton Content="Mężczyzna" GroupName="Plec" Margin="0,2,0,10" /> <TextBlock Text="Poziom doświadczenia:" FontWeight="Bold" /> <RadioButton Content="Początkujący" GroupName="Poziom" Margin="0,2" /> <RadioButton Content="Średni" GroupName="Poziom" Margin="0,2" /> <RadioButton Content="Zaawansowany" GroupName="Poziom" Margin="0,2,0,10" /> <TextBlock Text="Preferowany czas zajęć:" FontWeight="Bold" /> <RadioButton Content="Rano" GroupName="Czas" Margin="0,2" /> <RadioButton Content="Popołudnie" GroupName="Czas" Margin="0,2" /> <RadioButton Content="Wieczorem" GroupName="Czas" Margin="0,2" /> </StackPanel> <!-- Trzy niezależne grupy – wybór jednej nie wpływa na pozostałe, nawet bez fizycznego rozdzielenia kontenerami -->
RadioButton vs ComboBox – kiedy co?
Oba pozwalają wybrać jedną opcję z wielu. Różnica jest głównie w sposobie wyświetlania i liczbie opcji.
| Kryterium | RadioButton | ComboBox |
|---|---|---|
| Liczba opcji | Mało (2-6) | Dużo (7+) |
| Widoczność wszystkich opcji | ✅ Wszystkie widoczne naraz | ❌ Trzeba rozwinąć |
| Zajmowane miejsce | Więcej (każda opcja w linii) | Mało (jedna linia) |
| Szybkość wyboru | ✅ Jedno kliknięcie | Dwa kliknięcia (rozwiń + wybierz) |
| Dobre przy | Płeć, tak/nie, mała liczba kategorii | Kraj, miasto, długie listy |
Jeśli opcji jest 2-5 i chcesz, żeby użytkownik widział wszystkie naraz – użyj RadioButton. Jeśli opcji jest więcej niż 6-7, albo zależy Ci na zajmowaniu mniej miejsca – użyj ComboBox.
Praktyczny przykład – kalkulator dostawy
<StackPanel Margin="20" Width="280"> <TextBlock Text="Wybierz metodę dostawy:" FontWeight="Bold" Margin="0,0,0,10" /> <RadioButton x:Name="rbKurier" Content="Kurier (15 zł, 1-2 dni)" GroupName="Dostawa" IsChecked="True" Tag="15" Margin="0,4" Checked="Dostawa_Checked" /> <RadioButton x:Name="rbPaczkomat" Content="Paczkomat (10 zł, 2-3 dni)" GroupName="Dostawa" Tag="10" Margin="0,4" Checked="Dostawa_Checked" /> <RadioButton x:Name="rbOdbior" Content="Odbiór osobisty (0 zł)" GroupName="Dostawa" Tag="0" Margin="0,4" Checked="Dostawa_Checked" /> <TextBlock x:Name="lblKoszt" FontSize="16" FontWeight="Bold" Foreground="SteelBlue" Margin="0,16,0,0" /> </StackPanel>
private void Dostawa_Checked(object sender, RoutedEventArgs e) { // lblKoszt może być jeszcze null przy pierwszym wywołaniu (gdy XAML inicjalizuje IsChecked="True") if (lblKoszt == null) return; RadioButton wybrany = (RadioButton)sender; int koszt = int.Parse(wybrany.Tag.ToString()); lblKoszt.Text = koszt == 0 ? "Koszt dostawy: bezpłatnie" : $"Koszt dostawy: {koszt} zł"; }
Częste błędy
❌ Błąd 1: Zapomniany GroupName
❌ Wszystko w jednej grupie domyślnej
<StackPanel> <RadioButton Content="Tak" /> <RadioButton Content="Nie" /> <RadioButton Content="S" /> <RadioButton Content="M" /> <!-- Wszystkie 4 w jednej grupie! --> </StackPanel>
✅ Jawny GroupName na każdej grupie
<RadioButton Content="Tak" GroupName="Zgoda" /> <RadioButton Content="Nie" GroupName="Zgoda" /> <RadioButton Content="S" GroupName="Rozmiar" /> <RadioButton Content="M" GroupName="Rozmiar" />
❌ Błąd 2: Używanie CheckBox tam, gdzie powinien być RadioButton
❌ Można zaznaczyć kilka opcji
<!-- Metoda płatności –
powinna być jedna! -->
<CheckBox Content="Karta" />
<CheckBox Content="BLIK" />
<CheckBox Content="Gotówka" />
✅ RadioButton wymusza jeden wybór
<RadioButton Content="Karta"
GroupName="Platnosc" />
<RadioButton Content="BLIK"
GroupName="Platnosc" />
<RadioButton Content="Gotówka"
GroupName="Platnosc" />
❌ Błąd 3: Wyjątek przy odczycie Tag przed sprawdzeniem null
❌ NullReferenceException
int koszt = int.Parse(rb.Tag.ToString()); // Jeśli zapomniano dodać Tag="..." // w XAML – wyjątek!
✅ Sprawdzenie czy Tag istnieje
if (rb.Tag != null)
{
int koszt = int.Parse(rb.Tag.ToString());
}
❌ Błąd 4: Brak żadnej opcji domyślnie zaznaczonej
❌ Użytkownik może nic nie wybrać
<RadioButton Content="Kobieta"
GroupName="Plec" />
<RadioButton Content="Mężczyzna"
GroupName="Plec" />
<!-- Brak IsChecked="True" –
przy starcie żadna nie zaznaczona,
trzeba osobno walidować w C# -->
✅ Jedna opcja domyślna LUB walidacja
<RadioButton Content="Kobieta"
GroupName="Plec"
IsChecked="True" />
<RadioButton Content="Mężczyzna"
GroupName="Plec" />
Zadania do wykonania
Stwórz formularz z dwiema grupami RadioButtonów: „Poziom trudności” (Łatwy, Średni, Trudny) i „Liczba pytań” (10, 20, 30). Po kliknięciu przycisku „Start” wyświetl MessageBox z wybranymi opcjami obu grup.
Stwórz przełącznik motywu: trzy RadioButtony „Jasny”, „Ciemny”, „Niebieski”.
Po wybraniu opcji natychmiast (zdarzenie Checked)
zmień tło okna na odpowiedni kolor.
Zbuduj kalkulator dostawy (jak w przykładzie z sekcji 9), ale rozbuduj go: dodaj drugą grupę RadioButtonów „Sposób płatności” (Karta, BLIK, Gotówka przy odbiorze – ta opcja dostępna tylko gdy wybrano „Odbiór osobisty”, inaczej wyłączona przez IsEnabled). Pokaż łączne podsumowanie w TextBlock.
Stwórz mini-quiz z 3 pytaniami, gdzie każde pytanie ma 4 odpowiedzi jako RadioButtony w osobnej grupie (np. GroupName=”Pytanie1″, „Pytanie2”, „Pytanie3”). Przycisk „Sprawdź wynik” liczy ile odpowiedzi było poprawnych (porównaj IsChecked każdej poprawnej odpowiedzi) i wyświetla wynik w formacie „Wynik: 2/3”.