Lekcja 7. Kontrolki II — CheckBox, RadioButton, ComboBox, ListBox
ŚredniPo 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
Dodaj trzecią opcję RadioButton 'Wolę nie podawać' do grupy Plec.
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'.
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 if — if (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.