C# · WPF · INF.04 · Kontrolki

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.

RadioButton GroupName IsChecked wzajemne wykluczenie
1

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.

Analogia

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 vs CheckBox

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.

Najprostsza grupa RadioButton
<RadioButton Content="Kobieta"    GroupName="Plec" />
<RadioButton Content="Mężczyzna" GroupName="Plec" />
2

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.

Problem: dwie grupy bez GroupName w jednym StackPanel
<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

Rozwiązanie: jawne GroupName dla każdej grupy
<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 -->
Złota zasada

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.

3

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.

Dwie grupy w osobnych GroupBoxach
<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>
GroupBox + GroupName razem

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.

4

IsChecked – ustawianie domyślnej opcji

Dobrą praktyką jest ustawienie jednej opcji jako domyślnie zaznaczonej – użytkownik nie zostaje z „pustym” wyborem.

Domyślnie zaznaczona opcja
<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" />
Tylko jeden IsChecked=”True” w grupie!

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ń:

Ustawianie z C#
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Np. na podstawie zapisanych wcześniej preferencji użytkownika
    rbPaczkomat.IsChecked = true;
}
5

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.

XAML – podpięcie Checked
<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" />
C# – wspólna metoda dla wszystkich opcji grupy
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}";
}
Checked odpala się tylko dla nowo zaznaczonej opcji

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.

6

Odczyt zaznaczonej opcji w C#

Sprawdzanie każdego po kolei

Najprostszy sposób – sprawdzasz IsChecked każdego RadioButton z grupy po kolei.

Sprawdzanie po kliknięciu przycisku „Zamów”
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ł");
}
RadioButton.IsChecked to też bool?

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.

Pętla po RadioButtonach w kontenerze
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}");
}
7

Wiele grup w jednym oknie

Formularz może mieć dowolnie wiele niezależnych grup – każda z własnym, unikalnym GroupName.

Formularz z trzema niezależnymi grupami
<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 -->
8

RadioButton vs ComboBox – kiedy co?

Oba pozwalają wybrać jedną opcję z wielu. Różnica jest głównie w sposobie wyświetlania i liczbie opcji.

KryteriumRadioButtonComboBox
Liczba opcjiMało (2-6)Dużo (7+)
Widoczność wszystkich opcji✅ Wszystkie widoczne naraz❌ Trzeba rozwinąć
Zajmowane miejsceWięcej (każda opcja w linii)Mało (jedna linia)
Szybkość wyboru✅ Jedno kliknięcieDwa kliknięcia (rozwiń + wybierz)
Dobre przyPłeć, tak/nie, mała liczba kategoriiKraj, miasto, długie listy
Praktyczna zasada

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.

9

Praktyczny przykład – kalkulator dostawy

MainWindow.xaml
<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>
MainWindow.xaml.cs
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ł";
}
10

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" />
11

Zadania do wykonania

Zadanie 1 łatwe

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.

Zadanie 2 łatwe

Stwórz przełącznik motywu: trzy RadioButtony „Jasny”, „Ciemny”, „Niebieski”. Po wybraniu opcji natychmiast (zdarzenie Checked) zmień tło okna na odpowiedni kolor.

Zadanie 3 średnie

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.

Zadanie 4 średnie

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”.