Dataczwartek, 13 sierpnia 2026 Czas18:49:19
← Aplikacje desktopowe (WPF)

Lekcja 7. Kontrolki II — CheckBox, RadioButton, ComboBox, ListBox

Średni

Po co się tego uczymy?

Formularze rzadko ograniczają się do samego tekstu — użytkownik musi też zaznaczać opcje, wybierać jedną z kilku możliwości albo wskazywać element z listy. Ta lekcja dogłębnie omawia cztery kontrolki wyboru: CheckBox, RadioButton, ComboBox i ListBox/DataGrid — razem z pełnymi tabelami właściwości i odpowiednikami w Windows Forms.

Teoria

CheckBox — zaznaczenie opcji (tak/nie)

Właściwość WPF Odpowiednik Windows Forms Opis
IsChecked Checked Czy pole jest zaznaczone. W WPF to bool? (może być też null — stan "nieokreślony", trzeci stan checkboxa).
Content Text Opis wyświetlany obok kwadracika.
IsThreeState (brak wprost — wymaga CheckState) Włącza trzeci, "nieokreślony" stan zaznaczenia.

Windows Forms — zdarzenie po zmianie zaznaczenia:

private void chkZgoda_CheckedChanged(object sender, EventArgs e)
{
    btnDalej.Enabled = chkZgoda.Checked;
}

Efekt: przycisk "Dalej" włącza się dopiero, gdy użytkownik zaznaczy zgodę — bardzo częsty wzorzec przy formularzach z regulaminem.

RadioButton — wybór JEDNEJ opcji z grupy

RadioButtony działają w grupach — zaznaczenie jednego automatycznie odznacza pozostałe w tej samej grupie. W WPF grupę tworzy się, umieszczając przyciski we wspólnym kontenerze (np. StackPanel) lub nadając im tę samą wartość GroupName.

Właściwość WPF Odpowiednik Windows Forms Opis
IsChecked Checked Czy dana opcja jest wybrana.
Content Text Opis opcji.
GroupName (automatycznie wg wspólnego kontenera Panel) Nazwa grupy — przyciski o tej samej nazwie wykluczają się wzajemnie, nawet jeśli leżą w różnych kontenerach.

Windows Forms — odczyt wybranej opcji: string plec = rbKobieta.Checked ? "Kobieta" : "Mężczyzna";

ComboBox — lista rozwijana

Właściwość WPF Odpowiednik Windows Forms Opis
ItemsSource / Items Items Kolekcja elementów listy — w WPF zwykle podpinana z kodu przez binding (ItemsSource), a nie wpisywana ręcznie w XAML.
SelectedItem SelectedItem Aktualnie wybrany element (jego wartość, nie numer).
SelectedIndex SelectedIndex Numer pozycji wybranego elementu (licząc od 0).
IsEditable DropDownStyle = DropDown Czy użytkownik może wpisać własny tekst zamiast wybierać tylko z listy.

Windows Forms:

comboBox1.Items.AddRange(new[] { "Kot", "Pies", "Papuga" });
comboBox1.SelectedIndexChanged += (_, __) =>
{
    lblWybor.Text = comboBox1.SelectedItem?.ToString();
};

ListBox i DataGrid — listy i tabele danych

ListBox wyświetla prostą listę elementów (jedna kolumna), a DataGrid — pełną tabelę z wieloma kolumnami, jak arkusz kalkulacyjny. W Windows Forms odpowiednikiem DataGrid jest DataGridView.

Właściwość WPF Odpowiednik Windows Forms Opis
ItemsSource DataSource / Items Kolekcja danych źródłowych do wyświetlenia.
SelectedItem SelectedItem / SelectedRows Wybrany element (ListBox) lub wiersz (DataGrid).
SelectionMode SelectionMode Pojedynczy czy wielokrotny wybór elementów.
AutoGenerateColumns (tylko DataGrid) AutoGenerateColumns Czy kolumny tabeli mają być tworzone automatycznie na podstawie właściwości obiektów w kolekcji.

Windows Forms — wypełnienie prostej listy: listBox1.Items.AddRange(new[] { "Python", "C#", "Java" }); WPF — podpięcie kolekcji obiektów do tabeli jedną linijką: <DataGrid ItemsSource="{Binding Studenci}" AutoGenerateColumns="True"/> — każda właściwość publiczna klasy Student w kolekcji Studenci automatycznie stanie się osobną kolumną. Do pełnego omówienia list danych (sortowanie, filtrowanie, własne kolumny) wrócimy w osobnej, dedykowanej lekcji dalej w tym dziale.

Schemat

Kontrolki wyboru - kiedy ktorej uzyc:

CheckBox     -> zaznacz DOWOLNA liczbe opcji (0, 1, wiele)
RadioButton  -> wybierz DOKLADNIE JEDNA opcje z widocznej grupy
ComboBox     -> wybierz JEDNA opcje z listy ROZWIJANEJ (oszczedza miejsce)
ListBox      -> wybierz jedna lub wiele opcji z listy WIDOCZNEJ od razu
DataGrid     -> przegladaj/edytuj wiele REKORDOW z wieloma polami (tabela)

Przykład z życia

Formularz zamówienia w sklepie internetowym używa dokładnie tego zestawu: CheckBox przy "akceptuję regulamin", RadioButton przy wyborze formy dostawy (kurier / paczkomat / odbiór osobisty — TYLKO jedna opcja), ComboBox przy wyborze kraju (setki opcji, więc lista rozwijana oszczędza miejsce), i tabela (DataGrid) podsumowująca produkty w koszyku.

MainWindow.xaml

<Window x:Class="KontrolkiApp2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Ankieta" Height="320" Width="360">
    <StackPanel Margin="20">
        <CheckBox x:Name="PoleZgoda"
                  Content="Akceptuję regulamin"
                  Checked="PoleZgoda_Zmieniony"
                  Unchecked="PoleZgoda_Zmieniony"/>

        <TextBlock Text="Płeć:" Margin="0,15,0,5"/>
        <StackPanel Orientation="Horizontal">
            <RadioButton x:Name="OpcjaKobieta" Content="Kobieta" GroupName="Plec" Margin="0,0,15,0"/>
            <RadioButton x:Name="OpcjaMezczyzna" Content="Mężczyzna" GroupName="Plec"/>
        </StackPanel>

        <TextBlock Text="Ulubiony język programowania:" Margin="0,15,0,5"/>
        <ComboBox x:Name="ListaJezykow"
                  SelectionChanged="ListaJezykow_SelectionChanged"/>

        <Button x:Name="PrzyciskWyslij"
                Content="Wyślij ankietę"
                IsEnabled="False"
                Click="PrzyciskWyslij_Click"
                Margin="0,20,0,0"/>

        <TextBlock x:Name="EtykietaWynik" Margin="0,10,0,0" TextWrapping="Wrap"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace KontrolkiApp2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ListaJezykow.Items.Add("C#");
            ListaJezykow.Items.Add("Python");
            ListaJezykow.Items.Add("JavaScript");
            ListaJezykow.SelectedIndex = ;
        }

        private void PoleZgoda_Zmieniony(object sender, RoutedEventArgs e)
        {
            PrzyciskWyslij.IsEnabled = PoleZgoda.IsChecked == true;
        }

        private void ListaJezykow_SelectionChanged(object sender,
            System.Windows.Controls.SelectionChangedEventArgs e)
        {
            // Tu moglibysmy np. zapamietac wybor - na razie tylko demonstracja zdarzenia
        }

        private void PrzyciskWyslij_Click(object sender, RoutedEventArgs e)
        {
            string plec = OpcjaKobieta.IsChecked == true ? "Kobieta" : "Mężczyzna";
            string jezyk = ListaJezykow.SelectedItem?.ToString() ?? "(nie wybrano)";

            EtykietaWynik.Text = $"Dziękujemy! Płeć: {plec}, ulubiony język: {jezyk}.";
        }
    }
}

Komentarz i wyjaśnienie kodu

CheckBox ma podpięte DWA zdarzenia w XAML — Checked i Unchecked — obie wywołują tę samą metodę PoleZgoda_Zmieniony, bo w obu przypadkach chcemy zaktualizować stan przycisku "Wyślij". Zwróć uwagę na porównanie PoleZgoda.IsChecked == true zamiast samego PoleZgoda.IsChecked — to dlatego, że IsChecked jest typu bool? (nullable), więc bezpośrednie użycie w warunku if nie zawsze zadziała intuicyjnie.

Dwa RadioButton mają wspólne GroupName="Plec" — dzięki temu wybór jednego automatycznie odznacza drugi, mimo że oba leżą w tym samym StackPanel (który sam w sobie już by je zgrupował, ale jawne GroupName działa niezależnie od układu w XAML).

ComboBox jest wypełniany z kodu C# w konstruktorze (ListaJezykow.Items.Add(...)), a nie w XAML — to typowe podejście, gdy lista pochodzi z bazy danych albo innego źródła, a nie jest znana z góry. SelectedIndex = 0 ustawia pierwszą pozycję jako domyślnie zaznaczoną.

Ćwiczenie samodzielne

Utwórz nowy projekt WPF z powyższym kodem. Sprawdź: (1) czy przycisk "Wyślij" faktycznie jest zablokowany, dopóki nie zaznaczysz zgody; (2) czy zaznaczenie jednego RadioButtona odznacza drugi; (3) czy zmiana wyboru w ComboBoxie się rejestruje (możesz dodać tymczasowo MessageBox.Show w ListaJezykow_SelectionChanged, żeby to zobaczyć).

Zadania do pracy własnej

  1. Dodaj trzecią opcję RadioButton 'Wolę nie podawać' do grupy Plec.

  2. Dodaj do formularza ListBox z listą zainteresowań (Sport, Muzyka, Programowanie, Czytanie) z możliwością zaznaczenia WIELU pozycji naraz (ustaw SelectionMode="Multiple"), i wypisz wszystkie zaznaczone elementy w podsumowaniu po kliknięciu 'Wyślij'.

  3. Zbuduj ankietę z: CheckBox (zgoda), 3 RadioButton (poziom doświadczenia: Początkujący/Średni/Zaawansowany), ComboBox (ulubiony dział informatyki) i DataGrid pokazujący listę wcześniej 'wysłanych' odpowiedzi — po każdym kliknięciu 'Wyślij' nowa odpowiedź powinna DODAWAĆ SIĘ jako kolejny wiersz do kolekcji powiązanej z DataGrid (ObservableCollection), a nie nadpisywać poprzednią.

Typowe błędy

Porównanie IsChecked wprost w ifif (PoleZgoda.IsChecked) nie skompiluje się, bo IsChecked to bool?, a nie bool. Trzeba napisać if (PoleZgoda.IsChecked == true).

Brak wspólnego GroupName lub kontenera dla RadioButton — jeśli przyciski trafią do różnych, niepowiązanych kontenerów bez jawnego GroupName, będą działać niezależnie od siebie (będzie można zaznaczyć oba naraz), co psuje sens "wyboru jednej opcji".

Wypełnianie ComboBoxa w XAML na sztywno zamiast z kodu — działa dla listy, która się nigdy nie zmienia, ale w praktycznych aplikacjach dane najczęściej pochodzą z bazy lub pliku, więc trzeba je wczytać w konstruktorze albo przez binding.

Mylenie SelectedItem z SelectedIndex — pierwsze zwraca sam OBIEKT/wartość, drugie jego POZYCJĘ (liczbę) na liście — łatwo pomylić je przy odczycie wyniku.

Nawiązanie do egzaminu zawodowego

To dalsza realizacja INF.04.5.3. CheckBox, RadioButton i ComboBox to kontrolki wyboru pojawiające się niemal w każdym formularzu — od prostej ankiety po formularz rejestracyjny czy panel ustawień aplikacji, a ListBox/DataGrid to podstawa każdej listy danych, do której wrócimy szerzej w dalszej części działu.