Grafikus C# – Windows Forms tanfolyam › 5.3 lecke

Windows Forms · 5.3 lecke · egér

Click, DoubleClick és egérműveletek

Az egér a grafikus programok legfontosabb beviteli eszköze. Egy kattintás mögött azonban több esemény is lezajlik: lenyomás, felengedés, kattintás, dupla kattintás, és mindegyik máskor és másra jó. Ebben a leckében megismered az egéresemények sorrendjét, a Click és a MouseClick, az egyszeri és a dupla kattintás különbségét, a kurzor cseréjét, a jobb gombos menüt, a vezérlők egérrel való húzását és a fájlok ablakra ejtését (Drag and Drop).

  • kb. 110 perc
  • közepes szint
  • egérműveletek
Diák C# programozást gyakorol egy tanulósarokban

A lecke végére

  • ismered az egéresemények sorrendjét, és megkülönbözteted a Click, MouseClick, MouseDown és MouseUp eseményt;
  • tudod, hogy a Button nem kap DoubleClick eseményt, és hogyan kezeld az egyszeri és a dupla kattintás ütközését;
  • használod a MouseEnter, MouseLeave eseményt és a Cursor tulajdonságot;
  • jobb gombos menüt kapcsolsz egy vezérlőhöz (ContextMenuStrip, SourceControl);
  • vezérlőt húzol egérrel, és fájlokat fogadsz az ablakra (AllowDrop, DragEnter, DragDrop);
  • felismered a gyakori hibákat (ugrálás húzáskor, nem működő ejtés, egyszeri és dupla kattintás keveredése).

Mielőtt elkezded

Ehhez a leckéhez az 5.1 és az 5.2 lecke (események, sender, MouseEventArgs) szükséges. Új projektben próbáld ki a példákat.

5.2 sender és EventArgs →

Az egéresemények sorrendje

Egy egyszerű kattintás nem egyetlen esemény. Az alábbi táblázat a leggyakoribb egéreseményeket foglalja össze:

EseményMikor fut?Paraméter
MouseEnterAz egérkurzor belép a vezérlő területére.EventArgs
MouseMoveAz egér mozog a vezérlő felett.MouseEventArgs
MouseDownEgérgombot lenyomtak a vezérlő felett.MouseEventArgs
MouseUpAz egérgombot felengedték.MouseEventArgs
ClickKattintás: lenyomás és felengedés ugyanazon a vezérlőn.EventArgs
MouseClickUgyanaz, mint a Click, de az egér adataival.MouseEventArgs
DoubleClick, MouseDoubleClickDupla kattintás (két gyors egymás utáni kattintás).EventArgs / MouseEventArgs
MouseHoverAz egér egy ideig mozdulatlanul a vezérlő felett áll.EventArgs
MouseLeaveAz egérkurzor elhagyja a vezérlőt.EventArgs
MouseWheelAz egér görgőjét görgetik.MouseEventArgs
Click és MouseClickA Click csak annyit mond: „kattintottak”. A MouseClick megmondja azt is, melyik gombbal és hol (e.Button, e.Location). Ha az egérgombra is szükséged van, a MouseClick (vagy a MouseDown) való.
MouseDown és ClickA MouseDown már a lenyomáskor lefut, a Click csak a felengedés után. Húzáshoz és azonnali reakcióhoz MouseDown, a „végleges” választáshoz Click a jó.
A sorrend vezérlőfüggőAz események sorrendje kisebb eltéréseket mutathat a vezérlő típusától függően. A legbiztosabb, ha kipróbálod (lásd az alábbi kísérletet).
A jobb gombA Button a bal egérgomb Click eseményét adja, a jobb gombra nem: ehhez a MouseDown/MouseUp/MouseClick kell.

Kísérlet: az események sorrendje

Tegyél az ablakra egy lblProba címkét (AutoSize: False, BorderStyle: FixedSingle, méretét nagyítsd meg), egy lstNaplo ListBoxot és egy btnTorol gombot. Az összes eseményt ugyanaz a kód naplózza, így látod a sorrendet:

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
lblProba.MouseEnter += (s, ev) => Naplo("MouseEnter");
lblProba.MouseLeave += (s, ev) => Naplo("MouseLeave");
lblProba.MouseDown += (s, ev) => Naplo("MouseDown (" + ev.Button + ")");
lblProba.MouseUp += (s, ev) => Naplo("MouseUp (" + ev.Button + ")");
lblProba.Click += (s, ev) => Naplo("Click");
lblProba.MouseClick += (s, ev) => Naplo("MouseClick");
lblProba.DoubleClick += (s, ev) => Naplo("DoubleClick");
lblProba.MouseDoubleClick += (s, ev) => Naplo("MouseDoubleClick");
}

private void Naplo(string szoveg)
{
lstNaplo.Items.Add(szoveg);
lstNaplo.TopIndex = lstNaplo.Items.Count - 1; // az utolsó sor látszik
}

private void btnTorol_Click(object sender, EventArgs e)
{
lstNaplo.Items.Clear();
}

Jósolj!

A címke fölé viszed az egeret, rákattintasz egyszer a bal gombbal, majd elviszed az egeret. Milyen sorrendben jelennek meg az események a naplóban? Mi változik, ha a jobb gombbal kattintasz?

Jóslat ellenőrzése

Bal gombbal: MouseEnter, MouseDown (Left), MouseUp (Left), Click, MouseClick, MouseLeave. A lenyomás megelőzi a felengedést, a Click a felengedés után fut. Jobb gombbal a MouseDown és a MouseUp Right értékkel fut, a MouseClick is lefut, a Click esemény viszont egyes vezérlőknél csak a bal gombra reagál. Mivel a pontos sorrend a vezérlőtől is függhet, a saját környezetedben mindig érdemes kipróbálni.

Egyszeri és dupla kattintás

A dupla kattintás két gyors egymás utáni kattintás (az időkeretet a Windows egérbeállítása adja meg, ez a SystemInformation.DoubleClickTime, általában 500 ezredmásodperc). A dupla kattintásnál azonban az első kattintás egyszerű kattintásként is lefut, ezért egy dupla kattintás eseményei:

  1. MouseDown, MouseUp, Click, MouseClick (az első kattintás),
  2. MouseDown, DoubleClick, MouseDoubleClick, MouseUp (a második kattintás).

A Button nem kap DoubleClick eseményt

A Button vezérlő a gyors egymás utáni két kattintást két egyszeri kattintásként értelmezi: a Click kétszer fut le, a DoubleClick soha. Ha dupla kattintásra kell reagálni, használj Label, Panel, PictureBox, ListBox vagy DataGridView vezérlőt. A Button-nál a dupla kattintás felismeréséhez az időt kellene mérni (lásd a Gyakorlótárat).

Az egyszeri és a dupla kattintás ütközése

Ha egy vezérlőnek mind a Click, mind a DoubleClick eseményére reagálsz, a dupla kattintásnál mindkét művelet lefut: előbb az egyszeri kattintásé, utána a dupláé. Példa: egy fájllistában az egyszeri kattintás kijelöl, a dupla megnyit. Ha az egyszeri kattintás kezelője hosszú vagy zavaró műveletet végez, az a dupla kattintásnál is lefut.

Form1.csC#
// Jó terv: az egyszeri kattintás csak kijelöl (gyors, ártalmatlan),
// a dupla kattintás megnyit (a kijelölés már megtörtént)
private void lstFajlok_SelectedIndexChanged(object sender, EventArgs e)
{
lblKijelolt.Text = "Kijelölt: " + lstFajlok.Text;
}

private void lstFajlok_DoubleClick(object sender, EventArgs e)
{
if (lstFajlok.SelectedItem != null)
{
MessageBox.Show("Megnyitás: " + lstFajlok.SelectedItem);
}
}

Jósolj!

Egy Label Click eseménye egy számlálót növel, a DoubleClick eseménye is. Hányszor nő a számláló egy dupla kattintásra, ha mindkét esemény eggyel növeli?

Jóslat ellenőrzése

Háromszor. Az első kattintás Click eseménye (+1), a második kattintás is Click eseményt vált ki (+1), majd a DoubleClick (+1). A Windows a második kattintást egyszeri kattintásként és dupla kattintásként is jelzi. Ezért a Click és a DoubleClick ugyanarra az objektumra ritkán jó együtt: vagy csak az egyiket használd, vagy az egyszeri kattintás legyen olyan, amely nem zavar a dupla kattintás mellett (például kijelölés).

Kurzor, belépés és kilépés, jobb gombos menü

MouseEnter, MouseLeave és a Cursor

A MouseEnter és a MouseLeave eseményekkel a vezérlő „reagál az egérre” (kiemelés, súgó). A Cursor tulajdonság az egérkurzor alakját adja a vezérlő fölött:

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
lblLink.Cursor = Cursors.Hand; // kéz alakú kurzor, mint a hivatkozásoknál
}

private void lblLink_MouseEnter(object sender, EventArgs e)
{
lblLink.ForeColor = Color.RoyalBlue;
lblLink.Font = new Font(lblLink.Font, FontStyle.Underline);
}

private void lblLink_MouseLeave(object sender, EventArgs e)
{
lblLink.ForeColor = Color.Black;
lblLink.Font = new Font(lblLink.Font, FontStyle.Regular);
}
Cursors.Hand, IBeam, WaitCursor, SizeAllA kurzor alakja: kéz, szövegbeviteli kurzor, várakozás (homokóra), mozgatás. A Cursors felsorolás a beépített alakokat adja.
Cursor = Cursors.WaitCursorHosszabb művelet előtt állítsd be, utána vissza Cursors.Default értékre (érdemes try–finally blokkban).
new Font(lblLink.Font, FontStyle.Underline)Új betűtípus a régi alapján, más stílussal (2.4 lecke).
MouseHoverAkkor fut le, ha az egér egy ideig (SystemInformation.MouseHoverTime) mozdulatlanul áll. Súgóbuborékhoz inkább a ToolTip komponens való (1.11 lecke).

A jobb gombos menü

A jobb egérgomb menüje (ContextMenuStrip) a Toolbox Menus & Toolbars csoportjából kerül az ablakra (cmsKartya). Egy vezérlőhöz a ContextMenuStrip tulajdonságával rendeled hozzá, és a jobb kattintásra magától megnyílik. A menüpont eseménykezelője a SourceControl tulajdonságból tudja, melyik vezérlőn nyitották meg a menüt:

Form1.csC#
// a tervezőben: lblKartya.ContextMenuStrip = cmsKartya;
// a menüpontok: miSzinez (Színezés), miTorles (Törlés)

private void miTorles_Click(object sender, EventArgs e)
{
Control forras = cmsKartya.SourceControl;

if (forras != null)
{
forras.Visible = false;
}
}

private void miSzinez_Click(object sender, EventArgs e)
{
Control forras = cmsKartya.SourceControl;

if (forras != null)
{
forras.BackColor = Color.LightSalmon;
}
}

Jósolj!

Három címke is ugyanazt a cmsKartya menüt használja. A menü Törlés menüpontjának kezelőjében nem a SourceControl, hanem a sender paraméter alapján próbálod megtudni, melyik címkén nyitották meg a menüt. Mi lesz a sender?

Jóslat ellenőrzése

A sender a megkattintott menüpont (ToolStripMenuItem), nem a címke. A menüpont nem tudja, melyik címkéhez tartozik a menü, ezért a ContextMenuStrip.SourceControl tulajdonságot kell használni. Ez azt a vezérlőt adja, amelyen a menüt megnyitották.

Húzás és ejtés

Vezérlő húzása egérrel

A vezérlő mozgatásához három esemény kell: a MouseDown megjegyzi, hol fogtad meg, a MouseMove mozgatja, a MouseUp elengedi. A lényeg a fogási pont eltolása (offset): ha a vezérlőt a közepén fogod meg, nem ugorhat a bal felső sarkával az egérhez:

Form1.cs (három címke: lblKartya1, lblKartya2, lblKartya3)C#
private Label mozgatott = null;   // éppen húzott kártya
private Point fogasPont; // hol fogtuk meg (a kártyához képest)

private void Kartya_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}

mozgatott = (Label)sender;
fogasPont = e.Location;
mozgatott.BringToFront();
mozgatott.Cursor = Cursors.SizeAll;
}

private void Kartya_MouseMove(object sender, MouseEventArgs e)
{
if (mozgatott == null || mozgatott != sender)
{
return;
}

// az egér helye az ablakhoz képest, mínusz a fogási pont
Point egerAblakban = this.PointToClient(Cursor.Position);
mozgatott.Location = new Point(egerAblakban.X - fogasPont.X, egerAblakban.Y - fogasPont.Y);
}

private void Kartya_MouseUp(object sender, MouseEventArgs e)
{
if (mozgatott != null)
{
mozgatott.Cursor = Cursors.Hand;
mozgatott = null;
}
}
fogasPont = e.LocationAz egér helye a kártya saját koordinátarendszerében a lenyomás pillanatában.
PointToClient(Cursor.Position)Az egér helye az ablakhoz képest. Ebből kivonva a fogási pontot adódik a kártya új bal felső sarka.
Egér rögzítéseAmíg az egérgomb le van nyomva, a vezérlő „elkapja” az egeret, ezért a MouseMove akkor is megérkezik, ha az egér kimegy a kártyáról.
mozgatott != senderA közös kezelő mind a három kártyánál lefut, de csak a megfogottal foglalkozunk.

Fájl ejtése az ablakra (Drag and Drop)

A Windowsban a fájlt a Fájlkezelőből az ablakra húzhatod. Ehhez az ablaknak (vagy a vezérlőnek) engedélyeznie kell az ejtést, és két eseményt kell kezelnie: a DragEnter eldönti, elfogadja-e a húzott adatot, a DragDrop feldolgozza:

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
this.AllowDrop = true; // enélkül az ablak nem fogad el semmit
}

private void Form1_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Copy; // az egérkurzor jelzi: elfogadjuk
}
else
{
e.Effect = DragDropEffects.None; // tiltó kurzor
}
}

private void Form1_DragDrop(object sender, DragEventArgs e)
{
string[] fajlok = (string[])e.Data.GetData(DataFormats.FileDrop);

foreach (string fajl in fajlok)
{
lstFajlok.Items.Add(System.IO.Path.GetFileName(fajl));
}
}
AllowDrop = trueEngedélyezi a vezérlőnek az ejtést. A Form és a vezérlők (például a RichTextBox, ListBox) külön tulajdonsága.
DragEnterAkkor fut, amikor a húzott adat belép a vezérlő fölé. Itt döntjük el, milyen adatot fogadunk el (GetDataPresent) és milyen hatást jelzünk (e.Effect).
DragDropAz egérgomb felengedésekor fut: itt dolgozzuk fel az adatot. A fájlok útvonalait egy string tömb adja (DataFormats.FileDrop).
FelhasználásA MiniWordnél (2.10 lecke) így nyithatsz meg egy RTF fájlt úgy, hogy az ablakra húzod. Az ejtett fájlt mindig ellenőrizd (kiterjesztés, létezik-e), és védd try–catch blokkal.

Jósolj!

Az AllowDrop tulajdonságot nem állítod True értékre, de megírod a DragEnter és DragDrop kezelőt, és hozzákötöd. Mi történik, ha egy fájlt az ablakra húzol?

Jóslat ellenőrzése

A tiltó kurzor jelenik meg (áthúzott kör), és egyik esemény sem fut le. Az AllowDrop hiánya a leggyakoribb oka annak, hogy az ejtés nem működik. Ha a kurzor engedélyező (plusz jel), de a DragDrop mégsem fut, valószínűleg a DragEnter kezelő nem állította be az e.Effect értékét.

📷 Képernyőkép helye

Mit tegyél ide: egy felvétel az ablakról, amelyen egy fájlt húznak a lista fölé (a plusz jelű kurzorral), mellette a már ejtett fájlok névsora.Javasolt fájlnév: winforms-5-3-ejtes.webp · Alt szöveg: Fájl ejtése az ablakra

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 gomb DoubleClick eseménye soha nem fut

Egy Button DoubleClick kezelője sosem hívódik meg.

Ok és javítás

A Button a két gyors kattintást két Click eseményként kezeli. Használj Label, Panel vagy PictureBox vezérlőt, vagy mérd az időt két Click között (SystemInformation.DoubleClickTime).

2. Dupla kattintásnál az egyszeri művelet is lefut

Egy listaelem dupla kattintására megnyílik egy ablak, de előtte az egyszeri kattintás is felugrik egy üzenetet.

Ok és javítás

A dupla kattintás első kattintása egyszerű Click is. Az egyszeri kattintáshoz csak ártalmatlan műveletet (kijelölés, felirat) rendelj, és a megnyitás/felugró ablak a DoubleClick eseményben legyen.

3. A húzott vezérlő ugrik

mozgatott.Location = e.Location;   // a vezérlő bal felső sarka az egérhez ugrik
Hiba és javítás

Az e.Location a vezérlőhöz képest adja az egér helyét, a Location az ablakhoz képest kell. Számold át az egér helyét az ablakhoz képest (PointToClient(Cursor.Position)), és vond ki a fogási pontot (a MouseDown-nál megjegyzett e.Location).

4. Az ejtés nem működik

A fájl húzásakor tiltó kurzor jelenik meg, és a DragDrop esemény nem fut le.

Ok és javítás

Ellenőrizd: (1) az AllowDrop igaz-e azon a vezérlőn, amelyre ejtesz (a Form és a rajta lévő vezérlő külön tulajdonság); (2) a DragEnter kezelő be van-e kötve, és beállítja-e az e.Effect értékét; (3) a felvett eseménykezelő szerkezete helyes-e (DragEventArgs).

5. Az egér gombja nélkül is mozog a kártya

private void Kartya_MouseMove(object sender, MouseEventArgs e)
{
((Label)sender).Left = e.X; // minden egérmozgásra mozog
}
Hiba és javítás

A MouseMove minden egérmozgásnál lefut, nemcsak lenyomott gombnál. Használj mozgatott jelzőt (vagy objektumot), amelyet a MouseDown állít be, és a MouseUp töröl. A mozgatást csak akkor végezd, ha a jelző igaz.

6. A menüpont rossz vezérlőt érint

A közös helyi menü Törlés pontja mindig az első kártyát törli.

Ok és javítás

Valószínűleg konkrét kártyára (lblKartya1) hivatkoztál a kezelőben. Használd a cmsKartya.SourceControl tulajdonságot, amely azt a vezérlőt adja, amelyen a menüt megnyitották.

Mini projekt: mozgatható kártyák

Készíts programot, amelyben az egér több módon is használható:

  1. Három Label kártya (lblKartya1–lblKartya3, AutoSize: False, BorderStyle: FixedSingle, TextAlign: MiddleCenter, különböző szövegek és színek), kéz alakú kurzorral.
  2. A kártyák bal gombbal húzhatók, a megfogott kártya előre kerül (BringToFront), és a húzás alatt SizeAll kurzor látszik.
  3. A kártya dupla kattintásra színt vált (két szín között váltakozik).
  4. A jobb gombos menün (cmsKartya) két pont: Piros (a kártya háttere piros lesz) és Elrejtés (a kártya láthatatlan lesz). A menüpontok a SourceControl alapján működnek.
  5. Egy btnVisszaallit gomb a rejtett kártyákat visszaállítja és elrendezi a kiindulási helyükre.
  6. Az ablakra ejtett fájlok nevét egy lstFajlok mutatja (csak fájlok; mappát ne fogadjon el).
  7. Egy lblInfo felirat az egér ablakbeli helyét mutatja (MouseMove az ablakon).
  8. Teszteld: a húzás gyors egérmozgással, a kártyák egymáson, dupla kattintás, mindkét menüpont, fájl és mappa ejtése.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A három kártya mind ugyanazokat a közös kezelőket használja (sender alapján). A kezdeti helyeket a Form1_Load eltárolja, hogy a gomb visszaállíthassa őket. A mappa ejtését a System.IO.Directory.Exists szűri ki.

Form1.csC#
private Label mozgatott = null;
private Point fogasPont;
private Point[] kezdoHelyek = new Point[3];
private Label[] kartyak;

private void Form1_Load(object sender, EventArgs e)
{
kartyak = new Label[] { lblKartya1, lblKartya2, lblKartya3 };

for (int i = 0; i < kartyak.Length; i++)
{
kezdoHelyek[i] = kartyak[i].Location;
kartyak[i].Cursor = Cursors.Hand;
}

this.AllowDrop = true;
}

private void Kartya_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}

mozgatott = (Label)sender;
fogasPont = e.Location;
mozgatott.BringToFront();
mozgatott.Cursor = Cursors.SizeAll;
}

private void Kartya_MouseMove(object sender, MouseEventArgs e)
{
if (mozgatott == null || mozgatott != sender)
{
return;
}

Point egerAblakban = this.PointToClient(Cursor.Position);
mozgatott.Location = new Point(egerAblakban.X - fogasPont.X, egerAblakban.Y - fogasPont.Y);
}

private void Kartya_MouseUp(object sender, MouseEventArgs e)
{
if (mozgatott != null)
{
mozgatott.Cursor = Cursors.Hand;
mozgatott = null;
}
}

private void Kartya_DoubleClick(object sender, EventArgs e)
{
Label kartya = (Label)sender;
kartya.BackColor = kartya.BackColor == Color.LightSkyBlue ? Color.Khaki : Color.LightSkyBlue;
}

private void miPiros_Click(object sender, EventArgs e)
{
Control forras = cmsKartya.SourceControl;

if (forras != null)
{
forras.BackColor = Color.IndianRed;
}
}

private void miElrejtes_Click(object sender, EventArgs e)
{
Control forras = cmsKartya.SourceControl;

if (forras != null)
{
forras.Visible = false;
}
}

private void btnVisszaallit_Click(object sender, EventArgs e)
{
for (int i = 0; i < kartyak.Length; i++)
{
kartyak[i].Visible = true;
kartyak[i].Location = kezdoHelyek[i];
}
}

private void Form1_MouseMove(object sender, MouseEventArgs e)
{
lblInfo.Text = $"Egér: {e.X}, {e.Y}";
}

private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
}

private void Form1_DragDrop(object sender, DragEventArgs e)
{
string[] elemek = (string[])e.Data.GetData(DataFormats.FileDrop);

foreach (string elem in elemek)
{
if (System.IO.Directory.Exists(elem))
{
continue; // mappát nem fogadunk el
}

lstFajlok.Items.Add(System.IO.Path.GetFileName(elem));
}
}
📷 Képernyőkép helye

Mit tegyél ide: a kész program három, egymást részben takaró kártyával, az egyik kártya húzás közben, mellette a jobb oldali lista néhány ejtett fájlnévvel.Javasolt fájlnév: winforms-5-3-kesz.webp · Alt szöveg: A mozgatható kártyák programja

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. Eseménysorrend-naplózó a Buttonon

Ismételd meg a leckebeli kísérletet egy Button vezérlőn. Mit tapasztalsz dupla kattintáskor? Mi a különbség a Label eredményéhez képest?

Egy lehetséges megoldás

A Button esetében dupla kattintáskor két Click (és MouseClick) esemény lefut, de a DoubleClick és a MouseDoubleClick nem. A Label a második kattintásnál DoubleClick és MouseDoubleClick eseményt is jelez. A kísérlet kódja azonos, csak a vezérlő neve változik.

2. Fájl ejtése a szövegmezőbe

A rtbSzoveg RichTextBoxra ejtett TXT fájl tartalma töltődjön be a szövegmezőbe. Más kiterjesztésű fájlnál üzenet jelenjen meg.

Egy lehetséges megoldás

A RichTextBox AllowDrop tulajdonságát True értékre kell állítani (a vezérlőé, nem csak az ablaké). A DragDrop kezelőben a kiterjesztést a Path.GetExtension adja, a tartalmat a File.ReadAllText. A hibákat try–catch kezeli.

Form1.csC#
private void rtbSzoveg_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
}

private void rtbSzoveg_DragDrop(object sender, DragEventArgs e)
{
string[] fajlok = (string[])e.Data.GetData(DataFormats.FileDrop);
string fajl = fajlok[0];

if (System.IO.Path.GetExtension(fajl).ToLower() != ".txt")
{
MessageBox.Show("Csak TXT fájlt lehet ejteni.");
return;
}

try
{
rtbSzoveg.Text = System.IO.File.ReadAllText(fajl);
}
catch (Exception ex)
{
MessageBox.Show("A fájl nem olvasható: " + ex.Message);
}
}

3. Kattintásszámláló gombonként

Egy pnlTer panelen a bal gomb a lblBal, a jobb gomb a lblJobb, a középső a lblKozep számlálóját növelje. A dupla kattintás a bal számlálót egyszerre csak eggyel növelje (ne háromszor).

Egy lehetséges megoldás

A MouseClick eseményt használjuk, amelynek e.Button megmondja a gombot, és a MouseDoubleClick nélkül dolgozunk. A második, dupla kattintás is MouseClick esemény, ezért a bal számláló dupla kattintásnál kettővel nő, ami természetes (két kattintás volt). Az e.Clicks értéke 2 a második kattintásnál, így azt is megkülönböztetheted.

Form1.csC#
private int bal = 0;
private int jobb = 0;
private int kozep = 0;

private void pnlTer_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
bal = bal + 1;
lblBal.Text = "Bal: " + bal;
}
else if (e.Button == MouseButtons.Right)
{
jobb = jobb + 1;
lblJobb.Text = "Jobb: " + jobb;
}
else if (e.Button == MouseButtons.Middle)
{
kozep = kozep + 1;
lblKozep.Text = "Közép: " + kozep;
}
}

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. Kéz alakú kurzor

Egy pbLogo PictureBox fölött az egérkurzor legyen kéz alakú, és a kattintásra nyíljon meg a böngésző egy címmel. Mit kell az URL megnyitásához tenni?

Megoldás megnyitása

A Cursor tulajdonság egyszer állítható a tervezőben. A böngésző megnyitásához a Process.Start kell, a mai .NET-ben a UseShellExecute bekapcsolásával (a .NET Frameworkben az egyszerű forma elég). A címet csak megbízható forrásból, a programba írva használd.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
pbLogo.Cursor = Cursors.Hand;
}

private void pbLogo_Click(object sender, EventArgs e)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = "https://www.digitalisfelfedezok.hu",
UseShellExecute = true
});
}

2. Kiemelés egérrel

Három Label (lblA, lblB, lblC) háttere sárga legyen, amíg az egér fölötte van, és álljon vissza, ha elhagyja. Hogyan oldod meg három külön kód nélkül?

Megoldás megnyitása

Közös MouseEnter és MouseLeave kezelő, a sender alapján. Az eredeti színt a Tag tulajdonságban jegyezzük meg (az 1.4 lecke óta ismert eszköz), hogy minden címke a saját színére álljon vissza.

Form1.csC#
private void Cimke_MouseEnter(object sender, EventArgs e)
{
Label cimke = (Label)sender;
cimke.Tag = cimke.BackColor; // az eredeti szín megjegyzése
cimke.BackColor = Color.Yellow;
}

private void Cimke_MouseLeave(object sender, EventArgs e)
{
Label cimke = (Label)sender;

if (cimke.Tag is Color eredeti)
{
cimke.BackColor = eredeti;
}
}

Közepes szint

3. Egyszeri és dupla kattintás megkülönböztetése

Egy pnlGomb panelen az egyszeri kattintás a Egy, a dupla a Kettő szöveget írja a feliratra, és dupla kattintásnál az Egy ne jelenjen meg. Hogyan oldod meg?

Megoldás megnyitása

Az egyszeri kattintás kezelése késleltetve történik: egy Timer a dupla kattintás idejéig vár (SystemInformation.DoubleClickTime). Ha közben jön a második kattintás (DoubleClick), a Timer-t leállítjuk, és a Kettő felirat jelenik meg. Ha a Timer lejár, az egyszeri kattintás érvényes.

Form1.cs (tmrKattintas: Timer)C#
private void Form1_Load(object sender, EventArgs e)
{
tmrKattintas.Interval = SystemInformation.DoubleClickTime;
}

private void pnlGomb_Click(object sender, EventArgs e)
{
tmrKattintas.Stop();
tmrKattintas.Start(); // az egyszeri kattintás eredménye késik
}

private void tmrKattintas_Tick(object sender, EventArgs e)
{
tmrKattintas.Stop();
lblEredmeny.Text = "Egy";
}

private void pnlGomb_DoubleClick(object sender, EventArgs e)
{
tmrKattintas.Stop();
lblEredmeny.Text = "Kettő";
}

4. Keret nélküli ablak mozgatása

Egy keret nélküli ablakot (FormBorderStyle: None) a pnlCim címsáv panelről lehessen húzással mozgatni. Hogyan számolod az ablak új helyét?

Megoldás megnyitása

A képernyő-koordinátákkal dolgozunk (Cursor.Position): az egér elmozdulását a lenyomáskori helyhez képest hozzáadjuk az ablak kezdeti helyéhez. Ha az ablak mozog, az ablakhoz viszonyított koordináták (e.Location) is változnának, ezért a képernyő-koordináta a megbízható.

Form1.csC#
private bool mozog = false;
private Point kezdoEger;
private Point kezdoAblak;

private void pnlCim_MouseDown(object sender, MouseEventArgs e)
{
mozog = true;
kezdoEger = Cursor.Position;
kezdoAblak = this.Location;
}

private void pnlCim_MouseMove(object sender, MouseEventArgs e)
{
if (!mozog)
{
return;
}

this.Location = new Point(
kezdoAblak.X + Cursor.Position.X - kezdoEger.X,
kezdoAblak.Y + Cursor.Position.Y - kezdoEger.Y);
}

private void pnlCim_MouseUp(object sender, MouseEventArgs e)
{
mozog = false;
}

Kihívás szint

5. Elemek húzása két lista között

Két ListBox (lstBal, lstJobb) között húzással lehessen elemet áthelyezni. Mit kell beállítanod, és melyik eseményben indítod a húzást?

Megoldás megnyitása

A húzás indítása a DoDragDrop metódussal történik a MouseDown eseményben (a kijelölt elemmel). Mindkét lista AllowDrop tulajdonsága igaz. A DragEnter engedélyezi, a DragDrop felveszi az elemet, és a forráslistából törli. A forrást a DragEventArgs adatából vagy egy mezőből tudjuk meg.

Form1.csC#
private ListBox forras = null;

private void Lista_MouseDown(object sender, MouseEventArgs e)
{
ListBox lista = (ListBox)sender;
int index = lista.IndexFromPoint(e.Location);

if (index == ListBox.NoMatches)
{
return;
}

forras = lista;
lista.DoDragDrop(lista.Items[index].ToString(), DragDropEffects.Move);
}

private void Lista_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.Data.GetDataPresent(DataFormats.Text) ? DragDropEffects.Move : DragDropEffects.None;
}

private void Lista_DragDrop(object sender, DragEventArgs e)
{
ListBox cel = (ListBox)sender;

if (cel == forras)
{
return;
}

string elem = (string)e.Data.GetData(DataFormats.Text);
cel.Items.Add(elem);
forras.Items.Remove(elem);
}

6. Nagyítás Ctrl + görgetéssel

Egy rtbSzoveg RichTextBoxban a Ctrl + egérgörgő a szöveg nagyítását (a ZoomFactor) változtassa 0,5 és 3 között. Hogyan tudod meg, hogy a Ctrl le van-e nyomva egér esemény közben?

Megoldás megnyitása

Az egéreseményben az e nem tartalmazza a módosítókat, ezért a Control.ModifierKeys statikus tulajdonságot kérdezzük le. A RichTextBox a Ctrl+görgetést alapból is képes nagyításként kezelni, ezért a megoldást érdemes kipróbálni, és ha az alapműködés és a saját kód ütközik (kétszeres nagyítás), a ZoomFactor saját kezelését a RichTextBox helyett egy másik vezérlőn próbáld ki.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
rtbSzoveg.MouseWheel += rtbSzoveg_MouseWheel;
}

private void rtbSzoveg_MouseWheel(object sender, MouseEventArgs e)
{
if (Control.ModifierKeys != Keys.Control)
{
return;
}

float uj = rtbSzoveg.ZoomFactor + (e.Delta > 0 ? 0.1f : -0.1f);
rtbSzoveg.ZoomFactor = Math.Max(0.5f, Math.Min(3f, uj));
}

Gyors önellenőrzés

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

1. Mi az egéresemények sorrendje egy kattintásnál?

MouseEnter (belépéskor), MouseDown, MouseUp, Click, MouseClick, majd MouseLeave (kilépéskor). A pontos sorrendet a vezérlő is befolyásolhatja, ezért érdemes kipróbálni.

2. Mi a különbség a Click és a MouseClick között?

A Click EventArgs-ot kap (nincs gomb- és helyadat), a MouseClick MouseEventArgs-ot (gomb, hely).

3. Miért nem működik a Button DoubleClick eseménye?

A Button a gyors kattintásokat egyszeri kattintásokként (két Click) kezeli. Dupla kattintáshoz Label, Panel, PictureBox vagy lista való.

4. Mi történik dupla kattintáskor a Click eseménnyel?

Az első kattintás után a Click lefut, és a második kattintás is jelezhet Click eseményt, majd jön a DoubleClick. Az egyszeri kattintáshoz ezért ártalmatlan műveletet rendelj.

5. Hogyan húzol egy vezérlőt úgy, hogy ne ugorjon?

A MouseDown-nál megjegyzed a fogási pontot (e.Location), mozgatáskor az egér ablakbeli helyéből kivonod, és úgy állítod be a Location értéket.

6. Mi kell ahhoz, hogy egy fájlt az ablakra lehessen ejteni?

Az AllowDrop = true, a DragEnter kezelő (e.Effect beállítása) és a DragDrop kezelő (a DataFormats.FileDrop adatból).

7. Hogyan derül ki a helyi menü eseményében, melyik vezérlőn nyitották meg?

A ContextMenuStrip.SourceControl tulajdonságból. A sender a menüpont, nem a vezérlő.

Mit tudsz már?

  • Ismered az egéresemények sorrendjét és a Click / MouseClick / DoubleClick különbségét.
  • Tudod, hogyan kezeld az egyszeri és a dupla kattintás ütközését.
  • Kurzort cserélsz, és jobb gombos menüt használsz a SourceControl segítségével.
  • Vezérlőt húzol egérrel, és fájlokat fogadsz az ablakra.
  • Felismered a gyakori egérkezelési hibákat.

Következő lecke: KeyDown, KeyPress, KeyUp és gyorsbillentyűk

Az egér után a billentyűzet következik: a három billentyűesemény különbsége, a gyorsbillentyűk és a menük billentyűkombinációi.