Grafikus C# – Windows Forms tanfolyam › 8.1 lecke

Windows Forms · 8.1 lecke · listavezérlők

ListBox, CheckedListBox és ComboBox részletesen

Az 1.5 és 1.6 leckében már használtad a listavezérlőket. A 8. fejezetben az összetett adatok kezelése következik, ezért először rendet teszünk: mi a három lista (ListBox, CheckedListBox, ComboBox) közös alapja, miben térnek el, és melyiket mikor válaszd. Megtanulod a kijelölési módokat, a rendezést, a gyors feltöltést (BeginUpdate), a jelölőnégyzetes lista eseményeit, a ComboBox három stílusát és az automatikus kiegészítést.

  • kb. 100 perc
  • kezdő–közepes szint
  • listák
Anime stílusú fiú és lány egy C# programozási feladatot beszél meg

A lecke végére

  • megkülönbözteted a három listavezérlőt, és kiválasztod a helyzethez illőt;
  • kezeled a ListBox kijelölési módjait (SelectionMode) és több kijelölt elemét;
  • a CheckedListBox jelölt elemeit és az ItemCheck eseményt használod;
  • a ComboBox stílusait (DropDownStyle) és az automatikus kiegészítést beállítod;
  • sok elemet gyorsan töltesz fel (BeginUpdate/EndUpdate, AddRange);
  • felismered a gyakori hibákat (null kijelölés, a ComboBox szabad szövege).

Mielőtt elkezded

Ehhez a leckéhez az 1.5–1.6 lecke (listák, ComboBox alapjai), az 5.5 lecke (SelectedIndexChanged, ItemCheck, jelző) és az 5.8 lecke (közös kezelő, rekurzív bejárás) szükséges.

7.10 Beállítóablak →

ListBox: elemek listája

A ListBox az elemeket egymás alatt mutatja, a felhasználó kijelölhet egyet vagy többet. A SelectionMode határozza meg a kijelölés módját:

SelectionModeJelentése
One (alap)Egyszerre egy elem.
MultiSimpleTöbb elem: minden kattintás be- vagy kikapcsolja az elemet.
MultiExtendedTöbb elem a megszokott módon: Ctrl+kattintás hozzáad, Shift+kattintás tartományt jelöl.
NoneNem jelölhető ki elem (csak megjelenítés).
Form1.csC#
lstTermekek.SelectionMode = SelectionMode.MultiExtended;
lstTermekek.Sorted = true; // az elemek ábécésorrendben jelennek meg

// gyors feltöltés sok elemmel: a rajzolás a végén történik egyszer
lstTermekek.BeginUpdate();
lstTermekek.Items.AddRange(new object[] { "Alma", "Körte", "Szilva", "Barack" });
lstTermekek.EndUpdate();

// kijelölt elemek bejárása (MultiExtended esetén több is lehet)
foreach (object elem in lstTermekek.SelectedItems)
{
lstValasztott.Items.Add(elem);
}

// keresés a listában
int hely = lstTermekek.FindString("Ba"); // az első, "Ba" kezdetű elem (-1, ha nincs)
int pontos = lstTermekek.FindStringExact("Barack"); // pontos egyezés

if (hely >= 0)
{
lstTermekek.SelectedIndex = hely;
}
SelectedItem és SelectedItemsA SelectedItem az (első) kijelölt elem, null, ha nincs. A SelectedItems az összes kijelölt elem gyűjteménye. A SelectedIndex a sorszám (-1, ha nincs).
BeginUpdate és EndUpdateAz elemek hozzáadása közben a lista minden elemnél újrarajzolná magát. A páros felfüggeszti a rajzolást, ezért sok elemnél (több száz) érezhetően gyorsabb. Az EndUpdate-nek mindig meg kell történnie (try–finally).
SortedA Sorted = true ábécésorrendbe teszi az elemeket (szövegként). Rendezett listán az Insert(index, elem) hibát dob, mert a hely a rendezésből adódik.
Elemek típusaAz Items gyűjtemény object elemeket tárol: szöveget és saját objektumot is (a megjelenített szöveget a ToString() adja, 7.2 lecke).

Jósolj!

A lista Sorted = true értékű, és a kódban lst.Items.Insert(0, "Új"); sor szerepel. Mi történik?

Jóslat ellenőrzése

A rendezett lista az elemet nem a megadott helyre teszi, hanem a rendezésnek megfelelő helyre (a hely paraméter figyelmen kívül marad, vagy a viselkedés kiszámíthatatlan). Rendezett listán az Add a helyes módszer; a helyre szúrás csak rendezetlen listán értelmes.

CheckedListBox: jelölőnégyzetes lista

A CheckedListBox minden elem mellett jelölőnégyzetet mutat. Két külön fogalom van: az elem kijelölése (kék sáv, SelectedIndex) és bejelölése (pipa, CheckedItems). Az alapértelmezés szerint a pipa bejelöléséhez két kattintás kell (az első kijelöl, a második jelöl). A CheckOnClick = true egyetlen kattintásra jelöl:

Form1.csC#
clbFeladatok.CheckOnClick = true;

// a bejelölt elemek
foreach (object elem in clbFeladatok.CheckedItems)
{
lblKesz.Text += elem + "; ";
}

int db = clbFeladatok.CheckedItems.Count;

// egy elem állapota és beállítása sorszám szerint
bool bejelolt = clbFeladatok.GetItemChecked(2);
clbFeladatok.SetItemChecked(2, true);

// az ItemCheck esemény a változás ELŐTT fut: az e.NewValue az új állapot (5.5 lecke)
private void clbFeladatok_ItemCheck(object sender, ItemCheckEventArgs e)
{
int uj = clbFeladatok.CheckedItems.Count + (e.NewValue == CheckState.Checked ? 1 : -1);
lblKesz.Text = uj + " kész / " + clbFeladatok.Items.Count;
}

// mindent kijelöl / töröl
for (int i = 0; i < clbFeladatok.Items.Count; i++)
{
clbFeladatok.SetItemChecked(i, true);
}
ItemCheck a változás előttAz esemény az új állapot beállítása előtt fut, ezért a CheckedItems.Count még a régit adja. Az e.NewValue alapján számolj (ahogy a kódban), vagy a számolást BeginInvoke-kal késleltesd.
Kijelölt és bejelöltEgy elem lehet bejelölt anélkül, hogy kijelölt lenne, és fordítva. A bejelölt elemek: CheckedItems / CheckedIndices, a kijelölt: SelectedItem / SelectedIndex.
Adatkötés nélkülA CheckedListBox az adatkötést (8.5 lecke) csak korlátozottan támogatja: a bejelölt állapotot nem köti az adathoz, ezért a pipa állapotát külön kell kezelni (GetItemChecked, SetItemChecked).
Mikor jó?Kevés (legfeljebb néhány tucat) elem, amelyeknél a több választás természetes (jogosultságok, tevékenységek). Sok elemhez és több oszlophoz a DataGridView jobb (8.3 lecke).

Jósolj!

Az ItemCheck kezelő kiírja a CheckedItems.Count értéket, és a felhasználó bejelöl egy harmadik elemet (előtte kettő volt bejelölve). Mit ír ki a kezelő?

Jóslat ellenőrzése

2-t: az esemény az új állapot beállítása előtt fut, a gyűjtemény még a régi állapotot tükrözi. Ezért az e.NewValue alapján kell számolni (+1 vagy -1).

ComboBox: legördülő lista

A ComboBox egy szövegmező és egy legördülő lista együtt. A DropDownStyle háromféle viselkedést ad:

DropDownStyleViselkedésMikor?
DropDownListCsak a listából lehet választani, a szöveg nem szerkeszthető.Rögzített választék (ország, kategória). A legbiztonságosabb.
DropDown (alap)Választás a listából vagy szabad szöveg beírása.Javaslatokkal segített szabad bevitel (keresés, cím).
SimpleA lista mindig látszik (szövegmező + lista).Ritka; helyigényes.
Form1.csC#
cmbKategoria.DropDownStyle = ComboBoxStyle.DropDownList;
cmbKategoria.Items.AddRange(new object[] { "Gyümölcs", "Zöldség", "Pékáru" });
cmbKategoria.SelectedIndex = 0; // induló kiválasztás, különben üres

// automatikus kiegészítés: a szabad szöveges (DropDown) ComboBox-ban gépelés közben javasol
cmbTanar.DropDownStyle = ComboBoxStyle.DropDown;
cmbTanar.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cmbTanar.AutoCompleteSource = AutoCompleteSource.ListItems; // a lista elemei a javaslatok

// a választás kiolvasása
string ertek = cmbKategoria.Text; // mindig a látható szöveg
object elem = cmbKategoria.SelectedItem; // null, ha a szöveg nem a lista eleme

private void cmbKategoria_SelectionChangeCommitted(object sender, EventArgs e)
{
// csak a felhasználó választására fut le (5.5 lecke)
lblInfo.Text = "Választott kategória: " + cmbKategoria.Text;
}
SelectedIndex = -1A DropDownList stílusú lista is üres marad, amíg nem állítasz be SelectedIndex értéket. Üres választásnál a SelectedItem null.
Text és SelectedItemA DropDown stílusnál a Text bármi lehet (szabad szöveg), a SelectedItem csak akkor nem null, ha a szöveg egyezik egy listaelemmel.
Három eseményA SelectedIndexChanged a kódból történő változásra is lefut, a SelectionChangeCommitted csak a felhasználó választására, a TextChanged minden szövegváltozásra (5.5 lecke).
AutoCompleteA SuggestAppend javaslatlistát ad és kiegészíti a szöveget. Források: ListItems (a lista elemei), CustomSource (saját AutoCompleteStringCollection), FileSystem, HistoryList.

Jósolj!

Egy DropDown stílusú ComboBox listájában „Alma”, „Körte” szerepel, a felhasználó beírja: „Banán”, és a kód cmb.SelectedItem.ToString() sort hajt végre. Mi történik?

Jóslat ellenőrzése

NullReferenceException: a „Banán” nem listaelem, ezért a SelectedItem null. Szabad szövegnél a Text tulajdonságot használd; rögzített választéknál állítsd DropDownList stílusra, és ellenőrizd a SelectedIndex >= 0 feltételt.

Melyiket mikor?

HelyzetVezérlő
Egy érték választása néhány (3–15) lehetőségből, kevés helyen.ComboBox (DropDownList)
Egy érték választása, szabad szöveg is megengedett (javaslatokkal).ComboBox (DropDown + AutoComplete)
Sok elem megtekintése és egy vagy több kijelölése, a lista állandóan látszik.ListBox
Több független igen/nem választás (jogosultságok, tevékenységek).CheckedListBox (vagy külön CheckBox-ok, ha kevés)
Kevés (2–4) kölcsönösen kizáró lehetőség.RadioButton csoport (6.3 lecke)
Több oszlopos, szerkeszthető adat.DataGridView (8.3 lecke)
📷 Képernyőkép helye

Mit tegyél ide: egymás mellett a három vezérlő ugyanazzal a tartalommal: ListBox, CheckedListBox és kinyitott ComboBox.Javasolt fájlnév: winforms-8-1-listak.webp · Alt szöveg: ListBox, CheckedListBox és ComboBox

Hibakeresés: te vagy a hibadetektív

Mi a baj, és hogyan javítod?

Gondold végig az okot, és csak utána nyisd le a megoldást.

1. NullReferenceException a kijelölt elemnél

string nev = lst.SelectedItem.ToString();   // nincs kijelölt elem: null
Hiba és javítás

Ellenőrizd: if (lst.SelectedIndex >= 0) { ... } vagy lst.SelectedItem != null.

2. A kijelölt elemek törlése kihagy elemeket

foreach (object elem in lst.SelectedItems)
{
lst.Items.Remove(elem); // a gyűjtemény módosul bejárás közben
}
Hiba és javítás

A bejárt gyűjtemény módosul (InvalidOperationException). Készíts másolatot (object[] tomb = new object[lst.SelectedItems.Count]; lst.SelectedItems.CopyTo(tomb, 0);), vagy járj vissza a SelectedIndices indexein.

3. A CheckedListBoxban dupla kattintás kell a pipához

Az első kattintás csak kijelöl.

Ok és javítás

Állítsd be a CheckOnClick = true tulajdonságot.

4. Az ItemCheck rossz számot mutat

A kijelzett bejelölt elemek száma mindig eggyel elmarad.

Ok és javítás

Az esemény az állapotváltozás előtt fut. Használd az e.NewValue értéket (+1 vagy -1), vagy a számolást késleltesd.

5. A ComboBox üresen indul

Induláskor nincs kiválasztott érték, pedig a lista nem üres.

Ok és javítás

Állítsd be a SelectedIndex = 0 értéket a Load-ban (ha van elem).

6. Lassú a feltöltés

Ezer elem hozzáadása másodpercekig tart.

Ok és javítás

Használj BeginUpdate/EndUpdate párost és AddRange hívást (egy tömbbel egyszerre).

Mini projekt: kurzusjelentkezés

Készíts programot, amely mindhárom listavezérlőt használja:

  1. cmbKategoria (DropDownList): Programozás, Grafika, Hálózat. A kiválasztás a lstKurzusok (ListBox, MultiExtended, Sorted) tartalmát cseréli az adott kategória kurzusaira (BeginUpdate).
  2. A Jelentkezés >> gomb a kijelölt kurzusokat a clbJelentkezes (CheckedListBox, CheckOnClick) listába teszi, ismétlés nélkül; a << Vissza gomb a bejelölt elemeket törli a jelentkezések közül.
  3. A clbJelentkezes bejelölése jelzi, hogy a díj ki van fizetve; a lblAllapot mutatja: 3 jelentkezés, ebből 1 kifizetve (ItemCheck, e.NewValue).
  4. A cmbTanar (DropDown, SuggestAppend, ListItems) a tanár neve: szabad szöveg is lehet; a Keres gomb kijelöli a kurzusok közül azokat, amelyekhez a név tartozik (ha nincs ilyen tanár, üzenet a lblInfo-ban).
  5. Teszteld: kategóriaváltás, több kurzus kijelölése, ismétlés, törlés a bejelöltekre, a számláló, a tanár keresése a listából és szabad szöveggel.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A kurzusokat és a hozzájuk tartozó tanárt két párhuzamos tömb tartalmazza. A bejelölt elemek törlése visszafelé haladó ciklussal történik (a gyűjtemény módosul).

Form1.csC#
private readonly string[] kategoriak = { "Programozás", "Grafika", "Hálózat" };

private readonly string[][] kurzusok =
{
new string[] { "C# alapok", "Python kezdőknek", "Webfejlesztés" },
new string[] { "Digitális rajz", "Fényképszerkesztés" },
new string[] { "Hálózati alapok", "Biztonság" }
};

private readonly string[][] tanarok =
{
new string[] { "Kiss Péter", "Nagy Anna", "Kiss Péter" },
new string[] { "Tóth Eszter", "Tóth Eszter" },
new string[] { "Varga Gábor", "Nagy Anna" }
};

private void Form1_Load(object sender, EventArgs e)
{
lstKurzusok.SelectionMode = SelectionMode.MultiExtended;
lstKurzusok.Sorted = true;
clbJelentkezes.CheckOnClick = true;

cmbKategoria.DropDownStyle = ComboBoxStyle.DropDownList;
cmbKategoria.Items.AddRange(kategoriak);

cmbTanar.DropDownStyle = ComboBoxStyle.DropDown;
cmbTanar.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cmbTanar.AutoCompleteSource = AutoCompleteSource.ListItems;
cmbTanar.Items.AddRange(new object[] { "Kiss Péter", "Nagy Anna", "Tóth Eszter", "Varga Gábor" });

cmbKategoria.SelectedIndex = 0;
}

private void cmbKategoria_SelectedIndexChanged(object sender, EventArgs e)
{
lstKurzusok.BeginUpdate();

try
{
lstKurzusok.Items.Clear();
lstKurzusok.Items.AddRange(kurzusok[cmbKategoria.SelectedIndex]);
}
finally
{
lstKurzusok.EndUpdate();
}
}

private void btnJelentkezes_Click(object sender, EventArgs e)
{
foreach (object elem in lstKurzusok.SelectedItems)
{
if (!clbJelentkezes.Items.Contains(elem))
{
clbJelentkezes.Items.Add(elem);
}
}

AllapotFrissit(0);
}

private void btnVissza_Click(object sender, EventArgs e)
{
for (int i = clbJelentkezes.Items.Count - 1; i >= 0; i--)
{
if (clbJelentkezes.GetItemChecked(i))
{
clbJelentkezes.Items.RemoveAt(i);
}
}

AllapotFrissit(0);
}

private void AllapotFrissit(int valtozas)
{
lblAllapot.Text = clbJelentkezes.Items.Count + " jelentkezés, ebből " + (clbJelentkezes.CheckedItems.Count + valtozas) + " kifizetve";
}

private void clbJelentkezes_ItemCheck(object sender, ItemCheckEventArgs e)
{
AllapotFrissit(e.NewValue == CheckState.Checked ? 1 : -1);
}

private void btnKeres_Click(object sender, EventArgs e)
{
string nev = cmbTanar.Text.Trim();
int kategoria = cmbKategoria.SelectedIndex;

lstKurzusok.ClearSelected();
int db = 0;

for (int i = 0; i < kurzusok[kategoria].Length; i++)
{
if (tanarok[kategoria][i].Equals(nev, StringComparison.CurrentCultureIgnoreCase))
{
lstKurzusok.SetSelected(lstKurzusok.FindStringExact(kurzusok[kategoria][i]), true);
db++;
}
}

lblInfo.Text = db == 0 ? "Ebben a kategóriában nincs ilyen tanár: " + nev : db + " kurzus kijelölve.";
}

Három önálló gyakorlófeladat

Egyszerre csak egyet csinálj, és csak utána nyisd ki a megoldást.

1. Mindent kijelöl / semmit

Két gomb (Mind, Egyik sem) a CheckedListBox összes elemét bejelöli, illetve törli. Hogyan?

Egy lehetséges megoldás

Ciklus a SetItemChecked hívással.

Form1.csC#
private void Mind(bool allapot)
{
for (int i = 0; i < clb.Items.Count; i++)
{
clb.SetItemChecked(i, allapot);
}
}

2. Szűrő a listához

Egy szövegmező gépelés közben szűri a ListBox elemeit (azok maradnak, amelyek tartalmazzák a szöveget). Honnan veszed az eredeti elemeket?

Egy lehetséges megoldás

Az eredeti listát egy külön List<string> őrzi, a ListBox csak a szűrt részt mutatja.

Form1.csC#
private readonly List<string> mind = new List<string> { "Alma", "Körte", "Almalé", "Barack" };

private void txtSzuro_TextChanged(object sender, EventArgs e)
{
string minta = txtSzuro.Text.Trim();

lst.BeginUpdate();
lst.Items.Clear();

foreach (string s in mind)
{
if (s.IndexOf(minta, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
lst.Items.Add(s);
}
}

lst.EndUpdate();
}

3. Kiválasztott elem a ComboBoxból

A DropDown ComboBox-ban beírt új szöveg bekerül a listába is, ha még nincs benne (Enter hatására). Hogyan ellenőrzöd?

Egy lehetséges megoldás

A KeyDown eseményben az Enter után a Items.Contains ellenőrzés.

Form1.csC#
private void cmbTanar_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter && cmbTanar.Text.Trim().Length > 0 && !cmbTanar.Items.Contains(cmbTanar.Text.Trim()))
{
e.SuppressKeyPress = true;
cmbTanar.Items.Add(cmbTanar.Text.Trim());
}
}

Gyakorlótár: 6 további feladat

Alap, közepes és kihívás szinten gyakorolhatsz. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Melyik vezérlő?

Válassz: (a) ország; (b) értesítések típusa (e-mail, SMS, push, egyenként); (c) 200 termék, több kijelölhető.

Megoldás megnyitása

(a) ComboBox DropDownList; (b) CheckedListBox (vagy három CheckBox); (c) ListBox MultiExtended.

2. Kijelölés számlálása

Mit ad a SelectedItems.Count, ha nincs kijelölt elem?

Megoldás megnyitása

0 (nem null): a SelectedItems gyűjtemény létezik, csak üres. A SelectedItem ellenben null.

Közepes szint

3. Elemek mozgatása a listák között

Két ListBox (Elérhető, Kiválasztott) között a kijelölt elemek átkerülnek. Mire kell vigyázni?

Megoldás megnyitása

A kijelölteket előbb egy tömbbe másold, és csak utána add hozzá/töröld, mert a SelectedItems a törlés közben módosul.

Form1.csC#
private void Atrak(ListBox honnan, ListBox hova)
{
object[] tomb = new object[honnan.SelectedItems.Count];
honnan.SelectedItems.CopyTo(tomb, 0);

foreach (object elem in tomb)
{
hova.Items.Add(elem);
honnan.Items.Remove(elem);
}
}

4. Szabad szöveg érvényesítése

A DropDown ComboBox-ban csak a listában szereplő értéket fogadod el. Hol ellenőrzöd, és mit jelzel?

Megoldás megnyitása

A Validating eseményben (6.6 lecke): !cmb.Items.Contains(cmb.Text) esetén ErrorProvider. Egyszerűbb: DropDownList stílus.

Kihívás szint

5. Saját megjelenítés

A lista elemei Termek objektumok. Hogyan mutatja a lista az Alma – 250 Ft szöveget?

Megoldás megnyitása

A ToString() felülírásával (7.2 lecke), vagy az adatkötés DisplayMember tulajdonságával (8.5 lecke).

Termek.csC#
public class Termek
{
public string Nev = "";
public int Ar = 0;

public override string ToString()
{
return Nev + " – " + Ar + " Ft";
}
}

6. Több oszlopos ListBox

Hogyan jeleníted meg 100 rövid elemet több oszlopban, vízszintes görgetéssel?

Megoldás megnyitása

MultiColumn = true, és a ColumnWidth az oszlop szélessége. A HorizontalScrollbar a hosszú szövegek vízszintes görgetésére való egyoszlopos listában.

Gyors önellenőrzés

1. Mi a SelectionMode.MultiExtended?

Több elem kijelölése a megszokott módon: Ctrl hozzáad, Shift tartományt jelöl.

2. Mi a kijelölt és a bejelölt elem különbsége?

A kijelölt a kék sáv (SelectedItem), a bejelölt a pipa (CheckedItems).

3. Mikor fut az ItemCheck?

Az állapotváltozás előtt: az e.NewValue az új állapot.

4. Melyik DropDownStyle a legbiztonságosabb rögzített választéknál?

DropDownList.

5. Mire való a BeginUpdate?

Felfüggeszti a rajzolást sok elem hozzáadásakor; az EndUpdate újraindítja.

6. Miért nem jó a foreach a kijelöltek törlésére?

A gyűjtemény módosul bejárás közben: másolatot vagy visszafelé haladó ciklust használj.

7. Mi az AutoCompleteSource.ListItems?

A javaslatok forrása a ComboBox saját listaelemei.

Mit tudsz már?

  • Kiválasztod a helyzethez illő listavezérlőt.
  • Kezeled a kijelölési módokat, a jelölőnégyzetes lista eseményeit és a ComboBox stílusait.
  • Gyorsan töltesz fel listákat, és automatikus kiegészítést állítasz be.
  • Felismered a gyakori hibákat (null kijelölés, törlés bejárás közben).

Következő lecke: Items, SelectedItem és SelectedIndex

A listák elemeinek kezelése mélyebben: saját objektumok a listában, kijelölés követése, elemek mozgatása és cseréje.