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.
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.
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".
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 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.
<!-- 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>
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#.
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)
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; }
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.
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
// 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;
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 zwraca | Kiedy 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
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}"; } }
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
// 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.
<!-- 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}";
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ź”.
<ComboBox x:Name="cmbKolor" Width="180" SelectionChanged="cmbKolor_SelectionChanged"> <ComboBoxItem Content="Czerwony" /> <ComboBoxItem Content="Zielony" /> <ComboBoxItem Content="Niebieski" /> </ComboBox>
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; } }
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.
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 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:
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=False | IsEditable=True |
|---|---|---|
| Użytkownik może wpisać własny tekst | ❌ | ✅ |
| Odczyt wybranej wartości | SelectedItem | Text (lub SelectedItem) |
| Podpowiedzi podczas pisania | ❌ | ✅ (IsTextSearchEnabled) |
ComboBox z obiektami klasy – DisplayMemberPath
Zamiast nadpisywać ToString(), możesz powiedzieć ComboBox
którą właściwość obiektu wyświetlać używając
DisplayMemberPath.
<!-- 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}"; }
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.
<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>
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); }
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;
}
Zadania do wykonania
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.
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.
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ę.
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ł).