Grafikus C# – Windows Forms tanfolyam › 2.11 lecke

Windows Forms · 2.11 lecke

Kijelölés és a SelectionChanged esemény

Egy igazi szövegszerkesztő mindig megmutatja, hol áll a kurzor, és a gombjai követik a kijelölt szöveg formázását: ha félkövér szóra állsz, a félkövér gomb „benyomva” látszik. Ebben a leckében a MiniWordöt ilyen élő állapotjelzéssel egészítjük ki a SelectionChanged esemény segítségével.

  • kb. 70 perc
  • közepes szint
  • kurzor és formázás követése
Diák C# programozást gyakorol egy tanulósarokban

A lecke végére

  • érted, mikor fut le a SelectionChanged esemény;
  • kiírod a kurzor sorát, oszlopát és a kijelölés hosszát;
  • a formázógombok színe követi a kijelölt szöveg stílusát;
  • a betűtípus és méret listák is a kijelölést mutatják;
  • érted a visszacsatolás hibáját, és kivéded egy jelzőváltozóval (flag);
  • felismered a leggyakoribb hibákat.

Mielőtt elkezded

Ehhez a leckéhez kell a MiniWord a 2.10 lecke után, a ComboBox (2.5) és a mező fogalma (1.12 lecke).

2.10 MiniWord összeépítése →

Mikor fut le a SelectionChanged?

A RichTextBox SelectionChanged eseménye akkor fut le, amikor a kijelölés vagy a kurzor helye megváltozik: kattintáskor, a nyílbillentyűk használatakor, szöveg kijelölésekor és gépeléskor is (hiszen a kurzor ilyenkor továbblép). Ugyanúgy hozható létre, mint a többi esemény: Properties ablak, villám ikon (Events), dupla kattintás a SelectionChanged sorra.

TextChangedAkkor fut le, ha a szöveg változik.
SelectionChangedAkkor fut le, ha a kurzor vagy a kijelölés változik, a szöveg változása nélkül is.

Jósolj!

Egy szóra duplán kattintasz (kijelöli), majd megnyomod a jobbra nyilat. Hányszor fut le a SelectionChanged és a TextChanged?

Jóslat ellenőrzése

A SelectionChanged kétszer fut le (a duplakattintás kijelöléskor, majd a nyílbillentyűnél, mert a kijelölés megszűnik és a kurzor mozdul). A TextChanged egyszer sem, mert a szöveg nem változott.

Sor, oszlop és kijelölés kijelzése

Tegyél a MiniWord ablakának aljára egy lblAllapot feliratot, és írd meg a SelectionChanged eseménykezelőt:

Form1.csC#
private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
int kurzor = rtbDokumentum.SelectionStart;
int sor = rtbDokumentum.GetLineFromCharIndex(kurzor);
int oszlop = kurzor - rtbDokumentum.GetFirstCharIndexFromLine(sor);

lblAllapot.Text = $"Sor: {sor + 1}, Oszlop: {oszlop + 1}, Kijelölve: {rtbDokumentum.SelectionLength} karakter";
}
SelectionStartA kurzor (vagy a kijelölés kezdetének) sorszáma a teljes szövegben, 0-tól.
GetLineFromCharIndex(n)Megmondja, hányadik sorban van az n. karakter (0-tól számolva).
GetFirstCharIndexFromLine(sor)Megadja a sor első karakterének sorszámát a szövegben.
kurzor - sor elejeA különbség adja meg, hányadik karakternél áll a kurzor a soron belül.
+ 1A program 0-tól számol, az emberek 1-től: a kiírásnál mindig hozzáadunk egyet.
SelectionLengthA kijelölt karakterek száma.

A sor a képernyőn látható sor

A GetLineFromCharIndex a sorokat úgy számolja, ahogy a szövegdobozban megjelennek. Ha egy hosszú bekezdés az ablak szélén több sorba tör, a törés után új sornak számít. Ez a Wordhöz hasonló viselkedés, és a sorszám az ablak átméretezésekor megváltozhat.

Jósolj!

A szöveg: Alma Enter körte. A kurzor a „körte” szó „r” betűje előtt áll. Mit ír ki a felirat sorra és oszlopra?

Jóslat ellenőrzése

Sor: 2, Oszlop: 3. A „körte” a második sorban van, és az „r” előtt két karakter áll (k, ö), tehát a kurzor a harmadik oszlopban van. (A számlálás a belső 0-ról indul, ezért a kiírásnál az + 1 kell.)

📷 Képernyőkép helye

Mit tegyél ide: a MiniWord alsó sora a „Sor: 2, Oszlop: 3, Kijelölve: 0 karakter” felirattal, felette a szöveg és a kurzor.Javasolt fájlnév: winforms-2-11-allapotsor.webp · Alt szöveg: A MiniWord állapotsora a kurzor sorával és oszlopával

A formázógombok követik a kijelölést

A SelectionFont tulajdonságból kiolvashatjuk, hogy a kijelölt szöveg félkövér, dőlt vagy aláhúzott-e (2.4 lecke). A Font objektumnak vannak olvasható tulajdonságai: Bold, Italic, Underline, amelyek igaz/hamis értéket adnak. A gomb hátterét ennek megfelelően színezzük:

Form1.csC#
private void GombokFrissitese()
{
Font f = rtbDokumentum.SelectionFont;

if (f == null)
{
return;
}

btnFelkover.BackColor = f.Bold ? Color.LightSkyBlue : SystemColors.Control;
btnDolt.BackColor = f.Italic ? Color.LightSkyBlue : SystemColors.Control;
btnAlahuzott.BackColor = f.Underline ? Color.LightSkyBlue : SystemColors.Control;
}
f == nullVegyes kijelölésnél nincs egyetlen betűtípus, ilyenkor nem módosítjuk a gombokat (2.4 lecke).
f.Bold, f.Italic, f.UnderlineIgaz, ha a szöveg az adott stílusú.
feltétel ? igaz : hamisRövid forma az if–else helyett: ha a stílus igaz, világoskék a gomb, különben az alapszín.
SystemColors.ControlA Windows alapértelmezett gombszíne.

A SelectionChanged eseménykezelőben hívd meg a GombokFrissitese() metódust. A betűtípus- és méretlistákat is érdemes frissíteni, ehhez azonban vigyázni kell, ahogy a következő szakasz mutatja.

Jósolj!

Félkövér szóra kattintasz. Milyen színű lesz a félkövér (F) gomb? És a dőlt (D) gomb, ha a szó nem dőlt?

Jóslat ellenőrzése

Az F gomb világoskék lesz, a D gomb pedig az alapszínű, mert a Bold igaz, az Italic pedig hamis.

📷 Képernyőkép helye

Mit tegyél ide: a MiniWord felső sora, amelyben egy félkövér szóra kattintva az F gomb világoskék, a D és az A gomb pedig alapszínű.Javasolt fájlnév: winforms-2-11-gombok-kovetes.webp · Alt szöveg: A félkövér gomb kiemelve egy félkövér szó kijelölésekor

A visszacsatolás hibája

Szeretnénk, hogy a betűtípus- és méretlista is mutassa a kijelölés értékét. A kézenfekvő megoldás: a SelectionChanged eseményben beállítjuk a listák szövegét. De itt egy veszélyes hurok lappang:

A kijelölés változik→A SelectionChanged átállítja a listát→A lista SelectedIndexChanged eseménye lefut→Átállítja a kijelölt szöveg betűtípusát!

A lista átállítása kiváltja a SelectedIndexChanged eseményt (2.5 lecke), amely a kijelölt szövegre alkalmazza a listában látható betűtípust. Ha a kijelölés vegyes formázású vagy a lista értéke nem pontosan egyezik, a program véletlenül átformázhatja a szöveget, amelyre csak rákattintottál.

A megoldás egy jelzőváltozó (angolul flag): egy bool típusú mező, amely megmondja, hogy éppen a program frissíti-e a listát, és nem a felhasználó választ belőle.

Form1.csC#
private bool frissites = false;

private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
Font f = rtbDokumentum.SelectionFont;

if (f != null)
{
frissites = true;

cmbBetutipus.Text = f.FontFamily.Name;
cmbMeret.Text = f.Size.ToString();

frissites = false;
}

GombokFrissitese();
}

private void cmbBetutipus_SelectedIndexChanged(object sender, EventArgs e)
{
if (frissites)
{
return;
}

// ... a 2.5 leckében megírt kód ...
}

private void cmbMeret_SelectedIndexChanged(object sender, EventArgs e)
{
if (frissites)
{
return;
}

// ... a 2.5 leckében megírt kód ...
}
private bool frissites = false;Mező (1.12 lecke): megjegyzi, hogy éppen a program frissíti-e a listákat.
frissites = true; ... frissites = false;A listák átállítása előtt igazra, utána hamisra állítjuk. Közben a lista eseménykezelői tudják, hogy nem a felhasználó választott.
if (frissites) { return; }A lista eseménykezelőjében az első sor: ha a program frissít, azonnal kilépünk, és nem formázunk semmit.
f.FontFamily.NameA betűtípus neve szövegként (például Arial).

Gyakori minta

Ha egy vezérlő értékét a program állítja be, és a vezérlő eseménye ettől lefut, a jelzőváltozós védelem általános megoldás. Később más programoknál is találkozol vele.

Jósolj!

A frissites jelző nélkül egy Arial és egy Verdana szóból álló szövegben az Arial szóra kattintasz. Mi történhet a kijelölt szóval?

Jóslat ellenőrzése

A SelectionChanged átállítja a listát, amelynek SelectedIndexChanged eseménye lefut, és a kijelölésre alkalmazza a lista értékét. Ez a leírt esetben ugyanaz a betűtípus, tehát nem látszik változás, de vegyes méret vagy stílus esetén az átállítás módosíthatja a szöveg formázását. A jelzőváltozó megakadályozza.

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. Átformázódik a szöveg

Egy szóra kattintasz, és a szöveg formázása váratlanul megváltozik.

Ok és javítás

A lista átállítása kiváltja a SelectedIndexChanged eseményt, amely formáz. Használd a frissites jelzőváltozót, és a lista eseménykezelőinek elején lépj ki, ha igaz.

2. NullReferenceException

cmbBetutipus.Text = rtbDokumentum.SelectionFont.FontFamily.Name;
Hiba és javítás

Futás közbeni hiba, ha vegyes a kijelölés, mert a SelectionFont ilyenkor null. Mentsd el egy változóba, és ellenőrizd: if (f != null).

3. Az oszlop eggyel kevesebb

A szöveg elején álló kurzornál a felirat „Oszlop: 0” értéket mutat.

Ok és javítás

A program 0-tól számol, az emberek 1-től. A kiíráskor adj hozzá egyet: oszlop + 1, és ugyanígy a sorhoz is.

4. A felirat csak kattintásra frissül

A nyílbillentyűvel mozgatod a kurzort, de a sor és az oszlop nem változik.

Ok és javítás

A kódot valószínűleg a Click eseményhez kötötted, nem a SelectionChanged eseményhez. Billentyűvel mozgatásnál csak a SelectionChanged fut le.

5. A gombok színe nem változik vissza

if (f.Bold)
{
btnFelkover.BackColor = Color.LightSkyBlue;
}
Hiba és javítás

Rossz működés: a gomb kékre vált, de nem állítod vissza az alapszínre, amikor nem félkövér a szöveg. Adj hozzá else ágat, vagy használd a rövid feltétel ? igaz : hamis formát.

Mini projekt: állapotsor a MiniWordben

Építsd be a MiniWordbe az élő állapotjelzést:

  1. Az ablak aljára tedd az lblAllapot feliratot a sor, oszlop és kijelölés kiírásához.
  2. Add hozzá a GombokFrissitese() metódust, hogy az F, D, A gombok kövessék a stílust.
  3. A betűtípus és méret lista mutassa a kijelölés értékét a frissites jelzővel védve.
  4. Próbáld ki: kattints félkövér, dőlt, nagy és kis betűs szövegekre. Kattints vegyes kijelölésre is.
📷 Képernyőkép helye

Mit tegyél ide: a MiniWord egy félkövér, Verdana betűtípusú szóra kattintva: az F gomb világoskék, a betűtípus listában a Verdana látszik, az alsó sorban pedig a sor és az oszlop száma.Javasolt fájlnév: winforms-2-11-miniword-allapot.webp · Alt szöveg: A MiniWord a kurzor helyén álló szöveg formázását mutatja

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. Igazítás kijelzése

Az állapotsor végén jelenjen meg a kurzor bekezdésének igazítása is, például: Igazítás: Center.

Egy lehetséges megoldás
lblAllapot.Text = lblAllapot.Text + $", Igazítás: {rtbDokumentum.SelectionAlignment}";

Az igazítás (Left, Center, Right) felsorolás értéke. A sorkizárt bekezdéseket ez a tulajdonság nem ismeri fel.

2. Betűszín jelzése

Tegyél az állapotsorba egy pnlSzin kis panelt vagy feliratot, amelynek a háttere a kijelölt szöveg színe.

Egy lehetséges megoldás
pnlSzin.BackColor = rtbDokumentum.SelectionColor;

A SelectionColor a kijelölt szöveg betűszíne. A pnlSzin lehet egy kis Panel vezérlő. Vegyes színű kijelölésnél az érték nem határozható meg.

3. Kijelölt szó

Ha van kijelölt szöveg, az állapotsor mutassa meg az első 20 karakterét: Kijelölt: „alma körte”.

Egy lehetséges megoldás
string kijelolt = rtbDokumentum.SelectedText;

if (kijelolt.Length > 20)
{
kijelolt = kijelolt.Substring(0, 20) + "...";
}

if (kijelolt.Length > 0)
{
lblAllapot.Text = lblAllapot.Text + $" | Kijelölt: {kijelolt}";
}

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatokhoz a MiniWord rtbDokumentum RichTextBoxa és egy lblAllapot felirat kell. A SelectionChanged eseménykezelőbe kerül minden kód. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Hányadik sor?

Az állapotsor mutassa: Sor: 3 / 10, vagyis a kurzor sorát és az összes sor számát.

Megoldás megnyitása

A GetLineFromCharIndex a kurzor sorát adja (0-tól számolva), ezért adunk hozzá egyet. A sorok számához elég a Lines hossza, de a tördelt sorokat a GetLineFromCharIndex a végpozícióra is megadja.

Form1.csC#
private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
int sor = rtbDokumentum.GetLineFromCharIndex(rtbDokumentum.SelectionStart) + 1;
int osszes = rtbDokumentum.GetLineFromCharIndex(rtbDokumentum.TextLength) + 1;

lblAllapot.Text = $"Sor: {sor} / {osszes}";
}

2. Kijelölt karakterek

Ha van kijelölt szöveg, az állapotsor mutassa a hosszát: 12 karakter kijelölve. Ha nincs, legyen üres.

Megoldás megnyitása

A SelectionLength a kijelölt karakterek száma. Nulla esetén nincs kijelölés, ilyenkor üres szöveget írunk ki.

Form1.csC#
private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
if (rtbDokumentum.SelectionLength > 0)
{
lblAllapot.Text = $"{rtbDokumentum.SelectionLength} karakter kijelölve";
}
else
{
lblAllapot.Text = "";
}
}

Közepes szint

3. A félkövér gomb követi a kijelölést

A btnFelkover gomb háttere legyen világoskék, ha a kijelölt szöveg félkövér, egyébként az alapszín.

Megoldás megnyitása

A SelectionFont.Bold igaz, ha félkövér a szöveg. Vegyes kijelölésnél a SelectionFont null, ilyenkor nem módosítunk semmit.

Form1.csC#
private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
Font f = rtbDokumentum.SelectionFont;

if (f == null)
{
return;
}

btnFelkover.BackColor = f.Bold ? Color.LightSkyBlue : SystemColors.Control;
}

4. Védelem a visszacsatolás ellen

A cmbMeret lista mutassa a kijelölt szöveg méretét. Hogyan írod meg úgy, hogy a lista átállítása ne formázza át a szöveget?

Megoldás megnyitása

Egy bool jelzőváltozó megjegyzi, hogy a program frissít-e. Ha igen, a lista SelectedIndexChanged eseménye azonnal kilép.

Form1.csC#
private bool frissites = false;

private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
Font f = rtbDokumentum.SelectionFont;

if (f != null)
{
frissites = true;
cmbMeret.Text = f.Size.ToString();
frissites = false;
}
}

private void cmbMeret_SelectedIndexChanged(object sender, EventArgs e)
{
if (frissites)
{
return;
}

// a méretváltoztatás kódja
}

Kihívás szint

5. Betűtípus és méret az állapotsorban

Az állapotsor mutassa a kijelölt szöveg betűtípusát és méretét: Arial, 12 pt. Vegyes kijelölésnél: Vegyes formázás.

Megoldás megnyitása

A FontFamily.Name a betűcsalád neve, a Size a méret. A null érték vegyes kijelölést jelent.

Form1.csC#
private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
Font f = rtbDokumentum.SelectionFont;

if (f == null)
{
lblAllapot.Text = "Vegyes formázás";
}
else
{
lblAllapot.Text = $"{f.FontFamily.Name}, {f.Size} pt";
}
}

6. Hol tartasz a dokumentumban?

Az állapotsor mutassa százalékban, hogy a kurzor a dokumentum hányadik százalékánál áll: 42 %. Üres dokumentumnál ne legyen osztás nullával.

Megoldás megnyitása

A szorzást az osztás előtt végezzük (100 * hely / hossz), mert az egész számok osztása a tizedest levágja. A Math.Max(1, ...) megakadályozza, hogy a nevező nulla legyen.

Form1.csC#
private void rtbDokumentum_SelectionChanged(object sender, EventArgs e)
{
int hossz = Math.Max(1, rtbDokumentum.TextLength);
int szazalek = 100 * rtbDokumentum.SelectionStart / hossz;

lblAllapot.Text = szazalek + " %";
}

Gyors önellenőrzés

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

1. Mikor fut le a SelectionChanged?

Amikor a kurzor vagy a kijelölés helye megváltozik, a szöveg változása nélkül is.

2. Mi a különbség a TextChanged és a SelectionChanged között?

A TextChanged a szöveg, a SelectionChanged a kurzor és a kijelölés változásakor fut le.

3. Hogyan számolod ki a sort és az oszlopot?

A GetLineFromCharIndex adja a sort, az oszlop pedig a SelectionStart és a sor első karakterének (GetFirstCharIndexFromLine) különbsége. A kiíráskor mindkettőhöz +1 kell.

4. Mi a visszacsatolási hiba?

Ha a program átállítja egy vezérlő értékét, és ettől annak eseménye lefut, az pedig újabb módosítást végez. Így a program véletlenül átformázhatja a szöveget.

5. Mire való a jelzőváltozó (flag)?

Megmondja, hogy éppen a program frissíti-e a vezérlőt, és nem a felhasználó választott. Az eseménykezelő elején kilépünk, ha igaz.

6. Miért kell a SelectionFont null értékét ellenőrizni?

Vegyes formázású kijelölésnél nincs egyetlen betűtípus, ezért az érték null, és a használata hibát okozna.

Mit tudsz már?

  • Érted a SelectionChanged esemény működését.
  • Kiírod a kurzor sorát, oszlopát és a kijelölés hosszát.
  • A formázógombokat és listákat a kijelöléshez igazítod.
  • Jelzőváltozóval kivédöd a visszacsatolást.

Következő lecke: Undo, Redo, Copy, Cut és Paste

Az állapotjelzés kész. A következő leckében a szerkesztés alapműveleteit építjük be.