Grafikus C# – Windows Forms tanfolyam › 5.2 lecke

Windows Forms · 5.2 lecke · eseményparaméterek

sender, EventArgs és eseményparaméterek

Minden eseménykezelő két paramétert kap: object sender és EventArgs e. Eddig többnyire figyelmen kívül hagytuk őket. Pedig kincsek: a sender megmondja, ki váltotta ki az eseményt, az e pedig részleteket hoz: hová kattintottak, melyik billentyűt nyomták le, megszakítható-e a bezárás. Ebben a leckében megtanulod ezeket használni, és kétirányú kommunikációt is látsz: a kezelő nemcsak olvas az e-ből, hanem vissza is szólhat neki (e.Cancel, e.Handled).

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

A lecke végére

  • a sender paramétert típusátalakítással (as, is, kasztolás) használod, és ismered a buktatóit;
  • tudod, hogy az EventArgs egy családnak az őse, és melyik eseményhez melyik leszármazott tartozik;
  • használod a MouseEventArgs, KeyEventArgs, KeyPressEventArgs, FormClosingEventArgs és PaintEventArgs fontos tagjait;
  • érted, hogyan szól vissza a kezelő az eseménynek (Cancel, Handled, SuppressKeyPress);
  • felismered a leggyakoribb hibákat (hibás átalakítás, rossz paramétertípus, a koordináták eltérése).

Mielőtt elkezded

Ehhez a leckéhez az 5.1 lecke (események, delegáltak) és a 3.9 lecke (osztályok, öröklődés fogalma) szükséges. Új projektben próbáld ki a példákat.

5.1 Események és delegáltak →

A sender: ki váltotta ki az eseményt?

A sender az eseményt kiváltó objektum: a megnyomott gomb, a módosított szövegmező, az ablak. A típusa object (a legáltalánosabb típus), mert az EventHandler minden objektumhoz használható. Ha a konkrét típus tulajdonságait el akarod érni, át kell alakítani:

Form1.csC#
private void Gomb_Click(object sender, EventArgs e)
{
// 1. kasztolás: hibát dob, ha a sender nem Button
Button gomb = (Button)sender;
MessageBox.Show("A gomb felirata: " + gomb.Text);

// 2. as: null értéket ad, ha a sender nem Button
Button masik = sender as Button;
if (masik != null)
{
MessageBox.Show("A neve: " + masik.Name);
}

// 3. is + névvel: ellenőrzés és átalakítás egyben (C# 7)
if (sender is Button g)
{
MessageBox.Show("Szélessége: " + g.Width);
}

// 4. az alaposztály: minden vezérlő Control, ezért a közös tulajdonságok így is elérhetők
Control vezerlo = (Control)sender;
MessageBox.Show("Név: " + vezerlo.Name + ", Bal: " + vezerlo.Left);
}
(Button)senderA kasztolás. Ha a sender nem Button, InvalidCastException hibát dob. Akkor használd, ha biztosan tudod a típust.
sender as ButtonHa az átalakítás nem sikerül, nem hibát, hanem null értéket ad. Mindig vizsgáld a null értéket használat előtt.
sender is Button gEgy lépésben ellenőriz és átalakít: ha igaz, a g változó a Button típusú hivatkozás.
ControlA Button, a TextBox, a Label és a Panel mind a Control leszármazottja, ezért a közös tulajdonságok (Name, Text, Left, BackColor) minden vezérlőnél elérhetők a Control típuson át.

Nem minden sender vezérlő

A Timer komponens Tick eseményének sender paramétere a Timer, amely nem Control (nincs Name-je és Left tulajdonsága). A menüelemek (ToolStripMenuItem) szintén nem Control típusúak. Ezért ha több különböző típusú objektum is ugyanazt a kezelőt használja, a (Control)sender hibát dobhat: ilyenkor az is vagy az as a biztonságos.

Jósolj!

Ugyanazt az eseménykezelőt (Gomb_Click) egy Button és egy Label Click eseményéhez is hozzákötöd. A kezelőben Button gomb = (Button)sender; áll. Mi történik a címkére kattintva? Hogyan javítanád?

Jóslat ellenőrzése

A címkére kattintva InvalidCastException keletkezik: a Label nem alakítható Button típusúra. A javítás: if (sender is Button gomb) { ... }, vagy a közös Control típusra alakítás, ha csak a közös tulajdonságok kellenek (Control c = (Control)sender;).

Az EventArgs család

Az EventArgs az eseményadatok alaposztálya. A Click esemény csak jelzi, hogy kattintottak, ezért az EventArgs üres. Más eseményeknél a .NET az EventArgs leszármazott osztályát adja, amely az esemény részleteit tartalmazza. A kezelő paraméterének típusa ezért eseményenként eltér:

EseményParaméter típusaMit tartalmaz?
Click, TextChanged, LoadEventArgsSemmi érdemit (csak jelez).
MouseDown, MouseUp, MouseMove, MouseClickMouseEventArgsButton (melyik gomb), X, Y, Location, Clicks, Delta (görgetés).
KeyDown, KeyUpKeyEventArgsKeyCode (melyik billentyű), Control, Shift, Alt, Modifiers, Handled, SuppressKeyPress.
KeyPressKeyPressEventArgsKeyChar (a beírt karakter), Handled.
FormClosingFormClosingEventArgsCloseReason (miért zárul), Cancel (megszakítható).
ValidatingCancelEventArgsCancel.
PaintPaintEventArgsGraphics (rajzolóobjektum), ClipRectangle.
CellClick (DataGridView)DataGridViewCellEventArgsRowIndex, ColumnIndex.

Hogyan tudod meg, mit tartalmaz az e?

A kezelő paraméterlistájában az e típusa megmutatja a családot. Írd be az e. szöveget a kezelőben, és az IntelliSense listázza az elérhető tagokat. Ha egy új eseményt használsz, a Visual Studio a duplakattintással létrehozott metódusban már a helyes típust adja. Ha kézzel írod a metódust, a típust az esemény dokumentációjából vagy az IntelliSense-ből (az esemény nevére állva) olvashatod ki.

Jósolj!

Egy TextBox KeyPress eseményéhez ezt a kezelőt írod: private void txtSzam_KeyPress(object sender, KeyEventArgs e), és a tervezőben kijelölöd. Lefordul-e, és mi történik, amikor hozzákötöd?

Jóslat ellenőrzése

A KeyPress esemény delegáltja KeyPressEventHandler, amely KeyPressEventArgs típusú paramétert vár, nem KeyEventArgs-t. A Designer.cs-ben a feliratkozás fordítási hibát ad: CS0123: A „txtSzam_KeyPress” egyetlen túlterhelése sem felel meg a delegált típusnak. A javítás: a paraméter típusa legyen KeyPressEventArgs. A szabály: az e típusát az esemény határozza meg, nem te.

Egér és billentyű: az eseményadatok használata

MouseEventArgs: hová és melyik gombbal kattintottak?

Tegyél az ablakra egy pnlTer panelt és egy lblInfo feliratot:

Form1.csC#
private void pnlTer_MouseMove(object sender, MouseEventArgs e)
{
lblInfo.Text = $"X: {e.X}, Y: {e.Y}";
}

private void pnlTer_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
lblInfo.Text = "Bal gomb: " + e.Location;
}
else if (e.Button == MouseButtons.Right)
{
lblInfo.Text = "Jobb gomb: " + e.Location;
}
}

private void pnlTer_MouseWheel(object sender, MouseEventArgs e)
{
lblInfo.Text = "Görgetés: " + e.Delta; // előre: 120, hátra: -120
}
e.X, e.Y, e.LocationAz egér helye a vezérlőhöz képest (a vezérlő bal felső sarka a (0, 0) pont). Az e.Location egy Point, amely a két értéket együtt adja.
e.ButtonMelyik egérgomb váltotta ki: MouseButtons.Left, Right, Middle.
e.ClicksHány egymást követő kattintás (2: dupla kattintás).
e.DeltaA görgetés mértéke: pozitív előre, negatív hátra (általában ±120 egységenként).

KeyEventArgs és KeyPressEventArgs: melyik billentyű?

Form1.cs (a Form KeyPreview tulajdonsága: True)C#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.S)
{
lblInfo.Text = "Ctrl+S: mentés";
e.SuppressKeyPress = true;
}
else if (e.Shift && e.KeyCode == Keys.F5)
{
lblInfo.Text = "Shift+F5";
}
else
{
lblInfo.Text = "Billentyű: " + e.KeyCode;
}
}

private void txtSzam_KeyPress(object sender, KeyPressEventArgs e)
{
// csak számjegy és vezérlőkarakter (például Backspace) engedélyezett
if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
{
e.Handled = true; // a karakter nem kerül a szövegmezőbe
}
}
e.KeyCodeA fizikai billentyű (Keys.S, Keys.Enter, Keys.F5). Nem különbözteti meg a kis- és nagybetűt.
e.Control, e.Shift, e.AltIgaz, ha a módosító billentyű le van nyomva az esemény pillanatában. Így kezelhetők a billentyűkombinációk.
e.KeyChar (KeyPress)A beírt karakter: kis- és nagybetűre érzékeny (a és A különbözik).
char.IsDigit, char.IsControlA karakter számjegy, illetve vezérlőkarakter (Backspace, Enter) vizsgálata. A vezérlőkaraktereket engedni kell, különben a törlés sem működne.

Jósolj!

Egy TextBox KeyDown eseményében e.KeyCode == Keys.A feltételre írsz kódot. Nagy A-t és kis a-t gépelsz. Lefut-e mindkettőnél? És ha ugyanezt a KeyPress eseményben az e.KeyChar == 'a' feltétellel próbálod?

Jóslat ellenőrzése

A KeyDown KeyCode értéke a fizikai billentyű, ezért az A és az a megnyomásakor is Keys.A: mindkettőnél lefut. A KeyPress KeyChar értéke a beírt karakter, ezért csak a kis a-nál igaz az == 'a' feltétel, a nagy A ('A') más karakter. A szabály: a billentyűkombinációkhoz és a vezérlőbillentyűkhöz KeyDown, a beírt szöveg szűréséhez KeyPress való.

Visszaszólás az eseménynek: Cancel és Handled

Az eseményadat nemcsak információt hoz: bizonyos eseményeknél a kezelő vissza is szólhat a kibocsátónak, és befolyásolhatja a folytatást. Az eseményadat (e) egy osztály példánya, ezért a kezelő által beállított értéket a kibocsátó a kezelő lefutása után kiolvassa:

Form1.csC#
// 1. FormClosing: a bezárás megszakítható
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.UserClosing)
{
return; // a Windows leállítása vagy más ok: ne kérdezzünk
}

if (MessageBox.Show("Biztosan kilépsz?", "Kilépés", MessageBoxButtons.YesNo) == DialogResult.No)
{
e.Cancel = true; // az ablak nem záródik be
}
}

// 2. Validating: a fókusz elhagyása megakadályozható
private void txtNev_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
if (txtNev.Text.Trim().Length == 0)
{
epHiba.SetError(txtNev, "A név kötelező!");
e.Cancel = true; // a kurzor a mezőben marad
}
else
{
epHiba.SetError(txtNev, "");
}
}

// 3. KeyPress: a karakter bekerülésének megakadályozása
private void txtKod_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsLower(e.KeyChar))
{
e.KeyChar = char.ToUpper(e.KeyChar); // a karakter megváltoztatható is: nagybetű lesz
}
}
e.Cancel = trueMegszakítja a folyamatot: a bezárás elmarad (FormClosing), vagy a fókusz a mezőben marad (Validating).
e.Handled = trueJelzi, hogy az eseményt már kezelted, a rendszer ne dolgozza fel tovább. A KeyPress-ben ez azt jelenti, hogy a karakter nem kerül a szövegmezőbe.
e.SuppressKeyPress = trueA KeyDown eseményben megakadályozza, hogy a billentyű a vezérlőbe kerüljön (és a csengő hang elmaradjon). A Handled önmagában itt nem mindig elég.
e.CloseReasonA bezárás oka (UserClosing, WindowsShutDown, TaskManagerClosing): érdemes csak a felhasználói bezárásnál rákérdezni.

Jósolj!

A Form1_FormClosing kezelőben nincs CloseReason ellenőrzés, mindig rákérdez. A felhasználó a Windows leállítását kéri, miközben a program fut. Mi történik?

Jóslat ellenőrzése

A kérdés megjelenik a leállítás közben is. Ha a felhasználó a Nem gombot választja, vagy nem válaszol, a program megakadályozhatja a Windows leállítását (a rendszer jelzi, hogy egy program nem engedi). Ez zavaró, és egy kész programban kerülendő. Ezért ellenőrizd az e.CloseReason értékét: csak a UserClosing esetén kérdezz, minden más oknál engedd a bezárást (a mentetlen adatokat pedig automatikusan mentsd el, ha lehet).

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. InvalidCastException a sender átalakításakor

Button gomb = (Button)sender;   // a sender egy Timer, vagy egy Label
Hiba és javítás

A sender nem Button. Ellenőrizd az is vagy as használatával: if (sender is Button gomb) { ... }. Ha ugyanazt a kezelőt több típushoz kötötted, mindig számolj a nem várt típussal.

2. NullReferenceException az as átalakítás után

Button gomb = sender as Button;
gomb.Text = "Kész"; // ha a sender nem Button, a gomb null
Hiba és javítás

Az as sikertelen átalakításnál null értéket ad. Használat előtt vizsgáld meg: if (gomb != null), vagy használd az is Button gomb mintát, amely egyben ellenőriz.

3. A kezelő paraméterének típusa nem egyezik

private void txtSzam_KeyPress(object sender, KeyEventArgs e)
// CS0123: a metódus egyetlen túlterhelése sem felel meg a KeyPressEventHandler delegáltnak
Hiba és javítás

A KeyPress esemény KeyPressEventArgs típusú paramétert ad. A paraméter típusát az esemény határozza meg. A legbiztosabb: duplakattintás a Properties ablak Events nézetében, a Visual Studio a helyes szerkezetet generálja.

4. Hiányzó tag az e paraméteren

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyChar == 'a') { } // CS1061: a KeyEventArgs nem tartalmaz KeyChar definíciót
}
Hiba és javítás

A KeyDown esemény KeyEventArgs típusú, amelyben nincs KeyChar, csak KeyCode (e.KeyCode == Keys.A). A KeyChar a KeyPressEventArgs-ban van. A két esemény különböző adatot ad.

5. A tiltott karakter mégis bekerül

A KeyDown eseményben megpróbálod megakadályozni a betűk beírását e.Handled = true sorral, de a betű mégis megjelenik.

Ok és javítás

A KeyDown eseményben a karakter megakadályozásához az e.SuppressKeyPress = true kell. A Handled a KeyPress eseményben megakadályozza a karaktert, a KeyDown esetén azonban a KeyPress esemény még ettől függetlenül lefuthat. A legegyszerűbb: a karakterek szűrését a KeyPress eseményben végezd (e.Handled = true).

6. Az egér koordinátája eltér

A pnlTer_MouseMove kezelőben az e.X értéke más, mint a Cursor.Position.X.

Ok és javítás

Az e.X és e.Y a vezérlőhöz képest adja az egér helyét (a vezérlő bal felső sarka (0, 0)). A Cursor.Position a képernyőhöz képest, így a kettő eltér. A koordináták átszámolásához a PointToClient (képernyő → vezérlő) és a PointToScreen (vezérlő → képernyő) metódus használható.

Mini projekt: egér- és billentyűfigyelő

Készíts programot, amely az eseményadatokat valós időben megjeleníti:

  1. Az ablakon: egy nagy pnlTer panel, fölötte négy felirat (lblPoz, lblGomb, lblGorgetes, lblBillentyu), egy lstNaplo (ListBox) és egy txtSzam TextBox.
  2. A panel MouseMove eseménye a pozíciót (X: 120, Y: 45), a MouseDown a lenyomott gombot (Bal, Jobb, Közép), a MouseWheel a görgetés mértékét jeleníti meg.
  3. A Form KeyDown eseménye (KeyPreview: True) kiírja a billentyű nevét a módosítókkal együtt (Ctrl+Shift+S), és az lstNaplo felső sorába is beírja.
  4. A txtSzam mező csak számjegyet és Backspace-t enged be (KeyPress).
  5. A FormClosing csak felhasználói bezárásnál (CloseReason) kérdez rá a kilépésre.
  6. A panel MouseDown eseményére egy közös, object sender alapján működő kezelő is felirat: kiírja a panel nevét (Control átalakítással).
  7. Teszteld: az összes egérgomb, a görgetés, billentyűkombinációk, betű beírása a számmezőbe, a kilépés megszakítása.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A módosítók szövegét egy segédmetódus állítja össze az e.Control, e.Shift, e.Alt alapján. A MouseWheel eseményt kódból kell hozzákötni (a Properties ablak Events nézetében nem szerepel mindig). A panel nem kap fókuszt, ezért a görgetéshez pnlTer.Focus() kell a MouseEnter eseményben.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
this.KeyPreview = true;
pnlTer.MouseWheel += pnlTer_MouseWheel; // kódból kötjük
pnlTer.MouseEnter += (s, ev) => pnlTer.Focus();
}

private void pnlTer_MouseMove(object sender, MouseEventArgs e)
{
lblPoz.Text = $"X: {e.X}, Y: {e.Y}";
}

private void pnlTer_MouseDown(object sender, MouseEventArgs e)
{
string gomb = "Más";

if (e.Button == MouseButtons.Left)
{
gomb = "Bal";
}
else if (e.Button == MouseButtons.Right)
{
gomb = "Jobb";
}
else if (e.Button == MouseButtons.Middle)
{
gomb = "Közép";
}

Control vezerlo = (Control)sender;
lblGomb.Text = $"{gomb} gomb ({vezerlo.Name})";
}

private void pnlTer_MouseWheel(object sender, MouseEventArgs e)
{
lblGorgetes.Text = "Görgetés: " + e.Delta;
}

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
string szoveg = "";

if (e.Control)
{
szoveg = szoveg + "Ctrl+";
}

if (e.Shift)
{
szoveg = szoveg + "Shift+";
}

if (e.Alt)
{
szoveg = szoveg + "Alt+";
}

szoveg = szoveg + e.KeyCode;

lblBillentyu.Text = szoveg;
lstNaplo.Items.Insert(0, szoveg);
}

private void txtSzam_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
{
e.Handled = true;
}
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.UserClosing)
{
return;
}

if (MessageBox.Show("Biztosan kilépsz?", "Kilépés", MessageBoxButtons.YesNo) == DialogResult.No)
{
e.Cancel = true;
}
}
📷 Képernyőkép helye

Mit tegyél ide: a futó program: az egér a panel felett, a feliratokon a pozíció és a lenyomott gomb, a naplóban néhány billentyűkombináció.Javasolt fájlnév: winforms-5-2-kesz.webp · Alt szöveg: Az egér- és billentyűfigyelő program

Három önálló gyakorlófeladat

Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.

1. Egérgombonként más üzenet

Egy btnProba gombon a bal, a jobb és a középső egérgomb MouseDown eseménye különböző üzenetet jelenítsen meg a lblUzenet feliraton. Miért nem elég a Click esemény?

Egy lehetséges megoldás

A Click esemény EventArgs típusú, amely nem mondja meg, melyik gombbal kattintottak (és a jobb gombra a gomb Click eseménye általában nem is fut le). A MouseDown MouseEventArgs-ot ad, amelynek Button tulajdonsága megkülönbözteti a gombokat.

Form1.csC#
private void btnProba_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
lblUzenet.Text = "Bal gombbal kattintottál";
}
else if (e.Button == MouseButtons.Right)
{
lblUzenet.Text = "Jobb gombbal kattintottál";
}
else if (e.Button == MouseButtons.Middle)
{
lblUzenet.Text = "A görgőt nyomtad meg";
}
}

2. Csak számjegy, tizedesjellel

A txtAr mező csak számjegyet, Backspace-t és legfeljebb egy tizedesjelet (,) engedjen be. Hogyan ellenőrzöd, hogy már van-e tizedesjel?

Egy lehetséges megoldás

A tizedesjelet csak akkor engedjük, ha a szöveg még nem tartalmaz (Text.Contains). A beillesztéssel (Ctrl+V) bekerülő szöveg nem a KeyPress eseményen megy át, ezért az ellenőrzést a Validating eseményben is érdemes elvégezni (decimal.TryParse).

Form1.csC#
private void txtAr_KeyPress(object sender, KeyPressEventArgs e)
{
if (char.IsDigit(e.KeyChar) || char.IsControl(e.KeyChar))
{
return;
}

if (e.KeyChar == ',' && !txtAr.Text.Contains(","))
{
return;
}

e.Handled = true;
}

3. Kilépés csak mentetlen adatnál

Az ablak csak akkor kérdezzen rá a bezárásra, ha a txtJegyzet mező módosult (bool modosult jelző), és csak felhasználói bezárásnál. A Mégse esetén az ablak maradjon nyitva.

Egy lehetséges megoldás

A jelzőt a TextChanged esemény állítja igazra. A FormClosing csak akkor kérdez, ha a jelző igaz és a bezárás oka a felhasználó. A kérdés három gombos (Igen: mentés, Nem: bezárás mentés nélkül, Mégse: maradás).

Form1.csC#
private bool modosult = false;

private void txtJegyzet_TextChanged(object sender, EventArgs e)
{
modosult = true;
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!modosult || e.CloseReason != CloseReason.UserClosing)
{
return;
}

DialogResult valasz = MessageBox.Show("Mented a jegyzetet?", "Kilépés", MessageBoxButtons.YesNoCancel);

if (valasz == DialogResult.Cancel)
{
e.Cancel = true;
}
else if (valasz == DialogResult.Yes)
{
// a mentés kódja
}
}

Gyakorlótár: 6 további feladat

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

Alap szint

1. A megnyomott gomb felirata

Három gomb (btnA, btnB, btnC) ugyanazt a Gomb_Click kezelőt használja, és az lblEredmeny a megnyomott gomb feliratát mutassa. Hogyan használod a sender paramétert?

Megoldás megnyitása

A sender a megnyomott gomb. Az as átalakítással érjük el a Text tulajdonságát, és ellenőrizzük a null értéket.

Form1.csC#
private void Gomb_Click(object sender, EventArgs e)
{
Button gomb = sender as Button;

if (gomb != null)
{
lblEredmeny.Text = "Megnyomtad: " + gomb.Text;
}
}

2. Koordináták kijelzése

Egy pbRajz PictureBox fölött az egér mozgásakor a lblKoord mutassa az egér helyét a PictureBoxhoz képest, és külön a képernyő-koordinátát is (Cursor.Position). Mi a különbség?

Megoldás megnyitása

Az e.X, e.Y a vezérlőhöz mérten adja a helyet, a Cursor.Position a teljes képernyőhöz. A PointToClient a képernyő-koordinátát vezérlő-koordinátává alakítja.

Form1.csC#
private void pbRajz_MouseMove(object sender, MouseEventArgs e)
{
Point kepernyo = Cursor.Position;
Point vezerlo = pbRajz.PointToClient(kepernyo); // megegyezik az e.Location-nel

lblKoord.Text = $"Vezérlő: {e.X}, {e.Y} | Képernyő: {kepernyo.X}, {kepernyo.Y}";
}

Közepes szint

3. Billentyűkombináció

A Ctrl+Shift+T kombináció váltsa át az ablak háttérszínét sötétre, a Ctrl+L világosra. A kombináció ne kerüljön a szövegmezőbe, és kezeld le a KeyDown eseményben.

Megoldás megnyitása

A módosítókat az e.Control és e.Shift adja, a billentyűt az e.KeyCode. A SuppressKeyPress megakadályozza, hogy a billentyű a fókuszban lévő vezérlőbe kerüljön. A KeyPreview legyen igaz, hogy a Form is megkapja az eseményt.

Form1.csC#
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.Shift && e.KeyCode == Keys.T)
{
this.BackColor = Color.FromArgb(40, 40, 48);
e.SuppressKeyPress = true;
}
else if (e.Control && e.KeyCode == Keys.L)
{
this.BackColor = SystemColors.Control;
e.SuppressKeyPress = true;
}
}

4. Kötelező mező a Validating eseménnyel

A txtEmail mező elhagyásakor ellenőrizd, hogy a szöveg tartalmaz-e @ jelet. Ha nem, ErrorProvider jelezzen, és a kurzor maradjon a mezőben. Mit kell tenned, hogy az ablak bezárható legyen akkor is, ha a mező hibás?

Megoldás megnyitása

A Validating esemény CancelEventArgs-ot ad: az e.Cancel = true a fókuszt a mezőben tartja. A hibás mező ilyenkor az ablak bezárását is megakadályozhatja: a Form AutoValidate tulajdonságát EnableAllowFocusChange értékre állítva a fókusz elhagyható, vagy a bezáró gomb CausesValidation tulajdonsága False (a Mégse gombnál ez a szokás).

Form1.csC#
private void txtEmail_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!txtEmail.Text.Contains("@"))
{
epHiba.SetError(txtEmail, "Az e-mail címnek tartalmaznia kell a @ jelet.");
e.Cancel = true;
}
else
{
epHiba.SetError(txtEmail, "");
}
}

// a Mégse gombnál a tervezőben: CausesValidation = False

Kihívás szint

5. Egy kezelő több típushoz

Egy közös Kozos_Click kezelő egy Button, egy Label és egy Panel Click eseményét is kezeli, és az lblInfo-ban kiírja a vezérlő típusát és nevét. A gombnál a feliratot is. Hogyan kezeled biztonságosan a különböző típusokat?

Megoldás megnyitása

A sender is Button gomb minta ellenőriz és átalakít, így nem kell a null értéket külön vizsgálni. A közös Control típusra mindegyik átalakítható (Name), a típus nevét a GetType().Name adja.

Form1.csC#
private void Kozos_Click(object sender, EventArgs e)
{
Control vezerlo = (Control)sender;
string szoveg = vezerlo.GetType().Name + ": " + vezerlo.Name;

if (sender is Button gomb)
{
szoveg = szoveg + " (felirat: " + gomb.Text + ")";
}

lblInfo.Text = szoveg;
}

6. DataGridView cellaesemény

Egy dgvLista táblázat CellClick eseményében írd ki az lblInfo feliratra a kattintott cella sor- és oszlopszámát és értékét. Mire kell vigyázni a fejlécre kattintáskor?

Megoldás megnyitása

A DataGridViewCellEventArgs a RowIndex és ColumnIndex értéket adja. A fejlécre (oszlopnév) kattintva a RowIndex értéke -1, ezért a sor eléréséhez előbb ellenőrizni kell (e.RowIndex >= 0), különben hibát kapsz.

Form1.csC#
private void dgvLista_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || e.ColumnIndex < 0)
{
return; // fejlécre kattintottak
}

object ertek = dgvLista.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
lblInfo.Text = $"Sor: {e.RowIndex}, oszlop: {e.ColumnIndex}, érték: {ertek}";
}

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mi a sender, és miért object típusú?

Az eseményt kiváltó objektum. Azért object, mert az eseménykezelő szerkezete minden típushoz azonos: a konkrét típust szükség esetén át kell alakítani.

2. Mi a különbség a kasztolás, az as és az is között?

A kasztolás hibát dob, ha a típus nem egyezik. Az as null értéket ad. Az is logikai értéket ad, és mintával egyben átalakít is (is Button g).

3. Mi az EventArgs szerepe?

Az esemény részleteit hordozza. Az alaposztály üres, a leszármazottak adatot adnak (MouseEventArgs, KeyEventArgs, ...).

4. Mi a különbség a KeyDown és a KeyPress között?

A KeyDown a fizikai billentyűt adja (KeyCode, módosítók), a KeyPress a beírt karaktert (KeyChar).

5. Mire való az e.Cancel és az e.Handled?

A kezelő visszaszól az eseménynek: a Cancel megszakítja a folyamatot (bezárás, fókuszváltás), a Handled jelzi, hogy az eseményt már kezeltük (a karakter nem kerül a vezérlőbe).

6. Mit ad az e.X és e.Y, és miben tér el a Cursor.Position-től?

Az egér helyét a vezérlőhöz képest. A Cursor.Position a képernyőhöz képest adja.

7. Mikor érdemes az e.CloseReason értékét vizsgálni?

A FormClosing kezelőben: csak a felhasználói bezárásnál (UserClosing) kérdezzünk, hogy ne akadályozzuk meg például a Windows leállítását.

Mit tudsz már?

  • A sender paramétert biztonságosan átalakítod (as, is, kasztolás).
  • Tudod, hogy az e típusát az esemény határozza meg, és használod a fontos tagjait.
  • Egér- és billentyűadatokat dolgozol fel (Button, X, Y, KeyCode, KeyChar, módosítók).
  • Visszaszólsz az eseménynek (Cancel, Handled, SuppressKeyPress).
  • Felismered a gyakori eseménykezelési hibákat.

Következő lecke: Click, DoubleClick és egérműveletek

Most, hogy az eseményadatokat ismered, részletesen megnézzük az egérműveleteket: a Click és a DoubleClick különbségét, az egérgombok eseményeit és a húzást.