Grafikus C# – Windows Forms tanfolyam › 5.1 lecke Windows Forms · 5.1 lecke · események Az első leckéktől fogva eseménykezelőket írtál: a Események, delegáltak és eseménykezelő metódusok
btnMent_Click lefut, ha rákattintasz a gombra. De hogyan tudja a gomb, hogy melyik metódust hívja? Mi történik a háttérben, és hogyan hozhatsz létre saját eseményt? Ebben a leckében a „varázslat” mögé nézünk: az eseménykezelő metódus felépítésébe, a delegáltakba, a feliratkozásba (+=) és leiratkozásba (-=), a lambda kifejezésekbe, és megírod az első saját eseményedet.
A lecke végére
- érted az esemény szerepét: valami történik, és a program erre reagál;
- felismered az eseménykezelő metódus szerkezetét (
object sender, EventArgs e); - tudod, mi a delegált, és hogyan hivatkozik metódusra;
- az eseményt kódból is összekapcsolod (
+=), és szükség esetén leválasztod (-=); - használsz lambda kifejezést rövid eseménykezelőhöz;
- saját eseményt hozol létre és váltasz ki (
event,?.Invoke), és felismered a gyakori hibákat.
Mielőtt elkezded
Ehhez a leckéhez az 1. fejezet eseménykezelői, a 0.5 lecke (Form1.Designer.cs) és a 3.9 lecke (osztály, tulajdonság) szükséges. Új Windows Forms projektet készíts, és kísérletezz a példákkal.
Mi az esemény?
A grafikus programok eseményvezéreltek (0.2 lecke): nem egy előre megírt sorrendben futnak végig, hanem várnak, és reagálnak arra, ami történik: egy kattintásra, egy billentyűre, egy beírt szövegre, egy lejáró időzítőre. Az esemény (event) egy jelzés: „ez történt”. Az eseménykezelő (event handler) pedig az a metódus, amely a jelzésre reagál.
Hasonlat: a csengő (a Button) nem tudja, ki van otthon és mit csináljon. Annyit tud, hogy megszólal, és akik előfizettek rá (a lakók), azok reagálnak: az egyik ajtót nyit, a másik nem figyel. A csengő a kibocsátó (publisher), a lakók a feliratkozók (subscribers). A kibocsátó nem ismeri a feliratkozókat, ezért egy csengőhöz bármennyi (akár nulla) lakó tartozhat.
Button (Click), a TextBox (TextChanged), a Form (Load).Click, TextChanged, FormClosing.Jósolj!
Egy gombnak nincs eseménykezelője (senki nem iratkozott fel a Click eseményére). Mi történik, ha rákattintasz? Hibát kapsz?
Jóslat ellenőrzése
Nem történik semmi, hiba nincs. A gomb jelzi az eseményt, de ha senki nem figyel rá, a jelzés elveszik. A feliratkozók nélküli esemény teljesen normális helyzet.
Az eseménykezelő metódus felépítése
Az eseménykezelő metódusnak pontosan meghatározott szerkezete van. Ha duplán kattintasz egy gombra a tervezőben, a Visual Studio ezt generálja:
private void btnKoszon_Click(object sender, EventArgs e)
{
MessageBox.Show("Szia!");
}Click esetén nincs benne érdemi információ, a KeyPress-nél a lenyomott karakter is benne van.Hol köti össze a Visual Studio az eseményt és a metódust?
A kapcsolatot nem a metódus neve hozza létre, hanem egy sor a Form1.Designer.cs fájlban, az InitializeComponent metódusban (0.5 lecke). Ezt a Designer kezeli, ezért ne szerkeszd kézzel, de érdemes megnézni:
// btnKoszon
this.btnKoszon.Name = "btnKoszon";
this.btnKoszon.Text = "Köszön";
this.btnKoszon.Click += new System.EventHandler(this.btnKoszon_Click);Az utolsó sor a lényeg: a btnKoszon Click eseményére feliratkozik a btnKoszon_Click metódus. A += a feliratkozás jele, az EventHandler pedig a delegált típusa, amelyet a következő szakaszban nézünk meg.
Jósolj!
Átírod a btnKoszon_Click metódus nevét btnKoszon_Kattint-ra a Form1.cs fájlban (a Designer.cs-t nem módosítod). Lefordul-e a program? Hogy hívják ezt a hibát?
Jóslat ellenőrzése
Nem fordul le. A Designer.cs továbbra is a btnKoszon_Click nevű metódushoz akar kötni, de az már nincs: CS1061: A „Form1” nem tartalmaz „btnKoszon_Click” nevű definíciót. A javítás: a metódus átnevezése a Visual Studio Rename funkciójával (jobb gomb a néven → Rename), amely a Designer.cs-t is átírja, vagy a Properties ablak Events nézetében a kapcsolat átállítása.
A delegált: metódusra mutató típus
A delegált (delegate) egy típus, amelynek értéke egy metódus. Úgy működik, mint egy mutató vagy névjegykártya: nem a metódus kódját tartalmazza, hanem hivatkozik rá, és a delegáltat hívva a hivatkozott metódus fut le. A delegált típus megadja, milyen szerkezetű metódusra hivatkozhat (paraméterek és visszatérési típus):
// delegált típus: két egész számot kap, egész számot ad vissza
delegate int Muvelet(int a, int b);
private int Osszead(int a, int b) { return a + b; }
private int Szoroz(int a, int b) { return a * b; }
private void btnProba_Click(object sender, EventArgs e)
{
Muvelet m = Osszead; // a delegált az Osszead metódusra hivatkozik
MessageBox.Show(m(3, 4).ToString()); // 7
m = Szoroz; // most a Szoroz metódusra
MessageBox.Show(m(3, 4).ToString()); // 12
}int paramétert kapnak, és int értéket adnak vissza.Osszead metódusra hivatkozik (a metódus nevét zárójel nélkül írjuk: nem hívjuk, csak hivatkozunk rá).Az EventHandler egy delegált
A Windows Forms események típusa is delegált. Az EventHandler definíciója a .NET-ben:
public delegate void EventHandler(object sender, EventArgs e);Ebből következik az eseménykezelő szerkezete: olyan metódus, amely object és EventArgs paramétert kap, és nem ad vissza értéket (void). A Click esemény tehát EventHandler típusú delegáltak gyűjteménye: az eseményre feliratkozó metódusokat tárolja, és az esemény bekövetkeztekor mindet meghívja.
Beépített delegált típusok: Action és Func
Nem kell mindig saját delegált típust írnod. Az Action olyan metódusra hivatkozik, amely nem ad vissza értéket (Action<int>: egy int paraméter), a Func<int, int, int> két int paramétert kap, és int értéket ad vissza (az utolsó típus a visszatérési érték). A fenti Muvelet helyett Func<int, int, int> is használható.
Jósolj!
A Muvelet m = Osszead; sort átírod Muvelet m = Osszead(3, 4); alakra. Lefordul-e? Miért nem ugyanaz a két sor?
Jóslat ellenőrzése
Nem fordul le. Az Osszead(3, 4) a metódus hívása, amely egy int értéket (7) ad, nem egy Muvelet típusú delegáltat. A zárójel nélküli Osszead a metódusra hivatkozik. A különbség ugyanaz, mint az eseményeknél: a += btnKoszon_Click nem hívja a metódust, csak feliratkoztatja az eseményre.
Feliratkozás és leiratkozás kódból
A tervezőn kívül kódból is feliratkozhatsz egy eseményre, a += operátorral, és leiratkozhatsz a -= operátorral. Erre akkor van szükség, ha a vezérlőt kódból hozod létre, vagy az eseménykezelő csak bizonyos ideig kell:
private void Form1_Load(object sender, EventArgs e)
{
btnProba.Click += Proba_Click; // feliratkozás
}
private void Proba_Click(object sender, EventArgs e)
{
MessageBox.Show("Proba");
}
private void btnLeiratkozik_Click(object sender, EventArgs e)
{
btnProba.Click -= Proba_Click; // leiratkozás: a Proba_Click többé nem fut le
}Több feliratkozó: a „multicast”
Egy eseményre több metódus is feliratkozhat. Ezek a feliratkozás sorrendjében futnak le:
btnProba.Click += Elso;
btnProba.Click += Masodik;
btnProba.Click += Elso; // ugyanaz a metódus kétszer is feliratkozhat!
// egy kattintás: Elso, Masodik, ElsoLambda kifejezés: névtelen eseménykezelő
Rövid, egyszeri eseménykezelőhöz nem kell külön metódust írni: a lambda kifejezés (=> jellel) névtelen metódust ad:
// (paraméterek) => { utasítások }
btnProba.Click += (s, ev) =>
{
MessageBox.Show("Lambda kezelő");
};
// ha egyetlen utasítás, rövidebben:
btnProba.MouseEnter += (s, ev) => btnProba.BackColor = Color.LightYellow;
btnProba.MouseLeave += (s, ev) => btnProba.BackColor = SystemColors.Control;sender és EventArgs), a => jel után jön a kód. A paraméterek nevét te adod.-= operátorban (ugyanazt a lambdát nem tudod még egyszer leírni). Hosszabb, fontos kódhoz külön, névvel ellátott metódus kell.btnProba vezérlőt vagy egy helyi változót.Jósolj!
Egy gomb Click eseményére kétszer is feliratkozol ugyanazzal a metódussal: egyszer a tervezőben (Events nézet), egyszer a Form1_Load-ban (+=). Hányszor fut le a metódus egy kattintásra?
Jóslat ellenőrzése
Kétszer. Minden feliratkozás külön számít, ezért az üzenet kétszer jelenik meg. Ez az egyik leggyakoribb eseménykezelési hiba: a metódust a tervező és a kód is hozzákötötte. Megoldás: csak az egyik helyen iratkozz fel (kódból feliratkozva a tervezőben ne válaszd ki a metódust).
Saját esemény létrehozása
Az események nemcsak a vezérlőknek vannak: a saját osztályaidnak is lehet eseménye. Ez azt jelenti, hogy az osztály „jelez” valamit, és nem kell ismernie, ki figyel rá. Készítsünk egy számlálót, amely jelez, ha eléri a határértéket. Először az osztály:
public class Szamlalo
{
private int ertek = 0;
public int Hatar { get; set; } = 5;
public int Ertek
{
get { return ertek; }
}
// az esemény deklarációja
public event EventHandler ElerteAHatart;
public void Novel()
{
ertek = ertek + 1;
if (ertek >= Hatar)
{
// az esemény kiváltása: ha van feliratkozó, meghívja
ElerteAHatart?.Invoke(this, EventArgs.Empty);
}
}
public void Nulloz()
{
ertek = 0;
}
}És a használata az ablakban:
private Szamlalo szamlalo = new Szamlalo();
private void Form1_Load(object sender, EventArgs e)
{
szamlalo.ElerteAHatart += Szamlalo_ElerteAHatart; // feliratkozás a saját eseményre
}
private void Szamlalo_ElerteAHatart(object sender, EventArgs e)
{
lblUzenet.Text = "Elérted a határt!";
}
private void btnNovel_Click(object sender, EventArgs e)
{
szamlalo.Novel();
lblErtek.Text = szamlalo.Ertek.ToString();
}EventHandler delegált: olyan metódusok iratkozhatnak fel, amelyeknek a szerkezete (object, EventArgs).?. (null-feltételes operátor) csak akkor hívja az Invoke metódust, ha van legalább egy feliratkozó. A this a kibocsátó (sender), az EventArgs.Empty az üres adat.Szamlalo osztály nem tud a Form1-ről, a feliratkozás kívülről történik. Ezért újrahasznosítható: egy másik program más módon reagálhat ugyanarra a jelre.event kulcsszó miatt az osztályon kívülről az eseményt nem lehet kiváltani, csak += és -= műveletet lehet végezni rajta. Ez védi az osztályt a külső beavatkozástól.Jósolj!
Az Hatar értéke 5. Hányadik Novel() hívásnál jelenik meg az üzenet, és megjelenik-e újra a 6. hívásnál? Mit tennél, hogy az üzenet csak egyszer jelenjen meg?
Jóslat ellenőrzése
Az 5. hívásnál jelenik meg (ertek >= Hatar először akkor igaz). A 6. és minden további hívásnál újra kiváltódik, mert a feltétel továbbra is igaz (6 >= 5). Ha csak egyszer kell, a feltételt ertek == Hatar alakra írhatod át (csak pontosan a határnál jelez), vagy a kiváltás után egy jelzővel megjegyezheted, hogy már jelzett.
Mit tegyél ide: a futó program: egy Növel gomb, az érték felirata (5), és alatta a piros „Elérted a határt!” üzenet.Javasolt fájlnév: winforms-5-1-szamlalo.webp · Alt szöveg: Saját esemény a számláló osztályon
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. A metódus nem fordul le átnevezés után
// CS1061: A „Form1” nem tartalmaz „btnMent_Click” nevű definíciótHiba és javítás
A Form1.Designer.cs még a régi nevű metódust keresi. Állítsd vissza a metódus nevét, vagy használd a Rename funkciót, vagy a Properties → Events nézetben válaszd ki az új metódust.
2. A metódus kétszer fut le
Egy kattintásra az üzenet kétszer jelenik meg. A metódusra kétszer iratkoztál fel: a tervezőben (Events nézet) és kódból (Ok és javítás
+=). Töröld az egyiket. A Designer.cs fájlban kereshetsz is a Click += sorra.
3. Nem tudsz leiratkozni a lambdáról
btnProba.Click += (s, e) => MessageBox.Show("Hello");
btnProba.Click -= (s, e) => MessageBox.Show("Hello"); // nem működik!Hiba és javítás
A második lambda egy új névtelen metódus, amelyre senki nem iratkozott fel, ezért a -= nem csinál semmit (hiba sincs). Ha le akarsz iratkozni, használj névvel ellátott metódust, vagy tárold a lambdát egy változóban: EventHandler kezelo = (s, e) => ...; btnProba.Click += kezelo; ... btnProba.Click -= kezelo;
4. NullReferenceException az esemény kiváltásakor
ElerteAHatart(this, EventArgs.Empty); // hiba, ha nincs feliratkozóHiba és javítás
Ha az eseményre senki nem iratkozott fel, az érték null, és a hívása NullReferenceException hibát dob. Használd a ?.Invoke(...) alakot: ElerteAHatart?.Invoke(this, EventArgs.Empty); A ?. csak akkor hív, ha van feliratkozó.
5. Rossz szerkezetű metódus
private void Kezelo() // hiányoznak a paraméterek
{
}
btnProba.Click += Kezelo;
// CS0123: A „Kezelo” egyetlen túlterhelése sem felel meg a delegált „EventHandler” típusnakHiba és javítás
A Click esemény EventHandler típusú, tehát a metódusnak (object sender, EventArgs e) paraméterekkel kell rendelkeznie. Ha nincs szükséged a paraméterekre, akkor is meg kell adni őket (vagy lambda: (s, ev) => Kezelo()).
6. Az esemény nem fut le
A metódus megvan, de a gombnyomásra semmi sem történik. Valószínűleg nincs feliratkozva a metódus az eseményre. Ellenőrizd a Properties ablak Events nézetét (villám ikon): a Ok és javítás
Click sorban a metódus neve áll-e. Ha a metódust másolással hoztad létre, a Designer nem tud róla. Kódból a += sor hiányozhat.
Mini projekt: eseménynapló
Készíts programot, amely egy saját esemény és a vezérlők eseményeinek működését napló formájában mutatja meg:
- Az ablakon:
btnNovel,btnNulloz,btnKetszeres,lblErtek,lstNaplo(ListBox). - Egy
Szamlaloosztály (a leckéből), amelynek két eseménye van:ElerteAHatartésNullazodott. - Az ablak mindkét eseményre feliratkozik, és mindegyik reakció egy sort ír a naplóba (
HH:mm:ss – szöveg, a legújabb felül). - A
btnKetszeresegy kattintásra kétszer növeli a számlálót úgy, hogy abtnKetszeres.Clickeseményre két különböző metódus iratkozik fel kódból. - Egy jelölőnégyzet (
chkFigyel) be- és kikapcsolja a feliratkozást aElerteAHatarteseményre (+=és-=), és minden váltást a napló is rögzít. - Teszteld: a határ elérése, nullázás, feliratkozás ki- és bekapcsolása, a gombok többszöri megnyomása.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A napló egy közös metódus (Naplo), amelyet minden kezelő hív. A chkFigyel a feliratkozást a CheckedChanged eseményben kapcsolja. Fontos, hogy a feliratkozás egyszer történjen meg: a Form1_Load csak akkor iratkozik fel, ha a jelölőnégyzet bejelölt.
// Szamlalo.cs
public class Szamlalo
{
private int ertek = 0;
public int Hatar { get; set; } = 5;
public int Ertek { get { return ertek; } }
public event EventHandler ElerteAHatart;
public event EventHandler Nullazodott;
public void Novel()
{
ertek = ertek + 1;
if (ertek == Hatar)
{
ElerteAHatart?.Invoke(this, EventArgs.Empty);
}
}
public void Nulloz()
{
ertek = 0;
Nullazodott?.Invoke(this, EventArgs.Empty);
}
}
// Form1.cs
private Szamlalo szamlalo = new Szamlalo();
private void Naplo(string szoveg)
{
lstNaplo.Items.Insert(0, DateTime.Now.ToString("HH:mm:ss") + " – " + szoveg);
}
private void Form1_Load(object sender, EventArgs e)
{
szamlalo.Nullazodott += (s, ev) => Naplo("A számláló nullázódott.");
btnKetszeres.Click += Egyszer;
btnKetszeres.Click += Masodszor;
chkFigyel.Checked = true; // ez váltja ki a CheckedChanged eseményt, és ott iratkozunk fel
}
private void Elerte_Kezelo(object sender, EventArgs e)
{
Naplo("A számláló elérte a határt!");
}
private void chkFigyel_CheckedChanged(object sender, EventArgs e)
{
if (chkFigyel.Checked)
{
szamlalo.ElerteAHatart += Elerte_Kezelo;
Naplo("Feliratkoztunk a határ eseményére.");
}
else
{
szamlalo.ElerteAHatart -= Elerte_Kezelo;
Naplo("Leiratkoztunk a határ eseményéről.");
}
}
private void Frissit()
{
lblErtek.Text = szamlalo.Ertek.ToString();
}
private void btnNovel_Click(object sender, EventArgs e)
{
szamlalo.Novel();
Frissit();
}
private void btnNulloz_Click(object sender, EventArgs e)
{
szamlalo.Nulloz();
Frissit();
}
private void Egyszer(object sender, EventArgs e)
{
szamlalo.Novel();
}
private void Masodszor(object sender, EventArgs e)
{
szamlalo.Novel();
Frissit();
}Mit tegyél ide: a kész program: a három gomb, az érték, a jelölőnégyzet és a napló néhány sorral (köztük a „Feliratkoztunk” és a „határt elérte” sorral).Javasolt fájlnév: winforms-5-1-kesz.webp · Alt szöveg: Az eseménynapló 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. A kezelők sorrendje
Egy gomb A kezelők a feliratkozás sorrendjében futnak (A, B, C). Kétszeri feliratkozásnál az A kétszer fut le. A leiratkozás hatása a következő kattintásra érvényes, de az éppen futó eseményláncra nem mindig: a .NET az esemény kiváltásakor egy pillanatképpel dolgozik, ezért a B az éppen futó kattintásnál még lefuthat. A pontos viselkedést érdemes kipróbálni és megfigyelni.Click eseményére iratkoztass fel három metódust (A, B, C), amelyek egy ListBox-ba írják a nevüket. Milyen sorrendben jelennek meg? Mi történik, ha az A kezelőre kétszer iratkozol fel, és a B kezelőről leiratkozol a kattintás közben?Egy lehetséges megoldás
private void A(object s, EventArgs e) { lstSor.Items.Add("A"); }
private void B(object s, EventArgs e) { lstSor.Items.Add("B"); }
private void C(object s, EventArgs e) { lstSor.Items.Add("C"); }
private void Form1_Load(object sender, EventArgs e)
{
btnProba.Click += A;
btnProba.Click += B;
btnProba.Click += C;
}
2. Egyszer lefutó kezelő
Egy A kezelő a végén leiratkozik magáról a btnBeallit gomb első megnyomásakor jelenjen meg egy üzenet (Az első kattintás!), és ezután a gomb ne váltson ki több ilyen üzenetet. Oldd meg leiratkozással.Egy lehetséges megoldás
-= operátorral. Névvel ellátott metódus kell, mert a lambdáról nem lehetne leiratkozni.private void Form1_Load(object sender, EventArgs e)
{
btnBeallit.Click += ElsoKattintas;
}
private void ElsoKattintas(object sender, EventArgs e)
{
MessageBox.Show("Az első kattintás!");
btnBeallit.Click -= ElsoKattintas;
}
3. Saját esemény adatokkal
Bővítsd a Saját Szamlalo osztályt úgy, hogy az ElerteAHatart esemény átadja az elért értéket is (Elérted a határt: 5). Hogyan oldod meg az EventArgs segítségével?Egy lehetséges megoldás
EventArgs-ból származó osztály (SzamlaloEventArgs) viszi az adatot, az esemény típusa pedig az általános EventHandler<SzamlaloEventArgs>. A kezelő e.Ertek alakban éri el az adatot. Az öröklődés (: EventArgs) részletesebb tárgyalása a 12. fejezetben következik.// Szamlalo.cs
public class SzamlaloEventArgs : EventArgs
{
public int Ertek { get; set; }
}
public event EventHandler<SzamlaloEventArgs> ElerteAHatart;
// a kiváltás:
ElerteAHatart?.Invoke(this, new SzamlaloEventArgs { Ertek = ertek });
// Form1.cs
private void Szamlalo_ElerteAHatart(object sender, SzamlaloEventArgs e)
{
lblUzenet.Text = "Elérted a határt: " + e.Ertek;
}
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. Delegált és esemény: a különbség
Egy mondatban magyarázd el: mi a delegált, és mi az esemény? Miért nem iratkozhat fel ( A delegált egy típus, amelynek értéke egy (vagy több) metódusra mutató hivatkozás. Az esemény egy delegáltra épülő, védett szerkezet: kívülről csak feliratkozni (+=) egy egyszerű delegált-változóra az osztály kívülről?Megoldás megnyitása
+=) és leiratkozni (-=) lehet rajta, kiváltani csak a tulajdonos osztály tudja. Egy sima nyilvános delegált-mezőt bárki felülírhatna (= ...) vagy meghívhatna, ezért az event kulcsszó védi a kibocsátó osztályt.
2. Az eseménykezelő szerkezete
Írd meg egy A lblFelirat MouseEnter eseményének kezelőjét úgy, hogy a felirat színe pirosra váltson. Mi a metódus pontos szerkezete?Megoldás megnyitása
MouseEnter is EventHandler típusú: void visszatérés, object sender és EventArgs e paraméterek. A feliratkozás a tervezőben (Events nézet) vagy kódból történik.private void lblFelirat_MouseEnter(object sender, EventArgs e)
{
lblFelirat.ForeColor = Color.Red;
}
// kódból: lblFelirat.MouseEnter += lblFelirat_MouseEnter;
Közepes szint
3. Gombok kódból
Egy A kódból létrehozott vezérlő addig nem látszik, amíg nincs az ablak (vagy egy panel) Form1_Load esemény kódból hozzon létre egy Button-t (Kattints rám), tegye az ablakra, és iratkozzon fel a Click eseményére, amely üzenetet ír ki. Miért kell a gombot a Controls gyűjteményhez adni?Megoldás megnyitása
Controls gyűjteményében: a gyűjtemény mondja meg, mely vezérlők tartoznak az ablakhoz, és ezeket rajzolja ki. A tervező is ezt teszi az InitializeComponent metódusban.private void Form1_Load(object sender, EventArgs e)
{
Button gomb = new Button();
gomb.Text = "Kattints rám";
gomb.Location = new Point(20, 20);
gomb.Size = new Size(150, 40);
gomb.Click += (s, ev) => MessageBox.Show("Megnyomtál!");
this.Controls.Add(gomb);
}
4. Lambda és külső változó
Hozz létre kódból öt gombot (1 … 5) egy ciklusban, amelyek mindegyike a saját számát írja ki egy üzenetben. Mi a buktató a ciklusváltozóval és a lambdával? A lambda a külső változót eléri, de a ciklusváltozó (Megoldás megnyitása
i) egyetlen változó, amely a ciklus végén 6 értékű. A for ciklusnál ezért a lambda mind az 5 gombnál a ciklus végén lévő értéket látná. A megoldás: a ciklusban egy helyi másolat (int szam = i;), amelyet a lambda megjegyez. A foreach ciklusban ez a probléma a modern C#-ban nem áll fenn, de a for-nál igen.for (int i = 1; i <= 5; i++)
{
int szam = i; // helyi másolat minden körben
Button gomb = new Button();
gomb.Text = szam.ToString();
gomb.Location = new Point(20 + (szam - 1) * 60, 20);
gomb.Size = new Size(50, 40);
gomb.Click += (s, ev) => MessageBox.Show("A " + szam + ". gomb");
this.Controls.Add(gomb);
}
Kihívás szint
5. Az esemény kiváltója: a Termék osztály
Írj egy A tulajdonság Termek osztályt Nev és Ar tulajdonsággal, amelynek van egy ArValtozott eseménye. Ha az ár megváltozik, az esemény kiváltódik (a régi és az új árral). Az ablak egy feliraton mutassa a változást.Megoldás megnyitása
set része váltja ki az eseményt, ha az érték ténylegesen változott. Az esemény adatai (régi és új ár) saját EventArgs-ban utaznak.public class ArEventArgs : EventArgs
{
public decimal Regi { get; set; }
public decimal Uj { get; set; }
}
public class Termek
{
private decimal ar;
public string Nev { get; set; } = "";
public decimal Ar
{
get { return ar; }
set
{
if (value == ar)
{
return;
}
decimal regi = ar;
ar = value;
ArValtozott?.Invoke(this, new ArEventArgs { Regi = regi, Uj = ar });
}
}
public event EventHandler<ArEventArgs> ArValtozott;
}
// Form1.cs
private Termek termek = new Termek { Nev = "Kenyér" };
private void Form1_Load(object sender, EventArgs e)
{
termek.ArValtozott += (s, ev) => lblValtozas.Text = $"{termek.Nev}: {ev.Regi} Ft → {ev.Uj} Ft";
}
private void btnBeallit_Click(object sender, EventArgs e)
{
termek.Ar = nudAr.Value;
}
6. Esemény vagy közvetlen hívás?
A Ha a Szamlalo osztály kódjában a Novel() metódus közvetlenül is hívhatná a Form1.UzenetetKiir() metódust az esemény helyett. Miért rossz ötlet? Mit nyersz az eseménnyel?Megoldás megnyitása
Szamlalo közvetlenül hívná a Form1 metódusát, az osztály ismerné az ablakot: nem lehetne újra felhasználni egy másik programban, és a Form1 módosítása a Szamlalo-t is érintené. Az eseménynél a Szamlalo csak jelez, a feliratkozó dönti el, mit tesz. Így az osztály független, tesztelhető és újrahasznosítható. A rétegek szétválasztása (3.9 lecke) ugyanezt a célt szolgálja.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Az esemény egy jelzés arról, hogy valami történt. Az eseménykezelő az a metódus, amely a jelzésre reagál. Egy típus, amelynek értéke egy metódusra mutató hivatkozás. Megadja a metódus szerkezetét, és hívható. A Kétszer fut le egy eseménynél. Tervező és kód együtt gyakran okozza ezt a hibát. Mert ha nincs feliratkozó, az esemény Mert a kibocsátó nem ismeri a feliratkozót: az osztály független és újrahasznosítható marad.1. Mi az esemény, és mi az eseménykezelő?
2. Milyen szerkezetű egy eseménykezelő metódus?
void visszatérés, két paraméter: object sender és EventArgs e (az EventHandler delegált szerkezete).3. Mi a delegált?
4. Mit jelent a += és a -= egy eseménynél?
+= feliratkoztat egy metódust az eseményre, a -= leiratkoztatja.5. Mi történik, ha egy metódusra kétszer iratkozol fel?
6. Miért kell a ?.Invoke az esemény kiváltásakor?
null, és a közvetlen hívás NullReferenceException hibát dobna. A ?. csak akkor hív, ha van feliratkozó.7. Miért jobb esemény, mint egy közvetlen metódushívás a kibocsátóból?
Mit tudsz már?
- Érted az esemény, a kibocsátó, a feliratkozó és az eseménykezelő fogalmát.
- Tudod, mi a delegált, és hogy az
EventHandleregy delegált. - Kódból feliratkozol és leiratkozol (
+=,-=), és használsz lambdát. - Saját eseményt hozol létre és váltasz ki (
event,?.Invoke). - Felismered a gyakori hibákat: duplázott feliratkozás,
nullkiváltás, hibás szerkezet.
Következő lecke: sender, EventArgs és eseményparaméterek
Az eseménykezelő két paraméterét mostanáig alig használtuk. A következő leckében megtudod, mire jó a sender, és milyen információt hordoz az EventArgs.




