Aplikacje desktopowe · Kontrolki II

CheckBox, RadioButton, ComboBox i ListBox: kontrolki wyboru

Formularze rzadko ograniczają się do samego tekstu. Użytkownik zaznacza opcje, wybiera jedną z kilku albo wskazuje element na liście. Poznasz cztery kontrolki wyboru i zbudujesz ankietę, która naprawdę działa.

  • około 45 minut
  • WPF · CheckBox · RadioButton · ComboBox
  • INF.04.5.3

Po tej lekcji: dobrać kontrolkę do rodzaju wyboru, odczytać IsChecked, SelectedItem i SelectedIndex oraz poprawnie porównać wartość typu bool?.

Zacznijmy od sklepu

Który wybór wymaga której kontrolki?

Formularz zamówienia w sklepie internetowym używa dokładnie tego zestawu: CheckBox przy „akceptuję regulamin”, RadioButton przy formie dostawy (tylko jedna opcja), ComboBox przy wyborze kraju (dużo opcji, więc lista rozwijana oszczędza miejsce) i tabela z produktami w koszyku.

Pytanie, które zadajesz zawsze, brzmi: *ile opcji można wybrać i ile jest wszystkich?*

1 · Dobierz do potrzeby

Jedna kontrolka na jeden rodzaj wyboru

CheckBoxtak lub nie

Zaznaczenie jednej opcji. Można zaznaczyć dowolnie wiele pól naraz. Stan odczytasz z **IsChecked**.

Na przykład „Akceptuję regulamin”

RadioButtonjedna z kilku

Wybór jednej opcji z grupy. Zaznaczenie jednego odznacza pozostałe w tej samej grupie.

Na przykład kurier, paczkomat albo odbiór osobisty

ComboBoxlista rozwijana

Wybór jednej pozycji z długiej listy, przy małym zajmowanym miejscu. Wybór odczytasz z **SelectedItem lub SelectedIndex**.

Na przykład kraj, miasto, język

ListBox / DataGridlista i tabela

ListBox to lista w jednej kolumnie, DataGrid to tabela z wieloma kolumnami. W Windows Forms odpowiednikiem DataGrid jest DataGridView.

Na przykład koszyk z produktami

Laboratorium · ćwiczenie w przeglądarce, do którego odsyłają rozgrzewki na końcu lekcji

2 · Sprawdź w działaniu

Ankieta, w której wszystko jest podpięte

Zaznacz zgodę, a przycisk „Wyślij ankietę” się włączy. Przełączaj płeć i zobacz, że zaznaczenie jednej opcji odznacza drugą. Zmień język na liście i wyślij ankietę. W zakładce .cs zobaczysz, skąd biorą się te zachowania.

Ta interakcja wymaga włączonego JavaScriptu.

IsChecked ma typ bool? (może być też null), dlatego porównuj go jawnie: IsChecked == true.

3 · Pola wyboru

Właściwości CheckBox i RadioButton

WPFWindows FormsCo robi
IsCheckedCheckedCzy pole jest zaznaczone. W WPF typ bool?, więc może być też null (stan nieokreślony).
ContentTextOpis obok kwadracika lub kółka.
IsThreeState (tylko CheckBox)CheckStateWłącza trzeci, nieokreślony stan zaznaczenia.
GroupName (tylko RadioButton)wspólny kontenerOpcje o tej samej nazwie grupy wykluczają się wzajemnie.
zdarzenia Checked i UncheckedCheckedChangedReakcja na zmianę zaznaczenia.
W WPF CheckBox ma dwa zdarzenia (zaznaczono i odznaczono), często podpinane pod tę samą metodę.

4 · Listy

Właściwości ComboBox, ListBox i DataGrid

WPFWindows FormsCo robi
ItemsSource / ItemsItems / DataSourceKolekcja elementów listy. W WPF zwykle podpina się ją z kodu lub wiązaniem.
SelectedItemSelectedItemWybrany element (jego wartość), a nie numer.
SelectedIndexSelectedIndexNumer pozycji wybranego elementu, licząc od 0. Brak wyboru to −1.
IsEditableDropDownStyleCzy użytkownik może wpisać własny tekst.
SelectionModeSelectionModePojedynczy lub wielokrotny wybór (ListBox).
AutoGenerateColumns (DataGrid)AutoGenerateColumnsAutomatyczne kolumny z właściwości obiektów w kolekcji.
Do tabeli z danymi wrócimy w lekcji o listach i DataGrid. Tutaj liczy się, jak odczytać wybór z `ComboBox`.

5 · Wygląd

MainWindow.xaml

Wszystkie kontrolki, które czytasz lub zmieniasz w kodzie, mają x:Name. Pole ComboBox jest puste, bo jego elementy dodamy w kodzie C#.

MainWindow.xamlxaml
<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="360" 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>
  1. Checked i Unchecked Dwa zdarzenia wskazują tę samą metodę, bo w obu przypadkach trzeba odświeżyć stan przycisku.
  2. GroupName="Plec" Obie opcje należą do jednej grupy, więc zaznaczenie jednej odznacza drugą.
  3. IsEnabled="False" Przycisk „Wyślij” startuje wyłączony, aż do zaznaczenia zgody.

6 · Działanie

MainWindow.xaml.cs

Konstruktor wypełnia listę i wybiera pierwszą pozycję. Pozostałe metody reagują na zaznaczenie zgody, zmianę języka i kliknięcie przycisku.

MainWindow.xaml.cscsharp
using System.Windows;
using System.Windows.Controls;

namespace KontrolkiApp2;

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

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

    private void ListaJezykow_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        EtykietaWynik.Text = $"Wybrany język: {ListaJezykow.SelectedItem}";
    }

    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}.";
    }
}
  1. Items.Add("C#") Dodaje pozycję do listy. Taki zapis jest typowy, gdy lista pochodzi z bazy lub pliku.
  2. SelectedIndex = 0 Zaznacza pierwszą pozycję. Bez tego lista startuje bez wyboru.
  3. IsChecked == true Porównanie z true, bo IsChecked jest typu bool?. Zapis if (PoleZgoda.IsChecked) się nie skompiluje.
  4. SelectedItem?.ToString() ?? "(nie wybrano)" Jeśli nic nie wybrano, SelectedItem jest null. Operator ?. bezpiecznie go pomija, a ?? podstawia wartość zastępczą.
  5. warunek ? a : b Operator warunkowy: gdy zaznaczono „Kobieta”, wynikiem jest „Kobieta”, w przeciwnym razie „Mężczyzna”.

Do zadań

Wiele zaznaczonych pozycji oraz kolejne wiersze w DataGrid

Dwa zadania wymagają czegoś, czego nie ma w przykładzie: odczytu wielu zaznaczonych pozycji oraz tabeli, która rośnie po każdym kliknięciu.

MainWindow.xaml.cscsharp
using System.Collections.ObjectModel;   // ObservableCollection

// ListBox z SelectionMode="Multiple" i pozycjami <ListBoxItem Content="Sport" />
var wybrane = new List<string>();
foreach (ListBoxItem pozycja in ListaZainteresowan.SelectedItems)
    wybrane.Add(pozycja.Content.ToString()!);
Podsumowanie.Text = "Zainteresowania: " + string.Join(", ", wybrane);

// Tabela z historią odpowiedzi
public class Odpowiedz
{
    public bool Zgoda { get; set; }
    public string Poziom { get; set; } = "";
    public string Dzial { get; set; } = "";
}

private readonly ObservableCollection<Odpowiedz> odpowiedzi = new();

public MainWindow()
{
    InitializeComponent();
    Historia.ItemsSource = odpowiedzi;           // Historia to x:Name kontrolki DataGrid
}

private void Wyslij_Click(object sender, RoutedEventArgs e)
{
    string dzial = (ComboDzial.SelectedItem as ComboBoxItem)?.Content.ToString() ?? "";
    odpowiedzi.Add(new Odpowiedz { Zgoda = true, Poziom = "Średni", Dzial = dzial });
}

// Przycisk wyłączony bez zgody: wywołaj to z Checked i Unchecked pola CheckBox
PrzyciskWyslij.IsEnabled = CheckZgoda.IsChecked == true;
  1. SelectedItems Kolekcja wszystkich zaznaczonych pozycji. Przechodzisz po niej pętlą foreach.
  2. ObservableCollection Lista, która sama powiadamia tabelę o nowych elementach. Add dopisuje wiersz i nie nadpisuje poprzednich.
  3. ComboDzial.SelectedItem as ComboBoxItem Pozycje wpisane w XAML są obiektami ComboBoxItem, a napis jest w ich Content.

7 · Czego unikać

Cztery pułapki kontrolek wyboru

`IsChecked` użyte wprost w warunku
Tak nie
if (PoleZgoda.IsChecked)
{
    // ...
}

Błąd kompilatora: nie można niejawnie przekonwertować typu bool? na bool.

Tak
if (PoleZgoda.IsChecked == true)
{
    // ...
}

bool? może mieć wartość null, więc kompilator wymaga jawnego porównania.

RadioButtony bez wspólnej grupy
Tak nie
<StackPanel>
  <RadioButton Content="Kurier" />
</StackPanel>
<StackPanel>
  <RadioButton Content="Paczkomat" />
</StackPanel>

Można zaznaczyć obie opcje naraz.

Tak
<RadioButton Content="Kurier" GroupName="Dostawa" />
<RadioButton Content="Paczkomat" GroupName="Dostawa" />

Opcje w różnych kontenerach działają niezależnie. Wspólny GroupName je łączy.

Pozycje ComboBoxa wpisane na sztywno
Tak nie
<ComboBox>
  <ComboBoxItem Content="C#" />
  <ComboBoxItem Content="Python" />
</ComboBox>

Zadziała, ale zmiana listy wymaga zmiany plików projektu.

Tak
ListaJezykow.Items.Add("C#");
ListaJezykow.Items.Add("Python");

W prawdziwych aplikacjach lista pochodzi z bazy lub pliku, więc ładuje się ją w kodzie lub przez wiązanie.

SelectedItem zamiast SelectedIndex (albo odwrotnie)
Tak nie
string jezyk = ListaJezykow.SelectedIndex;

Błąd kompilatora: nie można przekonwertować int na string.

Tak
string jezyk = ListaJezykow.SelectedItem?.ToString();
int numer = ListaJezykow.SelectedIndex;

SelectedItem zwraca element, a SelectedIndex jego numer od zera.

8 · Sprawdź się

Której kontrolki użyjesz?

Cztery sytuacje z formularzem.

Ta interakcja wymaga włączonego JavaScriptu.

Podsumowanie

Co warto zapamiętać

  1. CheckBox: tak lub nie, stan w IsChecked (typ bool?, porównuj z == true).
  2. RadioButton: jedna opcja z grupy, grupę tworzy wspólny kontener lub GroupName.
  3. ComboBox: lista rozwijana, SelectedItem to element, a SelectedIndex to numer od zera.
  4. ListBox to prosta lista, a DataGrid to tabela z wieloma kolumnami.
  5. Zdarzenia: Checked i Unchecked (pola wyboru), SelectionChanged (listy).

Dalej na swoim koncie

Rozgrzewki, quiz i zadania są w biurku ucznia.

Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.

Zaloguj się →
Źródła i dalsza nauka