Grafikus C# – Windows Forms tanfolyam › 7.2 lecke Windows Forms · 7.2 lecke · helyi menü A jobb egérgombbal előhívott helyi menü (context menu) a kattintott dologra vonatkozó parancsokat kínálja: egy fájlon Átnevezés, egy szövegen Másolás, egy feladaton Kész. Jól megírva gyorsítja a munkát, rosszul megírva (rossz elemre hat, mindig ugyanazt mutatja) félrevezet. Ebben a leckében megtanulod a ContextMenuStrip és helyi menü
ContextMenuStrip használatát: hozzárendelést, a menü előzetes igazítását (Opening), a kattintás helyének azonosítását (SourceControl, IndexFromPoint), a dinamikus elemeket, és egy közös menüt több szövegmezőhöz.
A lecke végére
- a
ContextMenuStripvezérlőt hozzárendeled vezérlőkhöz, és tudod, mikor jelenik meg; - az
Openingeseményben igazítod a menüt a helyzethez, vagy megakadályozod a megnyitást (e.Cancel); - a
SourceControlsegítségével egy menüt több vezérlőhöz használsz; - a lista elemét a jobb kattintás helyén jelölöd ki (
IndexFromPoint); - dinamikus menüt építesz (állapottól függő feliratok, kapcsolók, almenü);
- tudod, hogy a helyi menü sosem lehet az egyetlen út egy funkcióhoz.
Mielőtt elkezded
Ehhez a leckéhez a 7.1 lecke (menüelemek, ToolStripMenuItem), az 5.3 lecke (egéresemények, MouseDown, SourceControl) és az 5.8 lecke (közös kezelő, Tag) szükséges.
Hozzárendelés és megjelenés
A ContextMenuStrip egy komponens (a tervező alsó sávjában jelenik meg), amelynek elemei ugyanolyan ToolStripItem elemek, mint a MenuStripéi (7.1 lecke). Egy vezérlőhöz a ContextMenuStrip tulajdonsággal rendeled hozzá. A menü ezután megjelenik a jobb egérgomb felengedésekor, illetve a billentyűzeten a Menu billentyűre vagy a Shift+F10 kombinációra, külön kód nélkül:
// a tervezőben: a vezérlő Properties → ContextMenuStrip → a menü kiválasztása
// kódból:
lstFeladatok.ContextMenuStrip = cmsFeladat;
txtUj.ContextMenuStrip = cmsSzoveg;
// kódból megjeleníthető tetszőleges helyen is (például egy gombnyomásra):
cmsFeladat.Show(btnTobb, new Point(0, btnTobb.Height)); // a gomb alatt
cmsFeladat.Show(Cursor.Position); // az egérmutatónál (képernyő-koordináta)TextBox és a RichTextBox saját, beépített helyi menüt kínál (Visszavonás, Kivágás, Másolás…). Ha hozzárendelsz egy ContextMenuStripet, az helyettesíti a beépítettet.MouseDown még a menü megnyitása előtt lefut (az elem kijelölésére ezt használjuk).Show(vezérlő, pont) a vezérlőhöz viszonyított, a Show(pont) a képernyő-koordinátában megadott helyen jeleníti meg. Ilyenkor a SourceControl a megadott vezérlő.Jósolj!
Egy Panel ContextMenuStrip értéke a cmsPanel, a panelen egy Label van (saját helyi menü nélkül). Jobb gombbal a címkére kattintasz. Megjelenik-e a cmsPanel?
Jóslat ellenőrzése
A helyi menü nem öröklődik: a ContextMenuStrip mindig az adott vezérlő saját tulajdonsága, ezért a címkén jobb kattintásra a cmsPanel nem jelenik meg (csak ott, ahol a panel maga az egérpont alatt van). Ha a címkén is szeretnéd, a menüt hozzá is rendeld (egy menü több vezérlőhöz is hozzárendelhető). A viselkedést a saját programodban érdemes kipróbálni.
Opening és SourceControl
Az Opening esemény közvetlenül a menü megjelenítése előtt fut. Itt igazíthatod a menüt a helyzethez, vagy megakadályozhatod a megnyitását az e.Cancel = true beállítással. A SourceControl tulajdonság megmondja, melyik vezérlő hívta elő a menüt, ezért egy menü több vezérlőhöz is használható:
private void cmsSzoveg_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
TextBoxBase mezo = cmsSzoveg.SourceControl as TextBoxBase;
if (mezo == null)
{
e.Cancel = true; // nem szövegmezőből hívták: nincs mit mutatni
return;
}
bool vanKijeloles = mezo.SelectionLength > 0;
miVisszavon.Enabled = mezo.CanUndo;
miKivag.Enabled = vanKijeloles && !mezo.ReadOnly;
miMasol.Enabled = vanKijeloles;
miBeillesztes.Enabled = Clipboard.ContainsText() && !mezo.ReadOnly;
miMindent.Enabled = mezo.TextLength > 0;
}
// ugyanaz a kezelő mind az öt menüponton: a Tag a művelet neve
private void SzovegMuvelet_Click(object sender, EventArgs e)
{
ToolStripMenuItem pont = (ToolStripMenuItem)sender;
ContextMenuStrip menu = (ContextMenuStrip)pont.Owner;
TextBoxBase mezo = menu.SourceControl as TextBoxBase;
if (mezo == null)
{
return;
}
switch (pont.Tag.ToString())
{
case "visszavon": mezo.Undo(); break;
case "kivag": mezo.Cut(); break;
case "masol": mezo.Copy(); break;
case "beilleszt": mezo.Paste(); break;
case "mindent": mezo.SelectAll(); break;
}
}TextBox és a RichTextBox közös őse: ugyanaz a kód mindkettőn működik (Cut, Copy, Paste, Undo, SelectAll, ReadOnly).ToolStrip, amelyben áll; a ContextMenuStrip-re átalakítva elérhető a SourceControl. (Almenü pontjánál az Owner a közvetlen almenü; a gyökérhez a OwnerItem láncon át jutsz.)Opening kezelőben Cancel igaz, a menü meg sem nyílik. Hasznos, ha nincs mit mutatni (üres területen kattintás).Jósolj!
Két szövegmező (txtA és txtB) osztozik egy cmsSzoveg menün. Az Mindent kijelöl kezelő txtA.SelectAll() sort tartalmaz (fix névvel). Mi történik, ha a txtB-n nyitod a menüt, és azt választod?
Jóslat ellenőrzése
A txtA szövege jelölődik ki, nem a txtB-é: a közös menü kezelője a fix névre hivatkozik, nem arra a vezérlőre, amelyen a menüt előhívták. A közös menü kezelője mindig a SourceControl értékét használja.
A kattintás helyén lévő elem kijelölése
A ListBox és más listák jobb kattintásra nem jelölik ki az elemet. Ha a menü az aktuálisan kijelölt elemre hat, a felhasználó egy másik elemre kattint jobb gombbal, és a menü az előzőn dolgozik: ez a leggyakoribb helyi menü hiba. A megoldás: a MouseDown eseményben a kattintás helye alatti elemet kijelöljük (a menü csak a jobb gomb felengedésekor nyílik meg, tehát ez még előtte fut):
private void lstFeladatok_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
int index = lstFeladatok.IndexFromPoint(e.Location); // -1 ('ListBox.NoMatches'), ha nincs elem a helyen
lstFeladatok.SelectedIndex = index; // a -1 megszünteti a kijelölést
}
}
private void cmsFeladat_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
bool vanElem = lstFeladatok.SelectedIndex >= 0;
// üres helyen csak az „Új” parancs értelmes
miSzerkeszt.Visible = vanElem;
miKesz.Visible = vanElem;
miPrioritas.Visible = vanElem;
miTorol.Visible = vanElem;
sepElem.Visible = vanElem;
}ListBox.NoMatches (-1) értéket, ha ott nincs elem (üres terület).TreeView GetNodeAt(pont), a DataGridView HitTest vagy CellMouseDown, a ListView GetItemAt(x, y) metódusa adja a kattintott elemet.Visible = false); ami értelmezhető, de most nem végezhető el, azt letiltani (Enabled = false), hogy a felhasználó lássa, létezik a funkció.SelectedIndex a kiindulópont.Jósolj!
A lista három elemből áll, a 3. van kijelölve. A felhasználó a 2. elemre kattint jobb gombbal, és a menüből a Törlés parancsot választja. A MouseDown kezelő nincs megírva. Melyik elem törlődik?
Jóslat ellenőrzése
A 3. elem (a kijelölt): a jobb kattintás nem váltotta át a kijelölést, így a program nem azt törli, amire a felhasználó rákattintott. Ez adatvesztéshez vezethet, ezért a törlést mindig erősítsd meg (7.4 lecke), és a MouseDown kezelő kötelező.
Dinamikus helyi menü
A menü feliratai és állapotai az elem állapotától függenek: egy kész feladatnál „Jelölés nem késznek”, egy nem késznél „Jelölés késznek”; a prioritás almenüben a jelenlegi bejelölve. Ezt az Opening eseményben állítjuk be, a meglévő elemek tulajdonságait módosítva (nem új elemeket építve minden alkalommal):
private void cmsFeladat_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
Feladat f = lstFeladatok.SelectedItem as Feladat;
miSzerkeszt.Visible = f != null;
miKesz.Visible = f != null;
miPrioritas.Visible = f != null;
miTorol.Visible = f != null;
if (f == null)
{
return;
}
miKesz.Text = f.Kesz ? "Jelölés &nem késznek" : "Jelölés &késznek";
// a prioritás almenü: a Tag 0, 1, 2; a jelenlegi bejelölve
foreach (ToolStripItem elem in miPrioritas.DropDownItems)
{
ToolStripMenuItem pont = elem as ToolStripMenuItem;
if (pont != null)
{
pont.Checked = int.Parse(pont.Tag.ToString()) == f.Prioritas;
}
}
}
private void Prioritas_Click(object sender, EventArgs e)
{
Feladat f = (Feladat)lstFeladatok.SelectedItem;
f.Prioritas = int.Parse(((ToolStripMenuItem)sender).Tag.ToString());
lstFeladatok.Items[lstFeladatok.SelectedIndex] = f; // ugyanaz az objektum: a lista újrarajzolja a szövegét
}Opening csak a meglévő elemek Text, Enabled, Visible, Checked értékét állítja. Az elemeket nem érdemes minden megnyitásnál létrehozni és törölni (lassabb, és a felszabadítás gondot okoz, 5.7 lecke).Items[i] = f), a lista újrarajzolja. A ToString() felülírásával az objektum adja a szöveget.&; ügyelj, hogy a két változatban ne ütközzön másik elemmel.Jósolj!
Az Opening kezelő minden megnyitáskor új ToolStripMenuItem elemeket ad a menühöz (Items.Add), a régieket nem törli. Mit tapasztalsz a harmadik megnyitásnál?
Jóslat ellenőrzése
A menüben háromszor annyi elem van: minden megnyitás újabb példányokat tesz hozzá. Ha az elemeket mégis dinamikusan akarod építeni, előbb töröld a régieket (Items.Clear(), a kezelőkről leiratkozva és Dispose-zal), vagy a végleges elemeket készítsd el egyszer, és csak a tulajdonságaikat frissítsd.
Mit tegyél ide: a feladatlista jobb gombbal előhívott menüje (Szerkesztés, Jelölés késznek, Prioritás almenü kinyitva, Törlés).Javasolt fájlnév: winforms-7-2-menu.webp · Alt szöveg: A feladatlista helyi menüje
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 menü rossz elemre hat
A felhasználó a második elemre kattint jobb gombbal, de a menü a kijelölt harmadikra hat. A jobb kattintás nem jelöl ki. A Ok és javítás
MouseDown eseményben a IndexFromPoint alapján állítsd a SelectedIndex értékét.
2. Az üres területen is megjelenik a teljes menü
Az üres részre kattintva is a „Törlés” és „Szerkesztés” látszik. Az Ok és javítás
Opening eseményben a kijelölés hiányakor rejtsd el az elemhez kötött pontokat (Visible = false), vagy a teljes menüt (e.Cancel = true).
3. A közös menü mindig ugyanazt a mezőt módosítja
private void miMasol_Click(object sender, EventArgs e)
{
txtA.Copy(); // a menü a txtB-hez is hozzá van rendelve
}Hiba és javítás
A közös menü kezelője a SourceControl értékét használja: (cmsSzoveg.SourceControl as TextBoxBase).Copy();.
4. NullReferenceException a SourceControl használatakor
A menü kódból jelenik meg ( A Show(pont)), és a SourceControl üres.Ok és javítás
SourceControl csak akkor ismert, ha a menü egy vezérlőhöz kötve jelent meg (jobb kattintás, vagy a Show(vezérlő, pont) hívás). A Show(Point) változat nem állítja be. Mindig ellenőrizd a null értéket (as + null vizsgálat).
5. A menü minden megnyitásnál több elemet tartalmaz
Az Opening kezelő új elemeket ad hozzá a régiek törlése nélkül. A meglévő elemek tulajdonságait módosítsd, ne újakat adj hozzá. Ha mégis dinamikus építés kell, előbb töröld a régieket (Ok és javítás
Clear és Dispose).
6. A funkció csak jobb gombbal érhető el
A felhasználók egy része nem találja a törlést, vagy a billentyűzettel dolgozók nem érik el. A helyi menü nem lehet az egyetlen út. A funkció legyen a főmenüből és/vagy gombról is elérhető, és a Menu billentyű és a Shift+F10 működjön a helyi menüre.Ok és javítás
Mini projekt: feladatlista helyi menüvel
Készíts feladatlistát, amelyet jobb gombbal is lehet kezelni:
- Az ablak:
lstFeladatok(ListBox),txtUj(új feladat neve,TextBox),btnHozzaad,lblInfo. AFeladatosztály:Nev,Kesz,Prioritas(0: alacsony, 1: közepes, 2: magas); aToString()adja a lista szövegét ([✔] Nev (!!)). - Feladat helyi menü (
cmsFeladat): Szerkesztés, Jelölés késznek / nem késznek (a felirat az állapottól függ), Prioritás almenü (Alacsony, Közepes, Magas, a jelenlegi bejelölve, aTag0/1/2, közös kezelő), elválasztó, Törlés (megerősítéssel), Új feladat (az üres terület menüje). - A jobb kattintás a
MouseDowneseményben kijelöli a kattintott elemet; üres területen csak az Új feladat látszik. - Szövegmező helyi menü (
cmsSzoveg): Visszavonás, Kivágás, Másolás, Beillesztés, Mindent kijelöl, atxtUjés egytxtMegjegyzesmező közösen használja aSourceControlalapján. - A Szerkesztés a feladat nevét a
txtUjmezőbe tölti, és a Hozzáad gomb felirata Módosít lesz, amíg a szerkesztés tart. - Minden funkció elérhető gombbal vagy billentyűvel is: Delete (törlés), F2 (szerkesztés), Szóköz (kész-váltás), Enter a
txtUj-ban (hozzáadás). - Teszteld: jobb kattintás különböző elemeken és üres helyen, a billentyűzetes Shift+F10, a prioritás bejelölése, a közös szövegmenü két mezőn, a szerkesztés és a törlés megerősítése.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A Feladat osztály egy különálló osztály (a Form1.cs mellett, vagy a fájl végén). A menüpontok a lstFeladatok.SelectedItem objektumon dolgoznak; a változás után az elem visszaírásával frissítjük a listát.
public class Feladat
{
public string Nev = "";
public bool Kesz = false;
public int Prioritas = 1;
public override string ToString()
{
string jel = new string('!', Prioritas + 1);
return (Kesz ? "[✔] " : "[ ] ") + Nev + " (" + jel + ")";
}
}
private Feladat szerkesztett = null;
private void Form1_Load(object sender, EventArgs e)
{
miAlacsony.Tag = "0";
miKozepes.Tag = "1";
miMagas.Tag = "2";
miVisszavon.Tag = "visszavon";
miKivag.Tag = "kivag";
miMasol.Tag = "masol";
miBeillesztes.Tag = "beilleszt";
miMindent.Tag = "mindent";
txtUj.ContextMenuStrip = cmsSzoveg;
txtMegjegyzes.ContextMenuStrip = cmsSzoveg;
}
private void Frissit()
{
int kesz = 0;
foreach (object o in lstFeladatok.Items)
{
if (((Feladat)o).Kesz)
{
kesz++;
}
}
lblInfo.Text = $"{lstFeladatok.Items.Count} feladat, ebből kész: {kesz}";
}
private void Mentes()
{
string nev = txtUj.Text.Trim();
if (nev.Length == 0)
{
return;
}
if (szerkesztett != null)
{
szerkesztett.Nev = nev;
lstFeladatok.Items[lstFeladatok.Items.IndexOf(szerkesztett)] = szerkesztett;
szerkesztett = null;
btnHozzaad.Text = "Hozzáad";
}
else
{
Feladat f = new Feladat();
f.Nev = nev;
lstFeladatok.Items.Add(f);
}
txtUj.Clear();
txtUj.Focus();
Frissit();
}
private void btnHozzaad_Click(object sender, EventArgs e)
{
Mentes();
}
private void txtUj_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.SuppressKeyPress = true;
Mentes();
}
}
private void lstFeladatok_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
lstFeladatok.SelectedIndex = lstFeladatok.IndexFromPoint(e.Location);
}
}
private void cmsFeladat_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
Feladat f = lstFeladatok.SelectedItem as Feladat;
miSzerkeszt.Visible = f != null;
miKesz.Visible = f != null;
miPrioritas.Visible = f != null;
miTorol.Visible = f != null;
sepElem.Visible = f != null;
if (f == null)
{
return;
}
miKesz.Text = f.Kesz ? "Jelölés &nem késznek" : "Jelölés &késznek";
foreach (ToolStripItem elem in miPrioritas.DropDownItems)
{
ToolStripMenuItem pont = elem as ToolStripMenuItem;
if (pont != null)
{
pont.Checked = int.Parse(pont.Tag.ToString()) == f.Prioritas;
}
}
}
private void miUjFeladat_Click(object sender, EventArgs e)
{
txtUj.Focus();
}
private void miSzerkeszt_Click(object sender, EventArgs e)
{
Feladat f = lstFeladatok.SelectedItem as Feladat;
if (f != null)
{
szerkesztett = f;
txtUj.Text = f.Nev;
btnHozzaad.Text = "Módosít";
txtUj.Focus();
txtUj.SelectAll();
}
}
private void miKesz_Click(object sender, EventArgs e)
{
Feladat f = lstFeladatok.SelectedItem as Feladat;
if (f != null)
{
f.Kesz = !f.Kesz;
lstFeladatok.Items[lstFeladatok.SelectedIndex] = f;
Frissit();
}
}
private void Prioritas_Click(object sender, EventArgs e)
{
Feladat f = lstFeladatok.SelectedItem as Feladat;
if (f != null)
{
f.Prioritas = int.Parse(((ToolStripMenuItem)sender).Tag.ToString());
lstFeladatok.Items[lstFeladatok.SelectedIndex] = f;
}
}
private void miTorol_Click(object sender, EventArgs e)
{
Feladat f = lstFeladatok.SelectedItem as Feladat;
if (f != null && MessageBox.Show("Biztosan törlöd: " + f.Nev + "?", "Törlés", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
{
lstFeladatok.Items.Remove(f);
Frissit();
}
}
private void lstFeladatok_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Delete) { miTorol.PerformClick(); e.Handled = true; }
else if (e.KeyCode == Keys.F2) { miSzerkeszt.PerformClick(); e.Handled = true; }
else if (e.KeyCode == Keys.Space) { miKesz.PerformClick(); e.SuppressKeyPress = true; }
}
private void cmsSzoveg_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
TextBoxBase mezo = cmsSzoveg.SourceControl as TextBoxBase;
if (mezo == null)
{
e.Cancel = true;
return;
}
bool kijeloles = mezo.SelectionLength > 0;
miVisszavon.Enabled = mezo.CanUndo;
miKivag.Enabled = kijeloles;
miMasol.Enabled = kijeloles;
miBeillesztes.Enabled = Clipboard.ContainsText();
miMindent.Enabled = mezo.TextLength > 0;
}
private void SzovegMuvelet_Click(object sender, EventArgs e)
{
ToolStripMenuItem pont = (ToolStripMenuItem)sender;
TextBoxBase mezo = ((ContextMenuStrip)pont.Owner).SourceControl as TextBoxBase;
if (mezo == null) { return; }
switch (pont.Tag.ToString())
{
case "visszavon": mezo.Undo(); break;
case "kivag": mezo.Cut(); break;
case "masol": mezo.Copy(); break;
case "beilleszt": mezo.Paste(); break;
case "mindent": mezo.SelectAll(); break;
}
}Mit tegyél ide: a kész feladatlista: néhány feladat, a jobb gombos menü a Prioritás almenüvel, alatta az információsor.Javasolt fájlnév: winforms-7-2-kesz.webp · Alt szöveg: A feladatlista helyi menüvel
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. Közös menü több gombhoz
Három A menü Button (Piros, Zöld, Kék) közös helyi menüje Háttér: sárga és Háttér: eredeti pontot tartalmaz, és a kattintott gombra hat. Honnan tudod, melyik gomb hívta?Egy lehetséges megoldás
SourceControl tulajdonságából, a menüpont kezelőjében az Owner láncon át.private void miSarga_Click(object sender, EventArgs e)
{
ToolStripMenuItem pont = (ToolStripMenuItem)sender;
Button gomb = ((ContextMenuStrip)pont.Owner).SourceControl as Button;
if (gomb != null)
{
gomb.BackColor = Color.Gold;
}
}
2. Menü a gomb alatt
A Több… gomb a A cmsTobb menüt a gomb alatt jeleníti meg. Hogyan, és mi lesz a SourceControl?Egy lehetséges megoldás
Show(gomb, new Point(0, gomb.Height)) hívással. A SourceControl ilyenkor a gomb.private void btnTobb_Click(object sender, EventArgs e)
{
cmsTobb.Show(btnTobb, new Point(0, btnTobb.Height));
}
3. Kép helyi menüje
Egy A vágólapra PictureBox jobb gombos menüje a Kép mentése és Kép másolása a vágólapra pontot kínálja. Melyik metódusokat használod?Egy lehetséges megoldás
Clipboard.SetImage(pb.Image), a mentésre pb.Image.Save(útvonal) (a SaveFileDialog adja az útvonalat, 7.8 lecke). Ha nincs kép, a menü nem nyílik meg (e.Cancel).private void cmsKep_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = pbKep.Image == null;
}
private void miMasolKep_Click(object sender, EventArgs e)
{
Clipboard.SetImage(pbKep.Image);
}
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. Mikor jelenik meg a helyi menü?
Sorolj fel három módot, ahogyan a felhasználó előhívhat egy helyi menüt. (1) Jobb egérgomb felengedése a vezérlőn; (2) a Menu billentyű; (3) Shift+F10. Kódból a Megoldás megnyitása
Show(...) hívással.
2. Mire való az e.Cancel az Opening-ben?
Adj egy példát, amikor a menüt nem szabad megjeleníteni. Ha nincs mit mutatni: például üres lista, kép nélküli Megoldás megnyitása
PictureBox, vagy olyan terület, ahol egyetlen parancs sem értelmezhető. Az e.Cancel = true megakadályozza a megnyitást.
Közepes szint
3. TreeView csomópont jobb kattintásra
Egy A TreeView csomópontjára jobb gombbal kattintva a csomópont legyen kijelölve, mielőtt a menü megnyílik. Melyik metódussal?Megoldás megnyitása
GetNodeAt(e.Location) metódussal a MouseDown eseményben.private void tvFa_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
tvFa.SelectedNode = tvFa.GetNodeAt(e.Location); // null, ha nincs csomópont a helyen
}
}
4. Menü a billentyűzetről
A Shift+F10 a listában a kijelölt elem helyén nyisson menüt. Mit kell a helyi menüvel tenned, és mit nem? A hozzárendelt Megoldás megnyitása
ContextMenuStrip a billentyűre magától megnyílik, külön kód nem kell. Ilyenkor nincs egérpont, ezért a menünek a kijelölt elemre kell hatnia (a SelectedIndex alapján), és az Opening ne az egér helyétől függjön.
Kihívás szint
5. Rendszertálca-menü
A programhoz tartozzon egy A NotifyIcon a tálca értesítési területén, helyi menüvel (Megnyitás, Kilépés). Mit kell beállítanod?Megoldás megnyitása
NotifyIcon.Icon (kötelező, különben nem látszik), a Visible = true és a ContextMenuStrip. Kilépéskor a Visible = false és Dispose() kell, különben az ikon a tálcán marad, amíg az egér rá nem mutat. Az ablak elrejtése a Hide(), megjelenítése a Show() és Activate().private void miMegnyit_Click(object sender, EventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();
}
private void miKilep_Click(object sender, EventArgs e)
{
ni.Visible = false;
ni.Dispose();
Application.Exit();
}
6. Közös menü és szerkesztő visszahívás
Egy RichTextBox helyi menüje a Félkövér pontot kínálja, a pipa mutatja, hogy a kijelölés félkövér-e. Hogyan állítod a pipát az A Opening-ben?Megoldás megnyitása
SelectionFont lehet null (vegyes formázású kijelölés), ezért ellenőrizni kell. A kapcsoló kattintáskor új Font példányt állít a stílus módosításával (2.4 lecke).private void cmsRtb_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
Font f = rtb.SelectionFont;
miFelkover.Checked = f != null && f.Bold;
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. A vezérlő A jobb gomb felengedésekor, ezért a Megmondja, melyik vezérlő hívta elő a menüt, így egy menü több vezérlőhöz is használható. A jobb kattintás nem jelöli ki az elemet, az Az A Mert nem mindenki fedezi fel, és a billentyűzettel dolgozók nehezen érik el. A funkció legyen főmenüből vagy gombról is elérhető.1. Hogyan rendelsz helyi menüt egy vezérlőhöz?
ContextMenuStrip tulajdonságával (tervezőben vagy kódból).2. Mikor jelenik meg a menü egérrel?
MouseDown még előtte lefut.3. Mire való a SourceControl?
4. Miért kell a MouseDown kezelő a listához?
IndexFromPoint alapján kell kijelölni, különben a menü rossz elemre hat.5. Hol igazítod a menüt az állapothoz?
Opening eseményben, a meglévő elemek tulajdonságait módosítva.6. Mi a különbség a Visible és az Enabled között a menüpontnál?
Visible = false elrejti (nem értelmezhető), az Enabled = false szürkévé teszi (értelmezhető, de most nem végezhető el).7. Miért ne legyen a helyi menü az egyetlen út?
Mit tudsz már?
- Helyi menüt rendelsz vezérlőkhöz, és kódból is megjelenítesz.
- Az
Openingeseményben igazítod a menüt, vagy megakadályozod a megnyitását. - A
SourceControlsegítségével közös menüt használsz több vezérlőn. - A kattintás helyén lévő elemet kijelölöd (
IndexFromPoint). - Dinamikus menüt készítesz, és tudod, hogy nem lehet az egyetlen út.
Következő lecke: StatusStrip és alkalmazásállapot
Az ablak alján az állapotsor: üzenetek, sor és oszlop, haladásjelző és ideiglenes értesítések.



