Grafikus C# – Windows Forms tanfolyam › 8.3 lecke

Windows Forms · 8.3 lecke · táblázat

DataGridView: oszlopok, sorok és cellák

A DataGridView a Windows Forms legerősebb vezérlője: táblázat, amelyben sorokat és oszlopokat jeleníthetsz meg, szerkeszthetsz, rendezhetsz. A 3. fejezetben már használtad adatbázis-adatok megjelenítésére. Most a vezérlőt önmagában ismered meg: kódból adsz hozzá oszlopokat (szöveg, jelölőnégyzet, legördülő lista, gomb), sorokat és cellákat kezelsz, kijelölést olvasol, és az eseményeivel reagálsz a felhasználóra. A legfontosabb csapdák: az új sor és a cellaérték típusa (object).

  • kb. 110 perc
  • közepes szint
  • DataGridView
Diáklány C# programot készít laptopon

A lecke végére

  • kódból adsz oszlopokat a táblázathoz (TextBox, CheckBox, ComboBox, Button oszlop), és beállítod a megjelenésüket;
  • sorokat és cellákat kezelsz (Rows.Add, Cells, RemoveAt), és a cellaértéket típushelyesen olvasod;
  • a kijelölést (SelectionMode, SelectedRows, CurrentCell) használod;
  • reagálsz az eseményekre (CellValueChanged, CellContentClick, CellFormatting);
  • tudod, mi az „új sor” (IsNewRow), és hogyan kerüld a null értékeket;
  • kezeled a DataError eseményt.

Mielőtt elkezded

Ehhez a leckéhez a 3.4 lecke (DataGridView első használata), a 8.2 lecke (kijelölés, Items), az 5.5 lecke (változás-események, jelző) és a 6.2 lecke (Dock, Anchor) szükséges.

8.2 Items és kijelölés →

Oszlopok

A tábla oszlopait a tervezőben (Edit Columns) vagy kódból adod meg. Az oszlop típusa határozza meg, hogyan jelenik meg és szerkeszthető a cella:

OszloptípusCella
DataGridViewTextBoxColumnSzöveg vagy szám (az alapértelmezés).
DataGridViewCheckBoxColumnJelölőnégyzet (bool érték).
DataGridViewComboBoxColumnLegördülő lista (Items vagy DataSource).
DataGridViewButtonColumnGomb a cellában (Text, UseColumnTextForButtonValue).
DataGridViewImageColumn, DataGridViewLinkColumnKép, hivatkozás.
Form1.csC#
dgv.AutoGenerateColumns = false;
dgv.Columns.Clear();

DataGridViewTextBoxColumn nev = new DataGridViewTextBoxColumn();
nev.Name = "colNev"; // a kódbeli azonosító
nev.HeaderText = "Név"; // a fejléc szövege
nev.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
nev.FillWeight = 40; // a Fill oszlopok közti arány
dgv.Columns.Add(nev);

DataGridViewTextBoxColumn ar = new DataGridViewTextBoxColumn();
ar.Name = "colAr";
ar.HeaderText = "Ár (Ft)";
ar.DefaultCellStyle.Format = "N0"; // ezres tagolás
ar.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
ar.Width = 100;
dgv.Columns.Add(ar);

DataGridViewCheckBoxColumn kesz = new DataGridViewCheckBoxColumn();
kesz.Name = "colKesz";
kesz.HeaderText = "Kész";
dgv.Columns.Add(kesz);

DataGridViewButtonColumn torol = new DataGridViewButtonColumn();
torol.Name = "colTorol";
torol.HeaderText = "";
torol.Text = "Törlés";
torol.UseColumnTextForButtonValue = true; // minden cellában ez a felirat
dgv.Columns.Add(torol);
Name és HeaderTextA Name a kódbeli azonosító (dgv.Columns["colAr"]), a HeaderText a felhasználó által látott fejléc. Mindkettőt add meg.
AutoSizeModeA Fill az oszlopot a szabad helyre nyújtja (a FillWeight adja az arányt), a AllDirectCells a tartalomhoz igazodik, a None fix szélességű (Width). Egy táblázatban általában egy-két Fill oszlop van.
DefaultCellStyleA cellák megjelenítése: Format (N0, 0.00, d dátum), Alignment, BackColor, ForeColor, Font. A számokat jobbra igazítjuk.
AutoGenerateColumnsAdatkötésnél (8.4–8.6 lecke) a vezérlő magától képez oszlopokat. Ha magad adod meg az oszlopokat, kapcsold ki (false), különben duplázódnak.

Jósolj!

A HeaderText értéke "Ár", de a Name nincs megadva, és a kód a dgv.Columns["Ar"] oszlopot kéri. Mi történik?

Jóslat ellenőrzése

A szöveges indexelő a Name alapján keres, és nem a HeaderText szerint: a kódból létrehozott oszlop neve üres, ezért a keresés null értéket ad (vagy kivételt dob a következő hivatkozásnál). Mindig adj Name értéket, és azzal hivatkozz.

Sorok és cellák

Form1.csC#
// sor hozzáadása: az értékek az oszlopok sorrendjében (a gomboszlop értéke nem kell)
int i = dgv.Rows.Add("Alma", 250, false); // az új sor sorszáma
dgv.Rows.Add("Körte", 320, true);

// cella olvasása és írása: oszlopnév vagy sorszám szerint
object ertek = dgv.Rows[i].Cells["colAr"].Value; // object típusú!
dgv.Rows[i].Cells["colAr"].Value = 300;

// típushelyes olvasás: a Value null vagy más típusú is lehet
int ar;

if (int.TryParse(Convert.ToString(dgv.Rows[i].Cells["colAr"].Value), out ar))
{
// használható az ar
}

// az összes sor bejárása: az „új sor” (üres, beírásra váró) kihagyása
int osszeg = 0;

foreach (DataGridViewRow sor in dgv.Rows)
{
if (sor.IsNewRow)
{
continue;
}

int egyAr;

if (int.TryParse(Convert.ToString(sor.Cells["colAr"].Value), out egyAr))
{
osszeg += egyAr;
}
}

dgv.Rows.RemoveAt(0); // törlés sorszám szerint
dgv.Rows.Clear(); // minden sor törlése
Value: objectA cella értéke object: lehet szöveg, szám, bool vagy null (üres cella). Ezért nem használunk közvetlen átalakítást ((int)Value), hanem TryParse-t a Convert.ToString eredményével (az null értéket üres szövegre alakítja).
Az új sorHa az AllowUserToAddRows igaz (alap), a tábla alján egy üres „új sor” áll, amelybe a felhasználó beírhat. Ez a sor a Rows gyűjtemény utolsó eleme (IsNewRow = true), a Rows.Count számába is beletartozik, értékei null.
Rows.Add visszatéréseA Rows.Add(értékek) az új sor sorszámát adja. Sok sor feltöltésénél a SuspendLayout sokat gyorsít, de a DataGridViewnak nincs BeginUpdate párja: az adatkötés (8.4 lecke) gyorsabb.
Cells[név] vagy Cells[index]A névvel való hivatkozás olvashatóbb és az oszlopsorrend változásától független. Az indexszel gyorsabb, de törékeny.

Jósolj!

A táblában három adatsor van, és az AllowUserToAddRows igaz. Mit ad a dgv.Rows.Count, és mi történik, ha ciklusban az összes sor colAr celláját (int) átalakítással olvasod?

Jóslat ellenőrzése

A Rows.Count értéke 4 (a három adatsor és az új sor). Az új sor colAr cellájának értéke null, és az (int)null átalakítás NullReferenceException kivételt dob. Az IsNewRow ellenőrzés és a TryParse véd.

Kijelölés és szerkesztés

TulajdonságJelentése
SelectionModeCellSelect (alap), FullRowSelect (az egész sor), FullColumnSelect, RowHeaderSelect.
MultiSelectTöbb sor/cella egyszerre.
CurrentCell, CurrentRowAz aktuális (kurzort tartalmazó) cella és sor.
SelectedRows, SelectedCellsA kijelölt sorok és cellák gyűjteménye.
ReadOnlyA tábla, oszlop vagy cella szerkeszthetősége.
AllowUserToAddRows, AllowUserToDeleteRowsÚj sor és a Delete billentyűvel törlés engedélyezése.
RowHeadersVisibleA sorfejlécek (bal oszlop) látszanak-e.
Form1.csC#
dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;   // sorkijelölés: kényelmesebb törléshez
dgv.MultiSelect = true;
dgv.AllowUserToAddRows = false; // nincs üres „új sor”: egyszerűbb a bejárás
dgv.Columns["colNev"].ReadOnly = true;
dgv.RowHeadersVisible = false;

// a kijelölt sorok törlése (visszafelé: a gyűjtemény módosul)
for (int i = dgv.SelectedRows.Count - 1; i >= 0; i--)
{
dgv.Rows.RemoveAt(dgv.SelectedRows[i].Index);
}

// az aktuális sor
if (dgv.CurrentRow != null)
{
lblNev.Text = Convert.ToString(dgv.CurrentRow.Cells["colNev"].Value);
}
AllowUserToAddRows = falseHa a sorokat gombbal adod hozzá, kapcsold ki: így nincs üres új sor, és a bejárás egyszerűbb (de az IsNewRow ellenőrzés akkor is jó szokás).
FullRowSelectAz egész sor kijelölődik a cellakattintásra: a törlés és a módosítás természetesebb. A cellaszerkesztés ilyenkor is működik (dupla kattintás vagy F2).
SelectedRows sorrendjeA SelectedRows sorrendje a kijelölés sorrendjét követheti, nem a sorok sorrendjét. Törlésnél az Index értéket használd, és ellenőrizd a bejárás módját (visszafelé).
CurrentRow lehet nullÜres táblában vagy fókusz nélkül a CurrentRow null: ellenőrizd.

Jósolj!

Három sor van kijelölve, és a törlő ciklus előrefelé halad (foreach (DataGridViewRow s in dgv.SelectedRows) dgv.Rows.Remove(s);). Mi történik?

Jóslat ellenőrzése

A törlés megváltoztatja a kijelölést (a SelectedRows gyűjteményt), ezért a foreach kihagyhat sorokat vagy kivételt dobhat. Másolatot készíts a kijelölt sorokról (List<DataGridViewRow>), vagy járj visszafelé az indexeken.

Események és formázás

Form1.csC#
// a cella értéke megváltozott (a szerkesztés befejezése után)
private void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0) // a fejléc sora -1
{
return;
}

Osszesit();
}

// a jelölőnégyzet azonnali érvényesítése: kattintáskor, nem a cella elhagyásakor
private void dgv_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (dgv.IsCurrentCellDirty)
{
dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}

// a gomb a cellában
private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && dgv.Columns[e.ColumnIndex].Name == "colTorol")
{
dgv.Rows.RemoveAt(e.RowIndex);
}
}

// formázás megjelenítéskor: a negatív szám piros
private void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex >= 0 && dgv.Columns[e.ColumnIndex].Name == "colAr" && e.Value != null)
{
int ar;

if (int.TryParse(e.Value.ToString(), out ar) && ar < 0)
{
e.CellStyle.ForeColor = Color.Firebrick;
}
}
}

// érvénytelen érték (például szöveg egy szám oszlopba adatkötésnél)
private void dgv_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.ThrowException = false;
lblUzenet.Text = "Érvénytelen érték a(z) " + (e.RowIndex + 1) + ". sorban.";
}
RowIndex -1A fejléc eseményeinél (kattintás a fejlécre) a RowIndex -1: az első ellenőrzés mindig ez legyen, különben a cellaelérés hibát dob.
CellValueChanged és a jelölőnégyzetA jelölőnégyzet értéke csak a cella elhagyásakor érvényesül. A CurrentCellDirtyStateChanged és a CommitEdit azonnali változást ad.
CellContentClick és CellClickA CellContentClick csak a cella tartalmára (gomb, jelölőnégyzet) kattintva fut, a CellClick a cella bármely pontjára. A gombokhoz az előbbi való.
CellFormattingSokszor fut (minden rajzoláskor), ezért gyors legyen, és csak a megjelenítést változtassa (e.CellStyle), az adatot ne.

Jósolj!

A CellValueChanged kezelő a dgv.Rows[e.RowIndex] sort olvassa. A felhasználó a fejlécre kattint (rendezés), és a CellValueChanged eseményt a kód e.RowIndex ellenőrzés nélkül kezeli. Mi történik?

Jóslat ellenőrzése

A fejléc-eseményeknél a RowIndex -1, és a dgv.Rows[-1] ArgumentOutOfRangeException kivételt dob. A CellClick, CellContentClick és a legtöbb cellaesemény kezelőjének első sora az if (e.RowIndex < 0) return; legyen.

📷 Képernyőkép helye

Mit tegyél ide: egy DataGridView négy oszloppal (Név, Ár, Kész jelölőnégyzet, Törlés gomb) és néhány sorral.Javasolt fájlnév: winforms-8-3-tablazat.webp · Alt szöveg: DataGridView különféle oszlopokkal

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 az összegzésnél

foreach (DataGridViewRow s in dgv.Rows)
{
osszeg += (int)s.Cells["colAr"].Value; // az új sor értéke null
}
Hiba és javítás

Hagyd ki az új sort (IsNewRow), és használj TryParse-t a Convert.ToString eredményén.

2. ArgumentOutOfRangeException fejléckattintásra

A cellaeseményt a fejlécre kattintva a program összeomlik.

Ok és javítás

Az e.RowIndex a fejlécnél -1. Az első sor: if (e.RowIndex < 0) return;.

3. A Columns["név"] null

A kód nem találja az oszlopot, pedig látszik a fejléce.

Ok és javítás

A HeaderText és a Name két külön tulajdonság. Az indexelő a Name alapján keres: add meg a Name értéket.

4. A jelölőnégyzet értéke késve változik

A CellValueChanged csak a cella elhagyásakor fut.

Ok és javítás

A CurrentCellDirtyStateChanged eseményben hívd a CommitEdit(DataGridViewDataErrorContexts.Commit) metódust.

5. A gomb a cellában nem reagál

A gomboszlop kattintására semmi nem történik.

Ok és javítás

Nincs feliratkozva a CellContentClick eseményre, vagy a kezelő nem ellenőrzi az oszlopot jól (Columns[e.ColumnIndex].Name). Ellenőrizd az eseménykötést a tervezőben.

6. Az oszlopok megduplázódnak

Kódból adtál oszlopokat, és a táblázat dupla oszlopot mutat.

Ok és javítás

Az AutoGenerateColumns igaz maradt adatkötésnél: állítsd false értékre, ha az oszlopokat te adod meg.

Mini projekt: osztályzat-nyilvántartó

Készíts táblázatot a diákok jegyeinek kezelésére:

  1. dgv (Dock = Fill egy panelen, AllowUserToAddRows = false, FullRowSelect) oszlopai: Név (Fill), Matek, Magyar, Történelem (szám oszlopok, jobbra igazítva, 70 széles), Átlag (csak olvasható, 0.00 formátum), Törlés (gomb).
  2. Az Új diák gomb (és a txtNev mező Enter) új sort ad a névvel; a jegyek üresek.
  3. A jegy cellák értéke 1–5 közötti egész lehet. A CellValueChanged újraszámolja a sor átlagát az érvényes jegyekből (az üres és az érvénytelen cellát kihagyva).
  4. A CellFormatting az 1,5 alatti átlagot piros félkövérrel, a 4,5 feletti átlagot zölddel jelöli (jellel is: ▼ / ▲, 6.9 lecke).
  5. Az lblOsszesito mutatja a diákok számát és az osztályátlagot (az átlagok átlaga, ahol van).
  6. A Törlés gomb a cellában megerősítéssel törli a sort (CellContentClick).
  7. Teszteld: új diákok, jegyek beírása (érvényes és érvénytelen), üres sor, törlés, a színezés, az összesítés frissülése.
Megoldás megnyitása (próbáld ki előbb önállóan!)

Az átlagszámítás egy közös metódusban van (SorAtlag), amelyet a CellValueChanged hív, ügyelve arra, hogy az Átlag oszlop maga is változtatja az értéket (a jelző védi a végtelen hurkot).

Form1.csC#
private readonly string[] jegyOszlopok = { "colMatek", "colMagyar", "colTort" };
private bool szamol = false;

private void Form1_Load(object sender, EventArgs e)
{
dgv.AutoGenerateColumns = false;
dgv.AllowUserToAddRows = false;
dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgv.RowHeadersVisible = false;

DataGridViewTextBoxColumn nev = new DataGridViewTextBoxColumn { Name = "colNev", HeaderText = "Név", AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill };
dgv.Columns.Add(nev);

string[] fejlec = { "Matek", "Magyar", "Történelem" };

for (int i = 0; i < jegyOszlopok.Length; i++)
{
DataGridViewTextBoxColumn o = new DataGridViewTextBoxColumn { Name = jegyOszlopok[i], HeaderText = fejlec[i], Width = 70 };
o.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
dgv.Columns.Add(o);
}

DataGridViewTextBoxColumn atlag = new DataGridViewTextBoxColumn { Name = "colAtlag", HeaderText = "Átlag", Width = 80, ReadOnly = true };
atlag.DefaultCellStyle.Format = "0.00";
atlag.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
dgv.Columns.Add(atlag);

DataGridViewButtonColumn torol = new DataGridViewButtonColumn { Name = "colTorol", HeaderText = "", Text = "Törlés", UseColumnTextForButtonValue = true, Width = 80 };
dgv.Columns.Add(torol);

Osszesit();
}

private void UjDiak()
{
string nev = txtNev.Text.Trim();

if (nev.Length == 0) { return; }

dgv.Rows.Add(nev);
txtNev.Clear();
txtNev.Focus();
Osszesit();
}

private void btnUj_Click(object sender, EventArgs e) { UjDiak(); }

private void txtNev_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) { e.SuppressKeyPress = true; UjDiak(); }
}

// a sor átlaga: az üres és az érvénytelen jegyeket kihagyja; null, ha nincs érvényes jegy
private double? SorAtlag(DataGridViewRow sor)
{
int db = 0;
int osszeg = 0;

foreach (string oszlop in jegyOszlopok)
{
int jegy;

if (int.TryParse(Convert.ToString(sor.Cells[oszlop].Value), out jegy) && jegy >= 1 && jegy <= 5)
{
osszeg += jegy;
db++;
}
}

if (db == 0) { return null; }

return (double)osszeg / db;
}

private void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || szamol) { return; }

szamol = true;
double? atlag = SorAtlag(dgv.Rows[e.RowIndex]);
dgv.Rows[e.RowIndex].Cells["colAtlag"].Value = atlag.HasValue ? (object)atlag.Value : null;
szamol = false;

Osszesit();
}

private void Osszesit()
{
double ossz = 0;
int db = 0;

foreach (DataGridViewRow sor in dgv.Rows)
{
if (sor.IsNewRow) { continue; }

double? a = SorAtlag(sor);

if (a.HasValue) { ossz += a.Value; db++; }
}

lblOsszesito.Text = dgv.Rows.Count + " diák, osztályátlag: " + (db == 0 ? "–" : (ossz / db).ToString("0.00"));
}

private void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex < 0 || dgv.Columns[e.ColumnIndex].Name != "colAtlag" || e.Value == null) { return; }

double a = Convert.ToDouble(e.Value);

if (a < 1.5)
{
e.Value = "▼ " + a.ToString("0.00");
e.FormattingApplied = true;
e.CellStyle.ForeColor = Color.Firebrick;
e.CellStyle.Font = new Font(dgv.Font, FontStyle.Bold);
}
else if (a > 4.5)
{
e.Value = "▲ " + a.ToString("0.00");
e.FormattingApplied = true;
e.CellStyle.ForeColor = Color.FromArgb(0, 100, 40);
}
}

private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || dgv.Columns[e.ColumnIndex].Name != "colTorol") { return; }

string nev = Convert.ToString(dgv.Rows[e.RowIndex].Cells["colNev"].Value);

if (MessageBox.Show(this, "Törlöd: " + nev + "?", "Törlés", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
{
dgv.Rows.RemoveAt(e.RowIndex);
Osszesit();
}
}

Három önálló gyakorlófeladat

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

1. Sorok száma és kijelölés

Az állapotsor mutassa: 12 sor, 3 kijelölt. Melyik eseményben frissíted, és mire kell vigyáznod az új sornál?

Egy lehetséges megoldás

A SelectionChanged és a RowsAdded/RowsRemoved eseményekben. Az új sor miatt AllowUserToAddRows = false esetén a Rows.Count pontos.

Form1.csC#
private void dgv_SelectionChanged(object sender, EventArgs e)
{
lblInfo.Text = dgv.Rows.Count + " sor, " + dgv.SelectedRows.Count + " kijelölt";
}

2. Váltakozó sorszín

A páratlan sorok háttere világosszürke. Melyik tulajdonsággal, és milyen kontraszt kell a szövegnek?

Egy lehetséges megoldás

Az AlternatingRowsDefaultCellStyle.BackColor tulajdonsággal; a szöveg sötét maradjon (kontraszt, 6.9 lecke).

Form1.csC#
dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.FromArgb(244, 240, 250);
dgv.DefaultCellStyle.ForeColor = Color.FromArgb(30, 25, 40);

3. ComboBox oszlop

A Szint oszlop legördülő lista (Kezdő, Haladó, Mester). Hogyan hozod létre, és mit teszel az új sorral?

Egy lehetséges megoldás

A DataGridViewComboBoxColumn Items gyűjteménye adja a választékot; sor hozzáadásakor a cella értékének a lista egyik elemének kell lennie (különben DataError).

Form1.csC#
DataGridViewComboBoxColumn szint = new DataGridViewComboBoxColumn { Name = "colSzint", HeaderText = "Szint" };
szint.Items.AddRange("Kezdő", "Haladó", "Mester");
dgv.Columns.Add(szint);

int i = dgv.Rows.Add();
dgv.Rows[i].Cells["colSzint"].Value = "Kezdő"; // a lista egyik eleme

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. Name és HeaderText

Mi a különbség a két tulajdonság között, és melyiket használod hivatkozáshoz?

Megoldás megnyitása

A HeaderText a látható fejléc, a Name a kódbeli azonosító: a Columns["név"] indexelő ezt használja.

2. Az új sor

Hogyan ismered fel, és hogyan tiltod le?

Megoldás megnyitása

A sor.IsNewRow igaz az üres beviteli sorra; a AllowUserToAddRows = false megszünteti.

Közepes szint

3. Oszlop összege

Írj metódust, amely az Ár oszlop összegét számolja (érvényes számok, új sor kihagyva).

Megoldás megnyitása

Ciklus IsNewRow ellenőrzéssel és TryParse-szal.

Form1.csC#
private decimal Osszeg(string oszlop)
{
decimal ossz = 0;

foreach (DataGridViewRow sor in dgv.Rows)
{
decimal ertek;

if (!sor.IsNewRow && decimal.TryParse(Convert.ToString(sor.Cells[oszlop].Value), out ertek))
{
ossz += ertek;
}
}

return ossz;
}

4. Sorok színezése feltétel szerint

Az Ár 1000 feletti sorai legyenek sárga hátterűek. Hol színezel (és miért nem a CellValueChanged-ben)?

Megoldás megnyitása

A CellFormatting eseményben: a megjelenítés dönti el a stílust, így a rendezés és görgetés után is helyes.

Form1.csC#
private void dgv_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (e.RowIndex < 0) { return; }

int ar;

if (int.TryParse(Convert.ToString(dgv.Rows[e.RowIndex].Cells["colAr"].Value), out ar) && ar > 1000)
{
e.CellStyle.BackColor = Color.LemonChiffon;
e.CellStyle.ForeColor = Color.Black;
}
}

Kihívás szint

5. Sorok mozgatása

A Fel gomb a kijelölt sort egy hellyel feljebb viszi. Hogyan oldod meg a DataGridView-ban?

Megoldás megnyitása

A sor kivétele (Rows.Remove), beszúrása (Rows.Insert(index, sor)), és a kijelölés visszaállítása. Adatkötéses tábláknál a mozgatás az adatgyűjteményben történik (8.4 lecke).

Form1.csC#
private void btnFel_Click(object sender, EventArgs e)
{
if (dgv.CurrentRow == null || dgv.CurrentRow.Index == 0) { return; }

int i = dgv.CurrentRow.Index;
DataGridViewRow sor = dgv.Rows[i];

dgv.Rows.RemoveAt(i);
dgv.Rows.Insert(i - 1, sor);
dgv.ClearSelection();
dgv.Rows[i - 1].Selected = true;
}

6. Szerkesztés letiltása feltétel szerint

A Kész sorok többé nem szerkeszthetők. Melyik eseményt használod?

Megoldás megnyitása

A CellBeginEdit eseményt: az e.Cancel = true megakadályozza a szerkesztést.

Form1.csC#
private void dgv_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
object kesz = dgv.Rows[e.RowIndex].Cells["colKesz"].Value;

if (kesz is bool && (bool)kesz && dgv.Columns[e.ColumnIndex].Name != "colKesz")
{
e.Cancel = true;
}
}

Gyors önellenőrzés

1. Milyen típusú a cella értéke?

object: lehet szöveg, szám, bool vagy null.

2. Mi az új sor?

A tábla alján álló üres sor (IsNewRow), amelybe a felhasználó beírhat; a Rows.Count része, értékei null.

3. Mi az e.RowIndex a fejléc eseményeinél?

-1: ellenőrizd az eseménykezelő elején.

4. Hogyan törlöd a kijelölt sorokat?

Visszafelé haladó ciklussal az Index értékek alapján (a kijelölés törlés közben változik).

5. Miért kell a CommitEdit a jelölőnégyzetnél?

Az érték alapból a cella elhagyásakor érvényesül; a CommitEdit azonnal.

6. Mi a különbség a CellClick és a CellContentClick között?

A CellContentClick csak a tartalomra (gomb, jelölőnégyzet) kattintva fut.

7. Mire való a CellFormatting?

A cella megjelenítésének módosítása (szín, szöveg) rajzoláskor, az adat módosítása nélkül.

Mit tudsz már?

  • Kódból építesz táblázatot különféle oszloptípusokkal.
  • Sorokat és cellákat kezelsz típushelyesen, kerülöd az új sor és a null csapdáit.
  • Kijelölést olvasol, és biztonságosan törölsz.
  • Eseményekkel reagálsz, formázol (CellFormatting), és kezeled a DataError eseményt.

Következő lecke: List, BindingList és BindingSource

Az adatot a gyűjtemény tartja: a táblázat vagy lista csak megjeleníti, és magától frissül.