Grafikus C# – Windows Forms tanfolyam › 2.1 lecke

Windows Forms · 2.1 lecke

RichTextBox és TextChanged – élő karakterszámláló

A tanfolyam második fejezetében egy Word-szerű szövegszerkesztőt építünk, a MiniWordöt. Most az első lépést tesszük meg: egy szövegmezőt és egy számlálót, amely mindig megmutatja, hány karakter van a dokumentumban. Közben megismered az ablakos programok legfontosabb gondolatát is: a program eseményekre reagál.

  • kb. 60–75 perc
  • kezdő szint
  • RichTextBox és események
Diáklány C# programot készít laptopon

A lecke végére

  • létrehozod a MiniWord nevű Windows Forms projektet;
  • elhelyezel egy RichTextBox-ot és egy Label-t, és beszédes nevet adsz nekik;
  • érted, mi az esemény és mi az eseménykezelő;
  • a TextChanged eseménnyel élő karakterszámlálót készítesz;
  • tudod, miben különbözik a Text és az Rtf;
  • tudod, hogyan számolja a program a sortöréseket.

Mielőtt elkezded

Erre a leckére akkor állsz készen, ha már készítettél első Windows Forms projektet, tudsz vezérlőt elhelyezni az ablakon, és ismered a string hosszát (Length), valamint a szöveginterpolációt.

TextBox, Button, Label, MessageBox →

Szöveg hossza és indexelés →

Új projekt: MiniWord

Ebben a fejezetben egyetlen programot építünk lépésről lépésre: egy Word-szerű szövegszerkesztőt. A következő leckék mindig ugyanezt a projektet bővítik, ezért érdemes külön, jól elnevezett projektet nyitni.

  1. Visual Studio → Create a new project, és válaszd a Windows Forms App sablont. A .NET Framework és az újabb .NET változat is jó ehhez a leckéhez.
  2. A projekt neve legyen MiniWord, majd kattints a Create gombra.
  3. Kattints az üres ablakra, és a Properties ablakban írd át a Text tulajdonságot MiniWord-re. Ez lesz az ablak címsora.
📷 Képernyőkép helye

Mit tegyél ide: a Visual Studio „Create a new project” ablaka, benne a Windows Forms App sablon kijelölve (a keresőmezőbe írd be: Windows Forms). Ha kényelmes, tegyél mellé egy második képet is a „Configure your new project” lépésről, a Project name mezőben a MiniWord névvel.Javasolt fájlnév: winforms-2-1-uj-projekt.webp · Alt szöveg: Új Windows Forms projekt létrehozása MiniWord néven a Visual Studióban

Te jössz!

Hozd létre a projektet, írd át az ablak címét, majd futtasd az F5 billentyűvel. Egy üres, MiniWord feliratú ablaknak kell megjelennie. Zárd be, és térj vissza a Visual Studióba.

Két vezérlő: RichTextBox és Label

Húzz a Toolboxból (View → Toolbox) egy RichTextBox-ot az ablakra, és alá egy Label-t. A RichTextBox a Common Controls csoportban található; ha nem látod, használd a Toolbox keresőmezőjét.

Miért nem a már ismert TextBox kell? A TextBox az egész szöveget egyetlen formátumban jeleníti meg. A RichTextBox formázott szöveget is kezel: az egyik szó lehet félkövér, a másik dőlt, a harmadik nagyobb betűméretű. Ráadásul RTF fájlokat is meg tud nyitni és el tud menteni. A szövegszerkesztőnknek pontosan erre van szüksége.

Állítsd be a Properties ablakban a két vezérlő tulajdonságait:

RichTextBox(Name): rtbDokumentum
Anchor: Top, Bottom, Left, Right
Label(Name): lblKarakterszam
Text: Karakterek: 0
Anchor: Bottom, Left
(Name)A vezérlő neve a kódban. Ezen a néven hivatkozol rá.
TextA felirat vagy szöveg, amelyet a felhasználó lát az ablakon.

Az Anchor azt adja meg, melyik ablakszélhez „kapcsolódjon” a vezérlő, amikor átméretezed az ablakot. A szövegdoboz így együtt nő az ablakkal, a számláló pedig az alsó sarokban marad. A részletekről a 6.2 leckében lesz szó.

📷 Képernyőkép helye

Mit tegyél ide: a Form a Designerben. A nagy RichTextBox kitölti az ablak nagy részét, alatta a Label „Karakterek: 0” felirattal. A kép mellett vagy külön képen látszódjon a Properties ablak is: a (Name) mezőben az rtbDokumentum érték, és az Anchor legördülő listában mind a négy oldal kijelölve.Javasolt fájlnév: winforms-2-1-felulet.webp · Alt szöveg: A MiniWord ablak a Visual Studio tervezőjében RichTextBoxszal és karakterszámláló címkével

Miért rtbDokumentum és lblKarakterszam?

A név elé rövid előtagot teszünk: rtb = RichTextBox, lbl = Label. Később több tucat vezérlő lesz az ablakon, és az előtagból azonnal látod, melyik milyen típusú. A (Name) és a Text két külön tulajdonság: az egyik a programozónak, a másik a felhasználónak szól. A leckék kódjai az itt megadott nevekkel működnek.

Mi az esemény?

A konzolos programban a program döntötte el, mikor jön a felhasználó: a Console.ReadLine() megállt, és várt. Az ablakos program fordítva működik. Az ablak nyitva van, a felhasználó pedig bármikor bármit csinálhat: kattinthat, gépelhet, törölhet. Ezeket nevezzük eseményeknek.

Olyan ez, mint a csengő. Nem állsz folyamatosan az ajtónál, hanem akkor mész oda, amikor megszólal. Az eseménykezelő az a metódus, amely akkor fut le, amikor az esemény bekövetkezik. A metódus kódját mi írjuk meg.

A szövegdobozunk TextChanged eseménye minden alkalommal jelez, amikor a szöveg megváltozik. Így hozhatod létre az eseménykezelőt:

  1. Jelöld ki a RichTextBoxot a Designerben.
  2. A Properties ablakban kattints a villám ikonra (Events).
  3. Keresd meg a TextChanged sort, és kattints duplán az üres mezőjére.

A Visual Studio elkészíti az üres eseménykezelőt, és megnyitja a Form1.cs fájlt:

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
// Ide írjuk a kódot. Ez fut le minden szövegváltozáskor.
}
rtbDokumentum_TextChangedA metódus neve: a vezérlő neve, aláhúzás, majd az esemény neve.
object senderAz a vezérlő, amelyik az eseményt kiváltotta. Egyelőre nem használjuk.
EventArgs eAz eseményhez tartozó további adatok. Ezt sem használjuk még.
{ }A kapcsos zárójelek közé kerül az a kód, amelynek az esemény bekövetkezésekor le kell futnia.
📷 Képernyőkép helye

Mit tegyél ide: a Properties ablak Events nézete (villám ikon) a TextChanged sorral, amelynek mezőjére duplán kattintanál. Ha tudsz, mutasd mellette a Form1.cs-t is, amelyben már megjelent az üres rtbDokumentum_TextChanged metódus.Javasolt fájlnév: winforms-2-1-textchanged-esemeny.webp · Alt szöveg: A TextChanged esemény létrehozása a Visual Studio Properties ablakában

Ne töröld kézzel az eseménykezelőt!

A Visual Studio a háttérben a Form1.Designer.cs fájlban összeköti az eseményt a metódussal. Ha csak a metódust törlöd, a program le sem fordul. Erről a hibavadászatnál lesz szó.

A karakterszámláló

Most már csak azt kell megmondanunk, mi történjen szövegváltozáskor: számoljuk meg a karaktereket, és írjuk ki az eredményt a címkére.

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
}
rtbDokumentum.TextA szövegdoboz teljes szövege string típusként.
.LengthA string hossza: a karakterek száma a szóközökkel és sortörésekkel együtt.
$"...{ }"Szöveginterpoláció: a kapcsos zárójelben lévő érték bekerül a mondatba.
lblKarakterszam.Text =Beállítja a címke új feliratát, ezért látjuk a változást az ablakon.

Jósolj, mielőtt futtatod!

Beírod a Szia szót, majd két szóközt. Mit fog mutatni a számláló?

Miért 6?

A Szia négy betű, a két szóköz is karakternek számít, ezért a Length értéke 6, és a címke Karakterek: 6 lesz.

📷 Képernyőkép helye

Mit tegyél ide: a futó MiniWord ablak. A RichTextBoxban a „Szia” szó két szóközzel a végén, az ablak alján pedig a „Karakterek: 6” felirat.Javasolt fájlnév: winforms-2-1-karakterszamlalo-fut.webp · Alt szöveg: A futó MiniWord program, amely hat karaktert számol a szövegdobozban

Működik!

Az eseménykezelő minden szövegváltozáskor lefut: gépeléskor, törléskor, beillesztéskor is. A számláló ezért mindig naprakész.

Text vagy Rtf?

A RichTextBox kétféleképp adja vissza a tartalmát:

TextCsak a látható szöveg, formázás nélkül. Ezt számoltuk meg.
RtfA szöveg és a hozzá tartozó formázási jelölések együtt. Az RTF a Rich Text Format rövidítése, a formázott szöveg fájlformátuma.

A formázási jelölések egy része így néz ki: {\rtf1\ansi ...}. Ezeket a Word is megérti, a felhasználónak viszont nem szabad látnia őket. Mentéskor az Rtf fog kelleni, a karakterszámláló viszont a Text-et használja.

Kísérlet

Írd át ideiglenesen a számláló sorát úgy, hogy rtbDokumentum.Rtf.Length szerepeljen a Text.Length helyén. Mit mutat üres dokumentumnál? 0-t?

Mi a tapasztalat?

Nem 0-t. Az Rtf már az üres dokumentumnál is tartalmazza a formázási jelöléseket, ezért az érték hosszú. A karakterszámlálóhoz tehát a Text.Length a jó megoldás. A kísérlet után állítsd vissza a kódot!

Mi számít karakternek?

Minden karakter számít: betű, szám, szóköz, írásjel és a sortörés is. Az Enter a RichTextBox.Text értékében egyetlen sortörés-karakter (\n). A sima TextBox ezzel szemben két karaktert használ (\r\n). Ez az egyik apró különbség a két vezérlő között.

Jósolj, aztán próbáld ki!

Beírod, hogy ab, megnyomod az Entert, majd beírod, hogy cd. Hány karaktert mutat a számláló?

Jóslat ellenőrzése

5: két betű, az Enter sortörése, majd még két betű. Ha mást látsz, ellenőrizd, hogy a Text.Length-et használod-e.

Jósolj, aztán futtasd!

Minden feladatnál először írd le a várható eredményt, és csak utána próbáld ki a programban.

1. Három betű és két szóköz

Beírod, hogy abc, majd két szóközt. Mit mutat a számláló?

Jóslat ellenőrzése

Karakterek: 5. A betűk és a szóközök is karakternek számítanak.

2. Két sor

Beírod: Szia, Enter, Peti. Hány karaktert mutat?

Jóslat ellenőrzése

Karakterek: 9: 4 + 1 sortörés + 4. Az Enter egy karakter a RichTextBox.Text értékében.

3. Törlés gomb

Az ablakon van egy btnTorles gomb. Mit mutat a számláló, miután megnyomod?

private void btnTorles_Click(object sender, EventArgs e)
{
rtbDokumentum.Clear();
}
Jóslat ellenőrzése

Karakterek: 0. A Clear() megváltoztatja a szöveget, ezért a TextChanged esemény ilyenkor is lefut, és a számláló automatikusan frissül.

Hibakeresés: te vagy a hibadetektív

Hibavadászat

Először jósold meg, hogy a hiba fordításkor, futás közben vagy a működésben jelentkezik. Csak utána nyisd le a megoldást.

1. Szám a szöveg helyén

lblKarakterszam.Text = rtbDokumentum.Text.Length;
Hiba és javítás

Fordítási hiba (CS0029): a Label.Text szöveget vár, a Length viszont int. Javítás: lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";

2. Nem történik semmi

// A metódust kézzel gépelted be a Form1.cs-be
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
}
Hiba és javítás

A program lefut, de a számláló nem változik: a metódus nincs összekötve az eseménnyel. A név önmagában nem elég. Jelöld ki a RichTextBoxot, nyisd meg az Events nézetet, és a TextChanged sor legördülő listájából válaszd ki a metódust. A legegyszerűbb: töröld a kézzel írt metódust, és dupla kattintással generáltasd újra.

3. Ismeretlen név

lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
// hibaüzenet: The name 'lblKarakterszam' does not exist in the current context
Hiba és javítás

Fordítási hiba (CS0103). A kód rendben van, de a címke (Name) tulajdonsága még label1. Írd át a Properties ablakban lblKarakterszam-ra. A kódban szereplő név és a (Name) tulajdonság pontosan egyezzen, a kis- és nagybetűket is beleértve.

4. Törölt eseménykezelő

Kitörölted a rtbDokumentum_TextChanged metódust a Form1.cs fájlból, a program pedig nem fordul le.

// Form1.Designer.cs – hibaüzenet (CS1061):
// 'Form1' does not contain a definition for 'rtbDokumentum_TextChanged'
Hiba és javítás

A Form1.Designer.cs még feliratkozik az eseményre, de a metódus már nincs meg. Két megoldás van: létrehozod újra az eseménykezelőt (dupla kattintás a TextChanged sorra), vagy az Events nézetben törlöd a TextChanged sor tartalmát, ezzel megszűnik a feliratkozás.

Mini projekt: a MiniWord alapja

Állítsd össze a MiniWord első változatát: egy átméretezhető ablak, amelyben a RichTextBox kitölti a helyet, alul pedig mindig látszik az aktuális karakterszám. Először próbáld meg önállóan megírni, és csak utána nyisd meg a megoldást.

Megoldás és ellenőrzés megnyitása
Form1.csC#
using System;
using System.Windows.Forms;

namespace MiniWord
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
}
}
}

A fájl tetején lévő using sorokat a Visual Studio készíti el. Ezek teszik elérhetővé a Windows Forms osztályait. A InitializeComponent() hívás építi fel az ablakot a Designerben beállított vezérlőkből.

📷 Képernyőkép helye

Mit tegyél ide: a kész MiniWord alapváltozat futás közben. A címsorban „MiniWord”, a szövegdobozban néhány mondat, alul a hozzá tartozó „Karakterek: …” felirat. Érdemes az ablakot kicsit átméretezni, hogy látszódjon, hogy a szövegdoboz együtt nő vele.Javasolt fájlnév: winforms-2-1-miniword-kesz.webp · Alt szöveg: A kész MiniWord alapváltozat szöveggel és karakterszámlálóval

Ne csak futtasd: próbáld ki hosszabb szöveggel is, majd méretezd át az ablakot, és figyeld meg, hogyan viselkedik a két vezérlő.

Önálló gyakorlófeladatok

Először készíts saját megoldást. A lenyitható rész csak ellenőrzéshez van.

1. Törlés gomb

Tegyél az ablakra egy btnTorles nevű, Törlés feliratú gombot, amely kitörli a dokumentum teljes szövegét. A számláló magától nullázódjon.

Egy lehetséges megoldás
private void btnTorles_Click(object sender, EventArgs e)
{
rtbDokumentum.Clear();
}

2. Karakterszám a címsorban

Az ablak címsora is mutassa a karakterek számát, például így: MiniWord – 12 karakter.

Egy lehetséges megoldás
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
this.Text = $"MiniWord – {rtbDokumentum.Text.Length} karakter";
}

A this az ablakot (Form1) jelenti, tehát a this.Text az ablak címsora.

3. Színjelzés

Ha a dokumentum 100 karakternél hosszabb, a számláló felirata legyen piros, különben fekete. Használj if–else szerkezetet.

Egy lehetséges megoldás
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
int darab = rtbDokumentum.Text.Length;
lblKarakterszam.Text = $"Karakterek: {darab}";

if (darab > 100)
{
lblKarakterszam.ForeColor = Color.Red;
}
else
{
lblKarakterszam.ForeColor = Color.Black;
}
}

Ha a Color alá piros hullámvonal kerül, add a fájl elejéhez a using System.Drawing; sort.

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatokhoz egy rtbDokumentum RichTextBox és a leírt feliratok kellenek. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Élő karakterszámláló

Gépelés közben a lblKarakterszam felirat mutassa: Karakterek: 12.

Megoldás megnyitása

A TextChanged esemény minden módosításkor lefut. A Text.Length a karakterek száma.

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
}

2. Sorok száma

Egy lblSorok felirat mutassa a szöveg sorainak (bekezdéseinek) számát, gépelés közben is.

Megoldás megnyitása

A Lines tulajdonság a sorok tömbje, a Length a darabszámuk. Egy sor akkor új, ha Entert nyomtál: a képernyőn tördelt sorokat nem számolja külön.

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblSorok.Text = $"Sorok: {rtbDokumentum.Lines.Length}";
}

Közepes szint

3. Szavak száma

A lblSzavak felirat mutassa a szavak számát. Szónak számít minden, amit szóköz választ el, az üres részek nem.

Megoldás megnyitása

A Split a szöveget a megadott karakter mentén darabolja. A StringSplitOptions.RemoveEmptyEntries kihagyja az üres darabokat (például a dupla szóközök között). A sorokat egyenként dolgozzuk fel, így az Enterrel elválasztott szavak is külön számítanak.

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
int szavak = 0;

foreach (string sor in rtbDokumentum.Lines)
{
szavak = szavak + sor.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length;
}

lblSzavak.Text = $"Szavak: {szavak}";
}

4. Figyelmeztető limit

A karakterszámláló legyen zöld 200 karakterig, 200 felett piros, és mutassa ki a túllépést is: Karakterek: 215 (15 felett).

Megoldás megnyitása

A szín a hossz és a limit összehasonlításától függ. A túllépés a hossz és a limit különbsége.

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
int hossz = rtbDokumentum.Text.Length;

if (hossz <= 200)
{
lblKarakterszam.Text = $"Karakterek: {hossz}";
lblKarakterszam.ForeColor = Color.SeaGreen;
}
else
{
lblKarakterszam.Text = $"Karakterek: {hossz} ({hossz - 200} felett)";
lblKarakterszam.ForeColor = Color.Firebrick;
}
}

Kihívás szint

5. Szóköz nélküli karakterszám

A lblSzokozNelkul felirat mutassa a karakterek számát a szóközök nélkül.

Megoldás megnyitása

A Replace(" ", "") minden szóközt törölne a szövegből, így a hossza a szóköz nélküli karakterszám. A sortörések (Enter) ezt a számot is növelik: ha ezeket sem akarod számolni, soronként kell végigmenni (lásd a szavak számolását).

Form1.csC#
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
int szokozNelkul = rtbDokumentum.Text.Replace(" ", "").Length;
lblSzokozNelkul.Text = $"Szóköz nélkül: {szokozNelkul}";
}

6. Statisztikai panel

Építs egy Statisztika() metódust, amely egyszerre frissíti a karakterek, szavak és sorok számát egy lblStat feliratban: Karakterek: 25 | Szavak: 4 | Sorok: 2. A TextChanged csak ezt hívja.

Megoldás megnyitása

A számításokat külön metódusba tesszük, hogy az eseménykezelő rövid maradjon, és a statisztikát bárhonnan frissíthessük (például fájl megnyitása után).

Form1.csC#
private void Statisztika()
{
int karakterek = rtbDokumentum.Text.Length;
int sorok = rtbDokumentum.Lines.Length;
int szavak = 0;

foreach (string sor in rtbDokumentum.Lines)
{
szavak = szavak + sor.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length;
}

lblStat.Text = $"Karakterek: {karakterek} | Szavak: {szavak} | Sorok: {sorok}";
}

private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
Statisztika();
}

Gyors önellenőrzés

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

1. Miben különbözik a RichTextBox a TextBoxtól?

A RichTextBox formázott szöveget kezel: a szöveg egyes részei különböző formátumúak lehetnek, és RTF fájlt is meg tud nyitni, el tud menteni. A TextBox az egész szöveget egy formátumban jeleníti meg.

2. Mi az esemény, és mi az eseménykezelő?

Az esemény valami, ami történik az ablakon, például szövegváltozás vagy kattintás. Az eseménykezelő az a metódus, amely az esemény bekövetkezésekor lefut.

3. Mikor fut le a TextChanged eseménykezelő?

Minden alkalommal, amikor a szöveg megváltozik: gépeléskor, törléskor, beillesztéskor és a Clear() hívásakor is.

4. Miért adunk beszédes nevet a vezérlőknek?

A kódban a (Name) tulajdonsággal hivatkozunk a vezérlőre. Az rtbDokumentum és az lblKarakterszam névből azonnal látszik, milyen vezérlőről van szó, és mire használjuk.

5. Miben különbözik a Text és az Rtf?

A Text csak a látható szöveg, az Rtf a szöveget és a formázási jelöléseket együtt tartalmazza. A karakterek megszámolásához a Text.Length kell.

6. Hol találod meg a vezérlők eseményeit a Visual Studióban?

A Properties ablakban, a villám (Events) ikonra kattintva. Az esemény melletti mezőre duplán kattintva a Visual Studio létrehozza az eseménykezelőt.

Mit tudsz már?

  • Létrehoztál egy Windows Forms projektet MiniWord néven.
  • Elhelyeztél és elneveztél egy RichTextBoxot és egy Labelt.
  • Érted az eseményvezérelt működés alapgondolatát.
  • A TextChanged eseménnyel élő karakterszámlálót készítettél.
  • Tudod, miben különbözik a Text és az Rtf.
  • Tudod, hogy a sortörés is karakternek számít.

Következő lecke: fájl megnyitása

A MiniWord már számol, de egyelőre csak a saját szövegét ismeri. A következő leckében megtanítjuk RTF és TXT fájlokat megnyitni az OpenFileDialog segítségével.