C# · WPF · INF.04 · Kontrolki

ComboBox – lista rozwijana

Poznasz ComboBox – kontrolkę, która pokazuje listę opcji do wyboru. Nauczysz się wypełniać ją w XAML i z kodu C#, odczytywać zaznaczony element oraz reagować na zmianę wyboru.

ComboBox SelectedItem SelectionChanged IsEditable
1

Czym jest ComboBox?

ComboBox to kontrolka, która wygląda jak pole tekstowe z przyciskiem strzałki. Po kliknięciu strzałki rozwija się lista opcji, z których użytkownik wybiera jedną. Po wyborze lista chowa się, a w polu pojawia się wybrana pozycja.

Analogia

ComboBox to jak lista rozwijana w formularzu na stronie internetowej – np. wybór województwa, roku urodzenia, kategorii produktu. Widzisz tylko aktualnie wybraną opcję, a reszta chowa się do środka.

ComboBox pozwala wybrać dokładnie jedną opcję z listy. Jeśli chcesz umożliwić wielokrotny wybór – użyj ListBox z SelectionMode="Multiple".

2

Elementy w XAML

ComboBoxItem – ręczne dodawanie opcji

Najprostszy sposób to wpisanie opcji bezpośrednio w XAML, każda jako <ComboBoxItem>. Treść każdej pozycji ustawiasz właściwością Content.

ComboBox z opcjami w XAML
<ComboBox x:Name="cmbKolor"
          Width="180"
          Height="30">
    <ComboBoxItem Content="Czerwony" />
    <ComboBoxItem Content="Zielony"  />
    <ComboBoxItem Content="Niebieski"/>
    <ComboBoxItem Content="Żółty"    />
    <ComboBoxItem Content="Fioletowy"/>
</ComboBox>

Domyślnie zaznaczona pozycja

Żeby któraś opcja była zaznaczona od razu po uruchomieniu aplikacji, ustaw na niej IsSelected="True". Możesz też ustawić SelectedIndex na samym ComboBox.

Domyślny wybór – dwa sposoby
<!-- Sposób 1: IsSelected na konkretnym elemencie -->
<ComboBox x:Name="cmbKolor" Width="180">
    <ComboBoxItem Content="Czerwony"                   />
    <ComboBoxItem Content="Zielony"  IsSelected="True"  />   <!-- ← domyślny -->
    <ComboBoxItem Content="Niebieski"                  />
</ComboBox>

<!-- Sposób 2: SelectedIndex na ComboBox (0 = pierwszy element) -->
<ComboBox x:Name="cmbKategoria"
          Width="180"
          SelectedIndex="0">   <!-- zawsze zaznacza pierwszy element -->
    <ComboBoxItem Content="Elektronika" />
    <ComboBoxItem Content="Odzież"      />
    <ComboBoxItem Content="Sport"       />
</ComboBox>
Kiedy używać SelectedIndex=”0″?

Ustawianie SelectedIndex="0" to dobra praktyka – ComboBox nigdy nie jest „pusty” przy starcie. Alternatywą jest dodanie pierwszej pozycji jako „– wybierz –” i pozostawienie ComboBox bez domyślnego wyboru, ale wtedy trzeba pamiętać o walidacji w C#.

3

Wypełnianie z kodu C#

W prawdziwych aplikacjach opcje ComboBox pochodzą z danych – z listy obiektów, bazy danych, pliku. Dlatego wypełniamy go z C#, a nie z XAML.

Dodawanie tekstów (stringów)

Proste dodawanie napisów
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Dodawanie pojedynczych tekstów
    cmbKolor.Items.Add("Czerwony");
    cmbKolor.Items.Add("Zielony");
    cmbKolor.Items.Add("Niebieski");
    cmbKolor.Items.Add("Żółty");

    // Zaznaczamy pierwszą pozycję
    cmbKolor.SelectedIndex = 0;
}
Dodawanie z tablicy lub pętli
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Tablica z opcjami
    string[] kraje = { "Polska", "Niemcy", "Francja", "Włochy", "Hiszpania" };

    foreach (string kraj in kraje)
    {
        cmbKraj.Items.Add(kraj);
    }

    cmbKraj.SelectedIndex = 0;

    // Lata – pętla
    for (int rok = 2020; rok <= 2030; rok++)
    {
        cmbRok.Items.Add(rok);
    }

    cmbRok.SelectedIndex = 0;
}

Dodawanie obiektów własnej klasy

Do ComboBox możesz dodawać nie tylko teksty, ale dowolne obiekty. WPF wyświetla je wywołując metodę ToString() na każdym obiekcie. Dlatego warto ją nadpisać w swojej klasie.

Klasa Produkt z nadpisanym ToString()
public class Produkt
{
    public int    Id    { get; set; }
    public string Nazwa { get; set; }
    public double Cena  { get; set; }

    // WPF wywoła tę metodę żeby wyświetlić obiekt w liście
    public override string ToString()
    {
        return $"{Nazwa} ({Cena:F2} zł)";
    }
}

// Wypełnianie ComboBox obiektami:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    cmbProdukt.Items.Add(new Produkt { Id = 1, Nazwa = "Laptop",    Cena = 3499.00 });
    cmbProdukt.Items.Add(new Produkt { Id = 2, Nazwa = "Mysz",      Cena =   89.99 });
    cmbProdukt.Items.Add(new Produkt { Id = 3, Nazwa = "Klawiatura", Cena =  149.00 });
    cmbProdukt.SelectedIndex = 0;
}

Czyszczenie i resetowanie

Operacje na zawartości ComboBox
// Usuń wszystkie pozycje
cmbKolor.Items.Clear();

// Usuń jedną konkretną pozycję (po indeksie)
cmbKolor.Items.RemoveAt(2);   // usuwa trzecią pozycję (indeks 2)

// Ile jest pozycji?
int liczba = cmbKolor.Items.Count;

// Odznacz wszystko (brak zaznaczenia)
cmbKolor.SelectedIndex = -1;
4

Odczytywanie wybranej pozycji

Do odczytania tego co wybrał użytkownik masz trzy właściwości. Każda zwraca co innego – dobierz tę, która pasuje do Twojego przypadku.

WłaściwośćCo zwracaKiedy używać
SelectedItem Zaznaczony obiekt (jako object) Gdy dodawałeś obiekty klasy – musisz rzutować
SelectedIndex Numer zaznaczonej pozycji (od 0), -1 gdy brak wyboru Gdy potrzebujesz numeru, a nie wartości
SelectedValue Wartość właściwości wskazanej przez SelectedValuePath Przy pracy z obiektami i Data Binding (zaawansowane)

SelectedItem – najczęściej używane

Odczyt SelectedItem – różne przypadki
private void btnSprawdz_Click(object sender, RoutedEventArgs e)
{
    // ── Przypadek 1: ComboBox wypełniony tekstami (string) ──
    if (cmbKolor.SelectedItem != null)
    {
        string wybranyKolor = (string)cmbKolor.SelectedItem;
        lblWynik.Text = $"Wybrany kolor: {wybranyKolor}";
    }

    // ── Przypadek 2: ComboBox wypełniony obiektami klasy ──
    if (cmbProdukt.SelectedItem != null)
    {
        Produkt wybrany = (Produkt)cmbProdukt.SelectedItem;
        lblWynik.Text = $"Id: {wybrany.Id}, Nazwa: {wybrany.Nazwa}, Cena: {wybrany.Cena} zł";
    }

    // ── Przypadek 3: ComboBoxItem w XAML ──
    if (cmbKategoria.SelectedItem != null)
    {
        ComboBoxItem pozycja = (ComboBoxItem)cmbKategoria.SelectedItem;
        string tekst = pozycja.Content.ToString();
        lblWynik.Text = $"Kategoria: {tekst}";
    }
}
Zawsze sprawdzaj czy SelectedItem != null!

Jeśli żadna pozycja nie jest zaznaczona (SelectedIndex == -1), wtedy SelectedItem jest równe null. Próba wywołania metody na null spowoduje wyjątek. Dlatego zawsze sprawdzaj if (cmb.SelectedItem != null) przed odczytaniem wartości.

SelectedIndex

SelectedIndex – numer zaznaczonej pozycji
// Odczyt numeru zaznaczonej pozycji
int numer = cmbKolor.SelectedIndex;

if (numer == -1)
{
    lblWynik.Text = "Nic nie wybrano";
}
else
{
    lblWynik.Text = $"Wybrano pozycję nr {numer}";
}

// Ustawienie zaznaczenia z C#
cmbKolor.SelectedIndex = 2;   // zaznacza trzecią pozycję
cmbKolor.SelectedIndex = -1;  // czyści zaznaczenie

SelectedValue z SelectedValuePath

SelectedValue z SelectedValuePath pozwala pobrać konkretną właściwość zaznaczonego obiektu – np. samo Id zamiast całego obiektu.

SelectedValuePath – pobierz konkretną właściwość
<!-- XAML: wskazujemy którą właściwość obiektu ma zwracać SelectedValue -->
<ComboBox x:Name="cmbProdukt"
          SelectedValuePath="Id"
          Width="200" />

// C# – SelectedValue zwraca teraz Id zaznaczonego produktu
int idProduktu = (int)cmbProdukt.SelectedValue;
lblWynik.Text = $"ID wybranego: {idProduktu}";
5

Zdarzenie SelectionChanged

SelectionChanged odpala się za każdym razem gdy użytkownik zmieni wybór w ComboBox. Dzięki niemu możesz reagować na wybór natychmiast – bez przycisku „Zatwierdź”.

XAML – podpięcie zdarzenia
<ComboBox x:Name="cmbKolor"
          Width="180"
          SelectionChanged="cmbKolor_SelectionChanged">
    <ComboBoxItem Content="Czerwony"  />
    <ComboBoxItem Content="Zielony"   />
    <ComboBoxItem Content="Niebieski" />
</ComboBox>
C# – obsługa zdarzenia
private void cmbKolor_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // Sprawdzamy czy cokolwiek jest zaznaczone
    if (cmbKolor.SelectedItem == null) return;

    ComboBoxItem pozycja = (ComboBoxItem)cmbKolor.SelectedItem;
    string wybrany = pozycja.Content.ToString();

    // Zmieniamy tło okna na wybrany kolor
    switch (wybrany)
    {
        case "Czerwony":
            Background = Brushes.Tomato;
            break;
        case "Zielony":
            Background = Brushes.MediumSeaGreen;
            break;
        case "Niebieski":
            Background = Brushes.SteelBlue;
            break;
    }
}
Uwaga: SelectionChanged odpala się też przy ładowaniu!

Kiedy aplikacja startuje i ustawiasz SelectedIndex = 0 w Window_Loaded, WPF odpali SelectionChanged. Jeśli w tej metodzie odwołujesz się do innych kontrolek, które jeszcze nie są gotowe – możesz dostać wyjątek. Zabezpieczenie: sprawdź if (cmbKolor.SelectedItem == null) return; na początku metody.

6

IsEditable – tryb edycji

Domyślnie ComboBox pozwala tylko wybrać z listy. Po ustawieniu IsEditable="True" użytkownik może też wpisać własną wartość, której nie ma na liście. Pole staje się edytowalne jak TextBox.

ComboBox edytowalny
<ComboBox x:Name="cmbMiasto"
          Width="200"
          IsEditable="True"
          IsTextSearchEnabled="True">   <!-- podpowiedzi podczas wpisywania -->
    <ComboBoxItem Content="Warszawa"   />
    <ComboBoxItem Content="Kraków"     />
    <ComboBoxItem Content="Gdańsk"     />
    <ComboBoxItem Content="Wrocław"    />
    <ComboBoxItem Content="Poznań"     />
</ComboBox>

Gdy IsEditable="True", wartość wpisana przez użytkownika odczytujesz przez właściwość Text, nie SelectedItem:

Odczyt wartości z edytowalnego ComboBox
private void btnSprawdz_Click(object sender, RoutedEventArgs e)
{
    // Text zawiera to co widać w polu – zarówno wybraną pozycję jak i wpisany tekst
    string miasto = cmbMiasto.Text;

    if (string.IsNullOrEmpty(miasto))
    {
        MessageBox.Show("Podaj lub wybierz miasto!");
        return;
    }

    lblWynik.Text = $"Wybrane miasto: {miasto}";
}
WłaściwośćIsEditable=FalseIsEditable=True
Użytkownik może wpisać własny tekst
Odczyt wybranej wartościSelectedItemText (lub SelectedItem)
Podpowiedzi podczas pisania✅ (IsTextSearchEnabled)
7

ComboBox z obiektami klasy – DisplayMemberPath

Zamiast nadpisywać ToString(), możesz powiedzieć ComboBox którą właściwość obiektu wyświetlać używając DisplayMemberPath.

DisplayMemberPath – elegantszy sposób
<!-- XAML: mówimy że chcemy wyświetlać właściwość "Nazwa" -->
<ComboBox x:Name="cmbProdukt"
          Width="200"
          DisplayMemberPath="Nazwa"
          SelectedValuePath="Id" />

// C# – wypełniamy obiektami, wyświetlane będzie tylko pole "Nazwa":
public class Produkt
{
    public int    Id    { get; set; }
    public string Nazwa { get; set; }
    public double Cena  { get; set; }
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    cmbProdukt.Items.Add(new Produkt { Id = 1, Nazwa = "Laptop",    Cena = 3499.00 });
    cmbProdukt.Items.Add(new Produkt { Id = 2, Nazwa = "Mysz",      Cena =   89.99 });
    cmbProdukt.Items.Add(new Produkt { Id = 3, Nazwa = "Klawiatura", Cena =  149.00 });
    cmbProdukt.SelectedIndex = 0;
}

private void btnPokazCene_Click(object sender, RoutedEventArgs e)
{
    if (cmbProdukt.SelectedItem == null) return;

    // Rzutujemy na nasz typ i odczytujemy dowolne pole
    Produkt wybrany = (Produkt)cmbProdukt.SelectedItem;
    lblCena.Text = $"Cena: {wybrany.Cena:F2} zł";

    // SelectedValue zwraca Id (bo SelectedValuePath="Id")
    int id = (int)cmbProdukt.SelectedValue;
    lblId.Text = $"ID: {id}";
}
8

Praktyczny przykład – kalkulator dostawy

Formularz zamówienia: użytkownik wybiera kraj dostawy i metodę płatności, a aplikacja pokazuje szacowany czas i koszt dostawy.

MainWindow.xaml
<Grid Margin="20">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="130" />
        <ColumnDefinition Width="*"   />
    </Grid.ColumnDefinitions>

    <TextBlock Grid.Row="0" Grid.Column="0" Text="Kraj dostawy:"
               VerticalAlignment="Center" Margin="0,0,0,10" />
    <ComboBox  Grid.Row="0" Grid.Column="1" x:Name="cmbKraj"
               Margin="0,0,0,10"
               SelectionChanged="cmbKraj_SelectionChanged" />

    <TextBlock Grid.Row="1" Grid.Column="0" Text="Płatność:"
               VerticalAlignment="Center" Margin="0,0,0,10" />
    <ComboBox  Grid.Row="1" Grid.Column="1" x:Name="cmbPlatnosc"
               Margin="0,0,0,10" />

    <TextBlock Grid.Row="2" Grid.Column="0"
               Grid.ColumnSpan="2"
               x:Name="lblInfo"
               Text="– wybierz kraj –"
               Margin="0,0,0,10"
               Foreground="SteelBlue" />

    <Button    Grid.Row="3" Grid.Column="1"
               Content="Zamów"
               HorizontalAlignment="Right"
               Width="90"
               Click="btnZamow_Click" />
</Grid>
MainWindow.xaml.cs
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Kraje dostawy
    cmbKraj.Items.Add("Polska");
    cmbKraj.Items.Add("Niemcy");
    cmbKraj.Items.Add("Francja");
    cmbKraj.Items.Add("Wielka Brytania");
    cmbKraj.SelectedIndex = 0;

    // Metody płatności
    cmbPlatnosc.Items.Add("Karta kredytowa");
    cmbPlatnosc.Items.Add("Przelew bankowy");
    cmbPlatnosc.Items.Add("BLIK");
    cmbPlatnosc.Items.Add("Pobranie");
    cmbPlatnosc.SelectedIndex = 0;
}

private void cmbKraj_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (cmbKraj.SelectedItem == null || lblInfo == null) return;

    string kraj = (string)cmbKraj.SelectedItem;

    switch (kraj)
    {
        case "Polska":
            lblInfo.Text = "✅ Dostawa w 1-2 dni robocze | koszt: 12 zł";
            break;
        case "Niemcy":
            lblInfo.Text = "🚚 Dostawa w 3-5 dni roboczych | koszt: 35 zł";
            break;
        case "Francja":
            lblInfo.Text = "🚚 Dostawa w 5-7 dni roboczych | koszt: 45 zł";
            break;
        case "Wielka Brytania":
            lblInfo.Text = "✈️ Dostawa w 7-10 dni roboczych | koszt: 65 zł";
            break;
    }
}

private void btnZamow_Click(object sender, RoutedEventArgs e)
{
    string kraj     = (string)cmbKraj.SelectedItem;
    string platnosc = (string)cmbPlatnosc.SelectedItem;
    MessageBox.Show($"Zamówienie złożone!\nKraj: {kraj}\nPłatność: {platnosc}",
                    "Potwierdzenie",
                    MessageBoxButton.OK,
                    MessageBoxImage.Information);
}
9

Częste błędy

❌ Błąd 1: Brak sprawdzenia null przed odczytem

❌ Wyjątek NullReferenceException

string kolor = (string)cmbKolor.SelectedItem;
// Jeśli nic nie zaznaczono –
// SelectedItem == null
// rzutowanie wyrzuci wyjątek!

✅ Sprawdzenie null

if (cmbKolor.SelectedItem == null) return;
string kolor = (string)cmbKolor.SelectedItem;
// Bezpieczne

❌ Błąd 2: Złe rzutowanie – XAML vs C#

❌ Nieprawidłowe rzutowanie

// Dodawałem w XAML: <ComboBoxItem Content="X" />
// Ale rzutuję na string:
string s = (string)cmbKolor.SelectedItem;
// Błąd! SelectedItem to ComboBoxItem,
// nie string

✅ Poprawne rzutowanie

// Dane w XAML jako ComboBoxItem:
ComboBoxItem item = (ComboBoxItem)cmbKolor.SelectedItem;
string s = item.Content.ToString();

// Dane dodane z C# jako string:
string s = (string)cmbKolor.SelectedItem;

❌ Błąd 3: Wyjątek w SelectionChanged przy ładowaniu okna

❌ Odwołanie do null kontrolki

private void cmb_SelectionChanged(...)
{
    // lblWynik może być null gdy
    // zdarzenie odpala się zanim
    // okno się w pełni załaduje!
    lblWynik.Text = "coś";
}

✅ Dodaj zabezpieczenie

private void cmb_SelectionChanged(...)
{
    if (lblWynik == null) return;
    if (cmb.SelectedItem == null) return;
    lblWynik.Text = "coś";
}

❌ Błąd 4: Podwójne wypełnianie listy

❌ Elementy dodają się wielokrotnie

private void btnOdswiez_Click(...)
{
    // Brak Clear() przed dodaniem –
    // za każdym kliknięciem elementy
    // się podwajają!
    cmbKolor.Items.Add("Czerwony");
    cmbKolor.Items.Add("Zielony");
}

✅ Clear() przed ponownym wypełnieniem

private void btnOdswiez_Click(...)
{
    cmbKolor.Items.Clear(); // najpierw czyść
    cmbKolor.Items.Add("Czerwony");
    cmbKolor.Items.Add("Zielony");
    cmbKolor.SelectedIndex = 0;
}
10

Zadania do wykonania

Zadanie 1 łatwe

Stwórz okno z ComboBox wypełnionym 5 kolorami (Czerwony, Zielony, Niebieski, Żółty, Fioletowy). Po wybraniu koloru tło okna natychmiast zmienia się na odpowiedni kolor (zdarzenie SelectionChanged). Domyślnie zaznaczony pierwszy element.

Zadanie 2 łatwe

Stwórz kalkulator VAT. W oknie: pole tekstowe na kwotę netto, ComboBox z wartościami VAT (5%, 8%, 23%) i TextBlock na wynik. Po zmianie kwoty lub stawki VAT automatycznie przelicz i wyświetl kwotę brutto.

Zadanie 3 średnie

Stwórz klasę Uczen z polami: Imie, Nazwisko, Klasa. Utwórz 6 obiektów i dodaj je do ComboBox. Użyj DisplayMemberPath="Nazwisko". Po wybraniu ucznia wyświetl w TextBlockach jego imię, nazwisko i klasę.

Zadanie 4 średnie

Stwórz formularz zamówienia pizzy z dwoma ComboBoxami:

  • Rozmiar (Mała – 25cm, Średnia – 30cm, Duża – 40cm)
  • Typ ciasta (Cienkie, Grube, Na zakwasie)

Zdefiniuj klasę Rozmiar z polami Nazwa i CenaBase. Typ ciasta jako zwykłe stringi. Po kliknięciu „Zamów” wyświetl podsumowanie z nazwą rozmiaru, typem ciasta i ceną (ciasto grube +3 zł, na zakwasie +5 zł).