Grafikus C# – Windows Forms tanfolyam › 6.6 lecke Windows Forms · 6.6 lecke · fókusz és navigáció Nem mindenki használ egeret. Sokan a billentyűzetet szeretik, mert gyorsabb, és vannak, akik egyáltalán nem tudnak egeret használni. Egy jó űrlap egér nélkül is teljesen használható: a Tab billentyű logikus sorrendben járja be a mezőket, az TabIndex, fókusz és billentyűzetes navigáció
Alt+betű a megfelelő mezőre ugrik, az Enter továbblép, és az ellenőrzés a megfelelő helyen szól. Ebben a leckében megtanulod a fókusz fogalmát, a TabIndex és a TabStop szerepét, a mnemonikus betűket, és a mezők ellenőrzését (Validating, ErrorProvider).
A lecke végére
- érted a fókusz fogalmát, és használod a
Focus(),ActiveControl,EnterésLeavelehetőségeit; - beállítod a tabulátorsorrendet (
TabIndex,TabStop, View → Tab Order), és érted a beágyazott tárolók szabályát; - mnemonikus betűket adsz a mezőkhöz (
&a címkén); - megvalósítod az Enter és Shift+Enter navigációt;
- a
Validating,ValidateChildren,AutoValidateésCausesValidationsegítségével érvényesítesz, és a hibátErrorProvider-rel jelzed; - billentyűzettel teszteled az űrlapot, és felismered a gyakori fókuszhibákat.
Mielőtt elkezded
Ehhez a leckéhez a 5.4 lecke (billentyűk, ProcessCmdKey, mnemonikus billentyűk), az 5.5 lecke (változás-események), az 1.11 lecke (ErrorProvider) és a 6.4 lecke (TableLayoutPanel) szükséges. Új projektben próbáld ki a példákat.
A fókusz: ki kapja a billentyűket?
A fókusz (focus) azt jelzi, melyik vezérlő kapja a billentyűzet bevitelét: a fókuszban lévő szövegmezőbe kerülnek a betűk, a fókuszban lévő gombot nyomja meg a Szóköz. Egyszerre mindig csak egy vezérlőnél lehet fókusz. A fókuszban lévő vezérlőt a Windows látható jelzéssel látja el (szövegmezőnél villogó kurzor, gombnál szaggatott keret).
| Tag | Jelentés |
|---|---|
vezérlő.Focus() | A fókuszt a vezérlőre teszi (igazat ad, ha sikerült). |
vezérlő.Select() | Hasonló: kiválasztja a vezérlőt, a fókuszt nem mindig kéri el. |
vezérlő.Focused | Igaz, ha a vezérlő most a fókuszban van. |
vezérlő.CanFocus | Igaz, ha a vezérlő fókuszt kaphat (látható, engedélyezett). |
this.ActiveControl | Az ablak aktív (fókuszban lévő) vezérlője; beállítható az induló fókuszhoz. |
Enter, Leave esemény | A vezérlő megkapja, illetve elveszíti a fókuszt (5.8 lecke). |
GotFocus, LostFocus | Alacsonyabb szintű párja az Enter és Leave eseménynek. Az Enter és Leave általában elég. |
Validating, Validated | A fókusz elhagyásakor, az érvényesítéshez (lásd lent). |
private void Form1_Shown(object sender, EventArgs e)
{
txtNev.Focus(); // az induló fókusz (az ablak megjelenése után)
// vagy: this.ActiveControl = txtNev;
}
private void txtNev_Enter(object sender, EventArgs e)
{
lblSugo.Text = "Add meg a teljes neved."; // súgó a fókuszba kerülő mezőhöz
txtNev.SelectAll(); // a tartalom kijelölve: gépeléssel felülírható
}
private void txtNev_Leave(object sender, EventArgs e)
{
lblSugo.Text = "";
}
private void btnMentes_Click(object sender, EventArgs e)
{
// a gomb megnyomása után a fókusz a gombon marad: visszaadjuk a szerkesztőnek
rtbSzoveg.Focus();
}Load eseményben az ablak még nem látható, ezért a Focus() hívás nem mindig sikeres. Az induló fókuszt a Shown eseményben, vagy a this.ActiveControl = vezérlő; értékadással a Load-ban állítsd be.Enabled = false), rejtett (Visible = false) vezérlő, és a Label, Panel, PictureBox alapból nem (ezek nem választhatók Tab billentyűvel).ToolStrip gombjai nem veszik át a fókuszt.SelectAll néha nem érvényesül (az egérkattintás törli a kijelölést). A billentyűzettel (Tab) érkezéskor jól működik.Jósolj!
A Form1_Load eseményben a txtNev.Focus(); sort írod, de indításkor a fókusz mégsem a névmezőn van. Miért?
Jóslat ellenőrzése
A Load az ablak megjelenése előtt fut, és a vezérlő ekkor még nem tud fókuszt kapni (a Focus() hamis értéket ad). A javítás: this.ActiveControl = txtNev; a Load-ban (ez az induló fókuszt jelöli ki), vagy a Focus() hívása a Shown eseményben (5.6 lecke).
TabIndex és TabStop: a tabulátorsorrend
A Tab billentyű a fókuszt a következő vezérlőre viszi, a Shift+Tab az előzőre. A sorrendet a TabIndex tulajdonság adja (kisebb szám előbb), a TabStop pedig megmondja, megáll-e rajta a fókusz (igaz) vagy átugorja (hamis).
TabIndex
Egész szám, amely a tárolón belüli sorrendet adja. A vezérlők a tervezőben a létrehozásuk sorrendjében kapnak indexet, ezért ha a felületet átrendezed, a sorrend könnyen zavaros lesz. Beállítás: View → Tab Order a Visual Studio menüjében: a vezérlők fölött számok jelennek meg, sorban rájuk kattintva újraszámozod őket.
TabStop
Ha hamis, a Tab átugorja a vezérlőt (egérrel továbbra is elérhető). Alapból minden fókuszt fogadó vezérlőn igaz. Hamisra állítsd azokat, amelyek nem tartoznak az űrlap kitöltéséhez (súgó gomb, eszköz gomb).
// a sorrend kódból is beállítható
txtNev.TabIndex = 0;
txtEmail.TabIndex = 1;
dtpSzuletes.TabIndex = 2;
btnKuld.TabIndex = 3;
// ne álljon meg a súgó gombon és a kép felett
btnSugo.TabStop = false;
// a tabulátorsorrend automatikus beállítása a vezérlők helye szerint:
// fentről lefelé, soron belül balról jobbra
private void RendezTab(Control szulo)
{
List<Control> gyermekek = new List<Control>();
foreach (Control c in szulo.Controls)
{
gyermekek.Add(c);
}
gyermekek.Sort((a, b) =>
{
int kulonbseg = a.Top.CompareTo(b.Top);
return kulonbseg != 0 ? kulonbseg : a.Left.CompareTo(b.Left);
});
for (int i = 0; i < gyermekek.Count; i++)
{
gyermekek[i].TabIndex = i;
RendezTab(gyermekek[i]); // a beágyazott tároló tartalma
}
}TabIndex a tárolón belüli sorrend. A Tab egy GroupBox belsejét a GroupBox saját TabIndex pozíciójában járja be: előbb a GroupBox összes gyermekét, aztán lép a következő, a GroupBox utáni vezérlőre. A Tab Order nézetben ezért 1.0, 1.1 alakú számok jelennek meg.Sort((a, b) => ...) a lista elemeit a megadott összehasonlítás szerint rendezi: itt előbb a Top, azonos magasságnál a Left érték dönt (5.1 lecke, lambda).Label és a Panel alapból TabStop = false, de a TabIndex a címke mnemonikus billentyűjénél számít (lásd lent).Jósolj!
Az ablakon van egy txtNev (TabIndex 0), egy GroupBox (TabIndex 1, benne két választógomb 0 és 1 indexszel) és egy btnKuld (TabIndex 2). Milyen sorrendben járja be a fókusz a vezérlőket a Tab billentyűvel?
Jóslat ellenőrzése
txtNev → (a választógomb-csoport bejelölt gombja) → btnKuld. A GroupBox maga nem kap fókuszt, de a belső vezérlői a GroupBox sorrendi helyén következnek. A csoport két gombjából a Tab csak a bejelöltre áll meg (vagy az elsőre, ha egyik sincs bejelölve), a másikat a nyilakkal éred el.
Mnemonikus betűk: Alt + betű
Az 5.4 leckében láttad az & jelet: a címke szövegében &Név: aláhúzza az N betűt, és az Alt+N billentyű a vezérlőre ugrik. A Label nem kaphat fókuszt, ezért a mnemonikus billentyű a tabulátorsorrendben a címke után következő vezérlőre teszi a fókuszt. Ezért kell, hogy a címke TabIndex értéke közvetlenül a mezőé előtt álljon:
lblNev.Text = "&Név:"; lblNev.TabIndex = 0; txtNev.TabIndex = 1;
lblEmail.Text = "&E-mail:"; lblEmail.TabIndex = 2; txtEmail.TabIndex = 3;
btnKuld.Text = "&Küldés"; // a gomb Alt+K billentyűre aktiválódik
// két azonos betű ne legyen egy ablakon: ilyenkor az Alt+betű váltogat közöttükTabIndex értéke legyen eggyel kisebb, mint a hozzá tartozó mezőé. Ha a sorrend más, az Alt+betű rossz mezőre ugrik.Label.UseMnemonic (alapból igaz) bekapcsolja az & értelmezését. Ha magát az & jelet kell kiírni, duplázd: &&.Text szövegében is használható az &: közvetlenül aktiválják a vezérlőt.Mente&s). Az aláhúzás csak Alt megnyomása után látszik (a Windows beállítástól függően).Jósolj!
A lblNev (&Név:) címke TabIndex értéke 0, de a mellette lévő txtNev TabIndex értéke 5, és közéjük kerül egy másik mező (TabIndex 1–4). Az Alt+N melyik vezérlőre ugrik?
Jóslat ellenőrzése
A sorrendben következő fókuszt kaphatő vezérlőre (TabIndex 1) ugrik, vagyis nem a névmezőre. Ezért kell a címke és a mező TabIndex értékének közvetlenül egymás után állnia.
Enter-navigáció
Az űrlapkitöltők gyakran az Enter billentyűt használják továbblépésre (a Tab helyett). Az 5.4 leckében a KeyDown eseménnyel oldottuk meg mezőnként. Központi, az egész ablakra vonatkozó megoldás a ProcessCmdKey-ben. Az egysoros szövegmezőnél lép tovább, a többsorosnál (Multiline) új sort ad, a gombnál megnyomja a gombot:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Enter || keyData == (Keys.Shift | Keys.Enter))
{
TextBox mezo = this.ActiveControl as TextBox;
if (mezo != null && !mezo.Multiline)
{
bool elore = keyData == Keys.Enter; // Shift+Enter: visszafelé
this.SelectNextControl(mezo, elore, true, true, true);
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}TabStop-ú vezérlők, beágyazva (tárolókba is), körbe (az utolsó után az első).Multiline és a típusvizsgálat adja.Jósolj!
A ProcessCmdKey az Enter billentyűt minden vezérlőnél továbblépésre használja (a Multiline ellenőrzés nélkül). Mi történik az Enter lenyomásakor egy többsoros megjegyzés mezőben?
Jóslat ellenőrzése
A fókusz továbblép a következő mezőre, és a felhasználó nem tud új sort írni a megjegyzésbe. Ezért a többsoros mezőket ki kell hagyni (!mezo.Multiline). A ProcessCmdKey az összes billentyűt elkapja, ezért a kivételeket mindig meg kell határozni.
Érvényesítés: Validating, ValidateChildren, ErrorProvider
A Validating esemény a fókusz elhagyásakor fut (5.2 lecke): itt ellenőrizheted a mezőt, és az e.Cancel = true a fókuszt a mezőn tartja. Három tulajdonság szabályozza a működést:
| Tulajdonság / metódus | Jelentése |
|---|---|
CausesValidation | A cél vezérlőn: ha hamis (például a Mégse gombon), a fókusz oda ellenőrzés nélkül mehet. Alapból igaz. |
AutoValidate (Form) | A Form érvényesítési módja: EnablePreventFocusChange (alap: a hibás mezőből nem lehet kilépni), EnableAllowFocusChange (kilépni lehet, de a hiba jelzett), Disable (nincs automatikus ellenőrzés). |
ValidateChildren() | Az összes gyermek Validating eseményét lefuttatja; hamis értéket ad, ha valamelyik hibás. A Küldés gomb ezzel ellenőrzi az egész űrlapot. |
private void Form1_Load(object sender, EventArgs e)
{
this.AutoValidate = AutoValidate.EnableAllowFocusChange; // a felhasználó nincs mezőhöz láncolva
btnMegse.CausesValidation = false; // a Mégse mindig megnyomható
}
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;
}
else
{
epHiba.SetError(txtNev, ""); // a hibajelzés törlése
}
}
private void btnKuld_Click(object sender, EventArgs e)
{
if (!this.ValidateChildren())
{
lblUzenet.Text = "Javítsd a jelölt mezőket!";
return;
}
lblUzenet.Text = "Az adatok rendben vannak.";
}Validating hibát jelez, a fókusz nem hagyhatja el a mezőt. Ez a fókuszcsapda zavaró lehet (a felhasználó sem a Mégse gombra, sem az ablak bezárására nem tud átlépni, ha a CausesValidation igaz).ValidateChildren() a mentéskor újra ellenőriz. Ez a barátságosabb mód.SetError(mező, "").ValidateChildren() az összes mezőt lefuttatja, tehát a nem érintettek hibája is kiderül.Jósolj!
Az AutoValidate értéke az alapérték, és a névmező hibás (üres), a Validating kezelő e.Cancel = true értéket ad. A felhasználó az ablak jobb felső sarkában lévő X gombra kattint. Bezáródik az ablak?
Jóslat ellenőrzése
Az alapértelmezett EnablePreventFocusChange módban a bezárás is megakadhat (a bezárás a fókuszváltást is érvényesíti): a hibás mező „fogva tart”. Ezért állítsd az AutoValidate értékét EnableAllowFocusChange értékre, és a bezáró/Mégse funkciókat tedd CausesValidation = false gombra. A viselkedést a Windows és a keretrendszer verziója is befolyásolhatja, ezért próbáld ki a saját programodban.
Mit tegyél ide: egy űrlap, amelyen a hibás névmező mellett piros felkiáltójel (ErrorProvider) látszik, és alul a „Javítsd a jelölt mezőket!” üzenet.Javasolt fájlnév: winforms-6-6-hiba.webp · Alt szöveg: Érvényesítés ErrorProvider jelzéssel
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 Tab össze-vissza ugrál
A Tab billentyű a mezőket nem a megjelenés sorrendjében járja be. A Ok és javítás
TabIndex értékek a létrehozás sorrendjét követik, nem a helyet. Használd a View → Tab Order nézetet, és kattints sorban a vezérlőkre, vagy számold újra kódból a helyük szerint (RendezTab).
2. Az Alt+betű rossz mezőre ugrik
Az Alt+N nem a névmezőre, hanem a következő vezérlőre visz. A címke Ok és javítás
TabIndex értéke nem közvetlenül a mező előtt áll. A mnemonikus billentyű a tabulátorsorrendben a címke után következő fókuszt kapható vezérlőre ugrik. Rendezd át a sorrendet.
3. A Focus() nem működik indításkor
private void Form1_Load(object sender, EventArgs e)
{
txtNev.Focus(); // az ablak még nem látható
}Hiba és javítás
Használd a this.ActiveControl = txtNev; értékadást, vagy hívd a Focus() metódust a Shown eseményben.
4. Nem lehet kilépni az ablakból
A Mégse gomb és az X nem működik, amíg a mező hibás. A Ok és javítás
Validating esemény e.Cancel = true értéke a fókuszt a mezőre kényszeríti, és a CausesValidation igaz a Mégse gombon. Állítsd a Mégse gomb CausesValidation értékét hamisra, és az AutoValidate értékét EnableAllowFocusChange értékre.
5. A kép és a címke nem fókuszálható
pbKep.TabStop = true; // de a PictureBox nem fogad billentyűketHiba és javítás
A PictureBox, a Label és a Panel alapból nem kap fókuszt (CanFocus hamis, a TabStop beállítása nem változtat). Ha a kép interaktív (kattintható), használj Button vezérlőt képpel, vagy saját vezérlőt, amely fókuszt fogad.
6. A gomb megnyomása után a szerkesztés nem folytatható
A Félkövér gomb megnyomása után a gépelés nem a szerkesztőbe kerül. A fókusz a gombon maradt. Adj a végén Ok és javítás
rtbSzoveg.Focus(); hívást a gomb kezelőjébe. A ToolStrip gombjai nem veszik át a fókuszt, így ott ez a probléma nem jelentkezik.
Mini projekt: billentyűzettel kezelhető jelentkezési űrlap
Készíts jelentkezési űrlapot, amelyet egér nélkül is gyorsan ki lehet tölteni:
- Az ablak: egy
TableLayoutPanel(tlp, 2 oszlop:AutoSize,Percent 100) a következő sorokkal: Név (txtNev), E-mail (txtEmail), Telefon (txtTelefon), Szint (cmbSzint: Kezdő, Haladó), Megjegyzés (txtMegjegyzes, többsoros), egyGroupBoxMikor érsz rá? három jelölőnégyzettel, és alul egyFlowLayoutPanela Küldés (btnKuld) és Mégse (btnMegse) gombbal. - Tabulátorsorrend: logikus (fentről lefelé); a címkék közvetlenül a mezőik előtt; a Mégse gomb
TabStopértéke hamis. - Mnemonikus betűk: &Név, &E-mail, &Telefon, &Szint, &Megjegyzés, a &Küldés gombon is, mind különböző betűvel.
- Induló fókusz: a névmezőn (
ActiveControl). - Enter-navigáció: az egysoros mezőkben az Enter előre, a Shift+Enter hátra lép, a megjegyzés mezőben sortörés (
ProcessCmdKey). - Súgó: minden mező
Entereseménye (közös kezelő, rekurzív bejárással, 5.8 lecke) kiírja azlblSugocímkébe a mező nevét és a megadott súgószöveget (aTagtulajdonságban). - Érvényesítés: a név kötelező, az e-mail tartalmazza az
@jelet, a telefon (ha van) csak számjegyet,+és szóköz karaktert. Hiba eseténErrorProvider. AzAutoValidatelegyenEnableAllowFocusChange, a Mégse gombonCausesValidation = false. - A Küldés gomb
ValidateChildren()hívásával ellenőriz, és hiba esetén az első hibás mezőre teszi a fókuszt. - Egy Tab-sorrend gomb (
btnSorrend) listába írja a vezérlőket aTabIndexszerint, behúzással a hierarchia szerint. - Teszteld: csak billentyűzettel kitöltés, rossz adatok, az Alt+betűk, a Shift+Tab, a Mégse gomb hibás mező mellett.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A súgó szövege a vezérlők Tag tulajdonságába kerül a Load-ban, a rekurzív feliratkozás a TextBox, ComboBox és CheckBox vezérlőkre köt egy közös Enter kezelőt. Az érvényesítést külön metódusok végzik, hogy a Validating kezelők rövidek maradjanak.
private void Form1_Load(object sender, EventArgs e)
{
this.AutoValidate = AutoValidate.EnableAllowFocusChange;
btnMegse.CausesValidation = false;
btnMegse.TabStop = false;
cmbSzint.Items.AddRange(new object[] { "Kezdő", "Haladó" });
cmbSzint.SelectedIndex = 0;
txtNev.Tag = "Add meg a teljes neved.";
txtEmail.Tag = "Az e-mail címnek tartalmaznia kell a @ jelet.";
txtTelefon.Tag = "Nem kötelező. Számjegyek, + jel és szóköz használható.";
cmbSzint.Tag = "Válaszd ki a tudásszinted.";
txtMegjegyzes.Tag = "Szabad szöveg; az Enter új sort kezd.";
SugoKot(this);
this.ActiveControl = txtNev;
}
private void SugoKot(Control szulo)
{
foreach (Control c in szulo.Controls)
{
if (c is TextBox || c is ComboBox || c is CheckBox)
{
c.Enter += Mezo_Enter;
}
SugoKot(c);
}
}
private void Mezo_Enter(object sender, EventArgs e)
{
Control c = (Control)sender;
lblSugo.Text = c.Tag == null ? "" : c.Tag.ToString();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Enter || keyData == (Keys.Shift | Keys.Enter))
{
TextBox mezo = this.ActiveControl as TextBox;
if (mezo != null && !mezo.Multiline)
{
this.SelectNextControl(mezo, keyData == Keys.Enter, true, true, true);
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void txtNev_Validating(object sender, CancelEventArgs e)
{
Hiba(txtNev, txtNev.Text.Trim().Length == 0 ? "A név kötelező." : "", e);
}
private void txtEmail_Validating(object sender, CancelEventArgs e)
{
Hiba(txtEmail, txtEmail.Text.Contains("@") ? "" : "Az e-mail címnek tartalmaznia kell a @ jelet.", e);
}
private void txtTelefon_Validating(object sender, CancelEventArgs e)
{
string uzenet = "";
foreach (char c in txtTelefon.Text)
{
if (!char.IsDigit(c) && c != '+' && c != ' ')
{
uzenet = "A telefonszám csak számjegyet, + jelet és szóközt tartalmazhat.";
break;
}
}
Hiba(txtTelefon, uzenet, e);
}
private void Hiba(Control mezo, string uzenet, CancelEventArgs e)
{
epHiba.SetError(mezo, uzenet);
if (uzenet.Length > 0)
{
e.Cancel = true;
}
}
private void btnKuld_Click(object sender, EventArgs e)
{
if (!this.ValidateChildren())
{
lblUzenet.Text = "Javítsd a jelölt mezőket!";
if (epHiba.GetError(txtNev).Length > 0) { txtNev.Focus(); }
else if (epHiba.GetError(txtEmail).Length > 0) { txtEmail.Focus(); }
else { txtTelefon.Focus(); }
return;
}
lblUzenet.Text = "Köszönjük a jelentkezést, " + txtNev.Text.Trim() + "!";
}
private void btnMegse_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnSorrend_Click(object sender, EventArgs e)
{
lstSorrend.Items.Clear();
Sorrend(this, 0);
}
private void Sorrend(Control szulo, int szint)
{
List<Control> gyermekek = new List<Control>();
foreach (Control c in szulo.Controls)
{
gyermekek.Add(c);
}
gyermekek.Sort((a, b) => a.TabIndex.CompareTo(b.TabIndex));
foreach (Control c in gyermekek)
{
lstSorrend.Items.Add(new string(' ', szint * 2) + c.TabIndex + ": " + c.Name + (c.TabStop ? "" : " (TabStop nélkül)"));
Sorrend(c, szint + 1);
}
}Mit tegyél ide: a kész űrlap: a kitöltött mezők, a súgósor, a hibás e-mail melletti piros jel és a tabulátorsorrend-lista.Javasolt fájlnév: winforms-6-6-kesz.webp · Alt szöveg: A billentyűzettel kezelhető jelentkezési űrlap
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. Automatikus tabulátorsorrend
Írj Tab-sorrend rendezése gombot, amely a vezérlők helye szerint (fentről lefelé, azonos magasságnál balról jobbra) újraszámolja a Hasznos egy átrendezett, kusza felületnél. A korlátja, hogy csak a helyet nézi: a címke–mező mnemonikus párosításhoz a címke mindig közvetlenül a mezője előtt kell, hogy álljon (ez a hely szerint általában teljesül, ha a címke bal oldalt van). A kód a leckebeli TabIndex értékeket, a beágyazott tárolókban is. Mikor hasznos, és mi a korlátja?Egy lehetséges megoldás
RendezTab metódus.
2. Kiemelt mező és a fókusz visszaadása
A fókuszban lévő szövegmező háttere legyen világossárga, ha elhagyja a fókusz, álljon vissza. A Töröl gomb a mezőt kiüríti, és a fókuszt visszaadja a mezőnek, amelyben volt. Hogyan tudod, melyik mezőben volt a fókusz, amikor a gombra kattintottak? Egy mezőben jegyzed az utolsó fókuszt kapott szövegmezőt az Egy lehetséges megoldás
Enter eseményben, mert a gombra kattintás után az ActiveControl már a gomb.private TextBox utolso = null;
private void Mezo_Enter(object sender, EventArgs e)
{
utolso = (TextBox)sender;
utolso.BackColor = Color.LightYellow;
}
private void Mezo_Leave(object sender, EventArgs e)
{
((TextBox)sender).BackColor = SystemColors.Window;
}
private void btnTorol_Click(object sender, EventArgs e)
{
if (utolso != null)
{
utolso.Clear();
utolso.Focus();
}
}
3. Érvényesítés Mégse gombbal
Készíts kétmezős űrlapot (név, kor), amelynek Mentés gombja A Mégse gombon ValidateChildren() hívással ellenőriz, a Mégse gomb pedig mindig bezárja az ablakot hibás mező mellett is. A kor 1 és 120 közötti szám. Melyik beállítások kellenek?Egy lehetséges megoldás
CausesValidation = false, a formon AutoValidate = EnableAllowFocusChange. A kor ellenőrzése int.TryParse és tartományvizsgálat.private void txtKor_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
int kor;
if (!int.TryParse(txtKor.Text, out kor) || kor < 1 || kor > 120)
{
epHiba.SetError(txtKor, "A kor 1 és 120 közötti szám legyen.");
e.Cancel = true;
}
else
{
epHiba.SetError(txtKor, "");
}
}
private void btnMent_Click(object sender, EventArgs e)
{
if (this.ValidateChildren())
{
MessageBox.Show("Mentve.");
}
}
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. TabStop
Mit tesz a A Tab billentyű átugorja a vezérlőt (egérrel továbbra is használható). Olyan vezérlőkön használom, amelyek nem tartoznak az űrlap kitöltéséhez (súgó gomb, eszköz gomb, Mégse).TabStop = false beállítás, és mikor használnád?Megoldás megnyitása
2. Induló fókusz
Állítsd be, hogy az ablak a Az txtKereso mezőn induljon, két módon.Megoldás megnyitása
ActiveControl értékadásával a Load-ban, vagy a Focus() hívásával a Shown eseményben.private void Form1_Load(object sender, EventArgs e)
{
this.ActiveControl = txtKereso; // 1. mód
}
private void Form1_Shown(object sender, EventArgs e)
{
txtKereso.Focus(); // 2. mód
}
Közepes szint
3. Címke és mnemonikus mező
A A címke után közvetlenül az lblEmail (&E-mail:) címke után két másik mező áll, mielőtt az txtEmail. Hogyan javítod?Megoldás megnyitása
txtEmail legyen a tabulátorsorrendben: a Tab Order nézetben állítsd át, vagy kódból: lblEmail.TabIndex = txtEmail.TabIndex - 1; (és a közbeeső mezők indexét igazítsd).
4. Az AutoValidate módjai
Hasonlítsd össze a három AutoValidate módot egy hibás mezőnél: mi történik, ha a felhasználó (a) másik mezőre kattint, (b) a bezáró gombra kattint?Megoldás megnyitása
Mód (a) másik mező (b) bezárás EnablePreventFocusChangenem lehet kilépni megakadhat EnableAllowFocusChangekilép, a hiba jelzett bezárul Disablenincs ellenőrzés bezárul
Kihívás szint
5. Fókusznapló
Készíts hibakereső listát, amely minden vezérlő fókuszváltását naplózza (12:03:11 – txtNev megkapta a fókuszt), a beágyazottakéit is. Mire jó? A napló megmutatja a tényleges fókuszsorrendet, és hogy melyik vezérlő veszi el váratlanul a fókuszt. A rekurzív feliratkozás az Megoldás megnyitása
Enter eseményre köt egy közös kezelőt.private void NaploKot(Control szulo)
{
foreach (Control c in szulo.Controls)
{
c.Enter += (s, e) =>
lstNaplo.Items.Insert(0, DateTime.Now.ToString("HH:mm:ss") + " – " + ((Control)s).Name);
NaploKot(c);
}
}
private void Form1_Load(object sender, EventArgs e)
{
NaploKot(this);
}
6. Nyilak a mezők között
A Le nyíl az egysoros szövegmezőkben a következő mezőre, a Fel nyíl az előzőre lép (mint egy táblázatban). Hol kezeled, és mire vigyázol? A Megoldás megnyitása
ProcessCmdKey-ben, csak egysoros TextBox-nál (a többsorosnál a nyilak a szövegben mozgatják a kurzort). Más vezérlőknél (ComboBox, NumericUpDown) a nyilaknak saját funkciójuk van, ezeket ne nyeld el.protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
TextBox mezo = this.ActiveControl as TextBox;
if (mezo != null && !mezo.Multiline)
{
if (keyData == Keys.Down)
{
this.SelectNextControl(mezo, true, true, true, true);
return true;
}
if (keyData == Keys.Up)
{
this.SelectNextControl(mezo, false, true, true, true);
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Az a vezérlő, amely a billentyűzet bevitelét kapja. Egyszerre egy vezérlőnél van. A tárolón belüli tabulátorsorrendet: a kisebb szám előbb következik. A beágyazott tárolók hierarchikusan járódnak be. View → Tab Order, és sorban a vezérlőkre kattintva. Mert a mnemonikus billentyű a tabulátorsorrendben a címke után következő fókuszt kapható vezérlőre ugrik. Az összes gyermek Hogy hibás mező mellett is megnyomható legyen az ellenőrzés nélkül, és a felhasználó ne legyen a mezőhöz láncolva.1. Mi a fókusz?
2. Mit ad meg a TabIndex?
3. Hogyan állítod át a sorrendet a tervezőben?
4. Miért kell a címke TabIndex értékének közvetlenül a mező előtt állnia?
5. Hol állítod be az induló fókuszt?
this.ActiveControl = mezo; a Load-ban, vagy Focus() a Shown-ban. A Load-beli Focus() nem megbízható.6. Mire való a ValidateChildren()?
Validating eseményét lefuttatja, és hamisat ad, ha valamelyik hibás: a mentés előtti teljes ellenőrzéshez.7. Miért kell a Mégse gombon CausesValidation = false?
Mit tudsz már?
- Érted a fókuszt, és beállítod az induló fókuszt és a tabulátorsorrendet.
- Mnemonikus betűkkel és Enter navigációval gyorsítod a kitöltést.
- A
Validating,ValidateChildren,AutoValidateésErrorProvidersegítségével érvényesítesz. - Az űrlapot billentyűzettel teszteled.
- Felismered a gyakori fókuszhibákat (fókuszcsapda, rossz sorrend,
Load-beliFocus).
Következő lecke: AcceptButton, CancelButton és alapértelmezett gombok
Az Enter és az Esc gomb: hogyan rendelhetsz az ablakhoz alapértelmezett és megszakító gombot.



