Grafikus C# – Windows Forms tanfolyam › 2.8 lecke

Windows Forms · 2.8 lecke

Szövegkeresés és az összes találat kiemelése

Egy szövegszerkesztőben a Ctrl+F keresés nélkülözhetetlen. Ebben a leckében a MiniWord megtanul keresni: először az első találatot kijelöli, majd az összes előfordulást sárgára színezi. Közben megismerjük az IndexOf metódust, a while ciklust és a kiemelés trükkjét.

  • kb. 75 perc
  • közepes szint
  • keresés és ciklus
Diáklány C# programot készít laptopon

A lecke végére

  • az IndexOf metódussal megkeresed egy szöveg helyét egy másik szövegben;
  • a Select() metódussal kijelölöd a találatot a szövegdobozban;
  • while ciklussal végigmész az összes találaton;
  • a SelectionBackColor segítségével sárgára színezed az összes előfordulást;
  • érted, miért kiemelés az „összes találat kijelölése”;
  • kezeled az üres keresőszöveget és a kis- és nagybetű kérdését.

Mielőtt elkezded

Ehhez a leckéhez kell a MiniWord a 2.7 lecke után, ismerned kell a Select() és a SelectionLength fogalmát (2.4), valamint az if–else szerkezetet. A while ciklust itt a lecke közben magyarázzuk el.

2.7 Sorkizárt igazítás →

A keresősáv

Tegyél a MiniWord ablakára három új vezérlőt:

txtKeresEgy TextBox: ide írja be a felhasználó a keresett szót.
btnKeresEgy gomb Keresés felirattal: az első találatot jelöli ki.
btnOsszesEgy gomb Összes kiemelése felirattal: az összes találatot sárgára színezi.
HideSelectionA RichTextBox HideSelection tulajdonságát állítsd False-ra (2.4 lecke), hogy a kijelölés a gombnyomás után is látszódjon.
📷 Képernyőkép helye

Mit tegyél ide: a MiniWord keresősávja a txtKeres beviteli mezővel és a két gombbal (Keresés, Összes kiemelése), alatta a szövegdobozzal.Javasolt fájlnév: winforms-2-8-keresosav.webp · Alt szöveg: A MiniWord keresősávja beviteli mezővel és két gombbal

Az IndexOf: hol van a keresett szöveg?

A string típusnak van egy IndexOf metódusa (ismerős lehet a C# alapokból). Megmondja, hogy a keresett szöveg hányadik karakternél kezdődik. A számozás 0-tól indul. Ha nincs találat, az eredmény -1.

PéldaC#
string szoveg = "Szia Peti, szia Anna!";
int hely = szoveg.IndexOf("szia", StringComparison.OrdinalIgnoreCase);
// hely értéke: 0 (a nagybetűs Szia a legelején van)
IndexOf(keresett, ...)A keresett szöveg kezdőpozíciója, vagy -1, ha nincs találat.
StringComparison.OrdinalIgnoreCaseKarakterről karakterre hasonlít, és nem tesz különbséget a kis- és nagybetű között. A StringComparison.Ordinal megkülönbözteti őket.

Jósolj!

A szöveg: Szia Peti, szia Anna!. Mi az IndexOf("Anna", ...) eredménye? És mi a IndexOf("Bence", ...) eredménye?

Jóslat ellenőrzése

Az „Anna” a 16. karakternél kezdődik (a számolás 0-tól indul: a „Szia Peti, szia ” 16 karakter). A „Bence” nem szerepel a szövegben, ezért az eredmény -1.

Miért Ordinal?

A magyar nyelvben vannak betűkapcsolatok (például cs, sz, zs), amelyekre a nyelvfüggő összehasonlítás másképp viselkedhet, és a visszaadott pozíció nem mindig egyezik a szövegdobozban lévő pozícióval. Az Ordinal és az OrdinalIgnoreCase mindig karakterről karakterre hasonlít, ezért biztonságos a keresésnél.

Az első találat kijelölése

Form1.csC#
private void btnKeres_Click(object sender, EventArgs e)
{
string keresett = txtKeres.Text;

if (keresett.Length == 0)
{
MessageBox.Show("Írd be, mit keressek!");
}
else
{
int hely = rtbDokumentum.Text.IndexOf(keresett, StringComparison.OrdinalIgnoreCase);

if (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.Focus();
}
else
{
MessageBox.Show("Nincs találat.");
}
}
}
keresett.Length == 0Üres keresőszöveg ellenőrzése. Ezt mindig meg kell csinálni a keresés előtt.
IndexOf(...)Megkeresi a találatot a dokumentum szövegében.
hely >= 0Akkor igaz, ha volt találat (a -1 azt jelenti, hogy nincs).
Select(hely, keresett.Length)Kijelöli a találatot: a kezdőpozíciótól a keresett szöveg hosszával.
📷 Képernyőkép helye

Mit tegyél ide: a MiniWord egy dokumentummal, amelyben a keresett szó első előfordulása kijelölve (kék háttér) látszik, mellette a txtKeres mezőben a keresett szó.Javasolt fájlnév: winforms-2-8-elso-talalat.webp · Alt szöveg: A MiniWord az első találatot kijelöli a szövegben

Új fogalom: a while ciklus

Az összes találat megkereséséhez ismételnünk kell a keresést, amíg van találat. Erre való a while (magyarul: „amíg”) ciklus: addig ismétli a kapcsos zárójelek közötti kódot, amíg a feltétel igaz.

Példa: számolás ciklussalC#
int szam = 1;

while (szam <= 3)
{
MessageBox.Show("Szám: " + szam);
szam = szam + 1;
}
while (feltétel)A feltétel igaz volta esetén a blokk lefut, utána a program újra megvizsgálja a feltételt.
szam = szam + 1A ciklus belsejében valamit változtatni kell, különben a feltétel sosem lesz hamis, és a ciklus végtelen.

Jósolj!

Hányszor jelenik meg az üzenetablak a fenti példában? Mik a számok?

Jóslat ellenőrzése

Háromszor: 1, 2 és 3. A negyedik körben a szam értéke 4, a szam <= 3 feltétel hamis, ezért a ciklus véget ér.

Az összes találat kiemelése

A RichTextBoxnak egyszerre csak egyetlen kijelölése lehet, ezért az „összes találat kijelölése” valójában kiemelés: minden találatot sárga háttérrel színezünk. A SelectionBackColor tulajdonság a kijelölt szöveg háttérszínét állítja.

Form1.csC#
private void btnOsszes_Click(object sender, EventArgs e)
{
string keresett = txtKeres.Text;

if (keresett.Length == 0)
{
MessageBox.Show("Írd be, mit keressek!");
}
else
{
TorolKiemeleseket();

string szoveg = rtbDokumentum.Text;
int darab = 0;
int hely = szoveg.IndexOf(keresett, 0, StringComparison.OrdinalIgnoreCase);

while (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.SelectionBackColor = Color.Yellow;
darab = darab + 1;

hely = szoveg.IndexOf(keresett, hely + keresett.Length, StringComparison.OrdinalIgnoreCase);
}

rtbDokumentum.Select(0, 0);
MessageBox.Show($"Találatok száma: {darab}");
}
}

private void TorolKiemeleseket()
{
rtbDokumentum.SelectAll();
rtbDokumentum.SelectionBackColor = rtbDokumentum.BackColor;
rtbDokumentum.Select(0, 0);
}
string szoveg = rtbDokumentum.Text;A dokumentum szövegét egyszer elmentjük egy változóba, hogy a ciklus ne kérje le minden körben újra.
while (hely >= 0)Addig fut a ciklus, amíg van találat.
hely + keresett.LengthA következő keresést a találat vége után kezdjük, így nem találjuk meg ugyanazt újra.
TorolKiemeleseket()Az előző keresés kiemeléseit eltünteti: az egész szöveg hátterét az alapszínre állítja.

A kiemelés a dokumentum része

A háttérszín formázás, ezért az RTF fájlba is belekerül. Ha mentés előtt nem törlöd a kiemeléseket, a mentett dokumentumban is sárga szavak lesznek. Ha a Color alá hullámvonal kerül, add a fájl elejéhez a using System.Drawing; sort.

📷 Képernyőkép helye

Mit tegyél ide: a MiniWord egy dokumentummal, amelyben a keresett szó minden előfordulása sárgával kiemelt. Mellette az üzenetablak a „Találatok száma: 4” szöveggel.Javasolt fájlnév: winforms-2-8-osszes-talalat.webp · Alt szöveg: A MiniWord a keresett szó összes előfordulását sárgával kiemeli

Jósolj, aztán futtasd!

Minden feladatnál először írd le, mit vársz, és csak utána próbáld ki.

1. Kis- és nagybetű

A dokumentumban ez áll: Alma alma ALMA. Az „alma” szóra keresel az Összes kiemelése gombbal. Hány találat lesz?

Jóslat ellenőrzése

3, mert az OrdinalIgnoreCase nem tesz különbséget a kis- és nagybetű között.

2. Átfedő találatok

A dokumentum: aaa. Az „aa” szóra keresel. Hány találat lesz?

Jóslat ellenőrzése

1: a program az első találat vége után folytatja a keresést, ezért az átfedő második „aa” nem számít.

3. Nincs találat

Olyan szót keresel, amely nincs a dokumentumban. Mit kapsz az Összes kiemelése gombra?

Jóslat ellenőrzése

A ciklus egyszer sem fut le, a darab értéke 0 marad, és az üzenet: Találatok száma: 0.

4. Két keresés egymás után

Először az „alma” szóra, utána a „körte” szóra keresel az Összes kiemelése gombbal. Mi látszik a második keresés után?

Jóslat ellenőrzése

Csak a „körte” találatai sárgák, mert a keresés elején a TorolKiemeleseket() eltünteti az előző kiemeléseket.

Hibakeresés: te vagy a hibadetektív

Hibavadászat

Jósold meg, hogy fordítási hiba, futás közbeni hiba vagy rossz működés lesz-e. Csak utána nyisd le a megoldást.

1. A program lefagy

Az Összes kiemelése gomb megnyomása után a MiniWord nem válaszol semmire, amikor a keresőmező üres.

Ok és javítás

Végtelen ciklus. Üres keresőszövegnél az IndexOf mindig az aktuális pozíciót adja vissza, a hely + 0 pedig sosem lép előre, ezért a while sosem ér véget. Az üres szöveg ellenőrzése ezért kötelező. Ha a program lefagyott, a Visual Studióban a Shift+F5 (Stop) paranccsal állíthatod le.

2. A ciklus nem lép tovább

while (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.SelectionBackColor = Color.Yellow;
darab = darab + 1;
}
Ok és javítás

Végtelen ciklus: a hely értéke a ciklusban sosem változik, ezért a feltétel mindig igaz marad. A következő találat helyét minden körben ki kell számolni: hely = szoveg.IndexOf(keresett, hely + keresett.Length, ...);

3. Nem kapok találatot

if (hely > 0)
{
rtbDokumentum.Select(hely, keresett.Length);
}
Ok és javítás

Rossz működés: a > 0 feltétel a dokumentum legelején lévő találatot (ahol a hely értéke 0) kihagyja. Javítás: hely >= 0, mert a -1 jelenti a találat hiányát.

4. Nem látszik a kijelölés

A Keresés gombra kattintasz, de a találat nincs kijelölve a szövegben.

Ok és javítás

A gomb átvette a fókuszt, és a RichTextBox elrejti a kijelölést. Állítsd a HideSelection tulajdonságot False-ra, és hívd meg a rtbDokumentum.Focus() metódust a kijelölés után.

5. Maradnak a régi sárga szavak

Új szót keresel, de a régi találatok továbbra is sárgák.

Ok és javítás

A kiemelések a dokumentum formázásának részei, nem tűnnek el maguktól. A keresés elején hívd meg a TorolKiemeleseket() metódust.

Mini projekt: a MiniWord keres

Építsd be a keresősávot a MiniWordbe, a leckében látott három metódussal (btnKeres_Click, btnOsszes_Click, TorolKiemeleseket). Tesztelj:

  1. Írj be egy többsoros szöveget, amelyben egy szó többször szerepel.
  2. Keress rá a Keresés gombbal: az első találat kijelölődik.
  3. Nyomd meg az Összes kiemelése gombot: minden előfordulás sárga lesz, és az üzenet megmutatja a számukat.
  4. Próbáld ki üres kereséssel és olyan szóval, amely nincs a szövegben.
  5. Mentés előtt töröld a kiemeléseket, hogy a mentett dokumentumban ne maradjon sárga háttér.
📷 Képernyőkép helye

Mit tegyél ide: a kész MiniWord kereséssel: a keresőmezőben egy szó, a dokumentumban a szó összes előfordulása sárgával kiemelve.Javasolt fájlnév: winforms-2-8-miniword-keres.webp · Alt szöveg: A kész MiniWord keresősávja és a kiemelt találatok

Önálló gyakorlófeladatok

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

1. Kis- és nagybetű megkülönböztetése

Tegyél az ablakra egy chkNagybetu nevű CheckBoxot Kis- és nagybetű megkülönböztetése felirattal. Ha be van jelölve, a keresés tegyen különbséget a kis- és a nagybetű között.

Egy lehetséges megoldás
StringComparison mod = StringComparison.OrdinalIgnoreCase;

if (chkNagybetu.Checked)
{
mod = StringComparison.Ordinal;
}

int hely = szoveg.IndexOf(keresett, 0, mod);

A mod változó tárolja az összehasonlítás módját, és ezt adjuk át az IndexOf-nak.

2. Következő találat

Készíts egy btnKovetkezo gombot, amely a kijelölés után következő találatra ugrik. Ha nincs több, kezdje elölről.

Egy lehetséges megoldás
string keresett = txtKeres.Text;
int kezdet = rtbDokumentum.SelectionStart + rtbDokumentum.SelectionLength;
int hely = rtbDokumentum.Text.IndexOf(keresett, kezdet, StringComparison.OrdinalIgnoreCase);

if (hely < 0)
{
hely = rtbDokumentum.Text.IndexOf(keresett, 0, StringComparison.OrdinalIgnoreCase);
}

if (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.Focus();
}

Az üres keresőszöveg ellenőrzését ide is tedd be, ahogy a leckében láttad.

3. Enterre keressen

Szeretnéd, ha az Enter lenyomására is elindulna a keresés. Hogyan oldanád meg a legegyszerűbben?

Egy lehetséges megoldás

Jelöld ki az ablakot (a Form üres részét), és a Properties ablakban állítsd az AcceptButton tulajdonságát btnKeres-re. Ekkor az Enter billentyű úgy működik, mintha a Keresés gombra kattintottál volna. A billentyűesemények részletes kezelését később, az 5. fejezetben tanuljuk.

Gyakorlótár: 6 további feladat

Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatokhoz egy rtbDokumentum RichTextBox (a HideSelection tulajdonsága False, hogy a kijelölés a fókusz elvesztése után is látsszon), egy txtKeres beviteli mező és egy gomb kell. Csak a próbálkozás után nyisd le a megoldást.

Alap szint

1. Az első találat kijelölése

A btnKeres gomb keresse meg a beírt szöveg első előfordulását, és jelölje ki a dokumentumban.

Megoldás megnyitása

Az IndexOf a keresett szöveg első helyét adja (-1, ha nincs). A Select(hely, hossz) kijelöli a szövegrészt.

Form1.csC#
private void btnKeres_Click(object sender, EventArgs e)
{
string keresett = txtKeres.Text;
int hely = rtbDokumentum.Text.IndexOf(keresett);

if (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.Focus();
}
}

2. Nincs találat

Ha a szöveg nem szerepel a dokumentumban, vagy a keresőmező üres, a program írjon ki érthető üzenetet.

Megoldás megnyitása

Az üres keresőszöveget előre ellenőrizzük: az IndexOf("") mindig 0-t adna. A -1 érték azt jelzi, hogy a szöveg nincs meg.

Form1.csC#
private void btnKeres_Click(object sender, EventArgs e)
{
string keresett = txtKeres.Text;

if (keresett.Length == 0)
{
MessageBox.Show("Írd be, mit keressek!");
return;
}

int hely = rtbDokumentum.Text.IndexOf(keresett);

if (hely < 0)
{
MessageBox.Show("A szöveg nem található.");
return;
}

rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.Focus();
}

Közepes szint

3. Kis- és nagybetű figyelmen kívül hagyása

A keresés ne tegyen különbséget a kis- és nagybetű között (alma megtalálja az Alma szót is).

Megoldás megnyitása

Az IndexOf második paramétere a keresés módja: a StringComparison.OrdinalIgnoreCase a kis- és nagybetűt nem különbözteti meg.

Form1.csC#
int hely = rtbDokumentum.Text.IndexOf(keresett, StringComparison.OrdinalIgnoreCase);

4. Az összes találat kiemelése

A gomb emelje ki sárga háttérrel a keresett szöveg összes előfordulását, és a lblTalalat mutassa a számukat.

Megoldás megnyitása

A while ciklus addig fut, amíg van találat. Minden körben a keresést az előző találat vége utáni helytől folytatjuk, különben ugyanazt találná újra. Az üres keresőszöveg végtelen ciklust okozna, ezért előbb ellenőrizzük.

Form1.csC#
private void btnOsszes_Click(object sender, EventArgs e)
{
string keresett = txtKeres.Text;

if (keresett.Length == 0)
{
return;
}

string szoveg = rtbDokumentum.Text;
int darab = 0;
int hely = szoveg.IndexOf(keresett, 0, StringComparison.OrdinalIgnoreCase);

while (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.SelectionBackColor = Color.Yellow;
darab = darab + 1;

hely = szoveg.IndexOf(keresett, hely + keresett.Length, StringComparison.OrdinalIgnoreCase);
}

rtbDokumentum.Select(0, 0);
lblTalalat.Text = $"Találatok száma: {darab}";
}

Kihívás szint

5. Következő találat

A btnKovetkezo gomb minden megnyomásra a következő előfordulást jelölje ki. Ha elfogytak, a program jelezze, és kezdje elölről.

Megoldás megnyitása

Az utolsó találat helyét mezőben tároljuk, hogy a gombnyomások között is megmaradjon. A keresés mindig az utolsó találat vége után folytatódik. Ha nincs több, a mezőt visszaállítjuk, és elölről kezdjük.

Form1.csC#
private int kovetkezoHely = 0;

private void btnKovetkezo_Click(object sender, EventArgs e)
{
string keresett = txtKeres.Text;

if (keresett.Length == 0)
{
return;
}

int hely = rtbDokumentum.Text.IndexOf(keresett, kovetkezoHely, StringComparison.OrdinalIgnoreCase);

if (hely < 0)
{
MessageBox.Show("Nincs több találat, a keresés elölről indul.");
kovetkezoHely = 0;
return;
}

rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.Focus();
kovetkezoHely = hely + keresett.Length;
}

6. Kiemelések törlése

A btnTorolKiemel gomb szüntesse meg az összes sárga kiemelést, és a találatok számát jelző felirat is ürüljön.

Megoldás megnyitása

A háttérszín a dokumentum formázása, ezért az egész szöveget kijelölve vissza kell állítani a szövegdoboz alapszínére. A kiemelés mentéskor a fájlba is bekerülne, ezért érdemes a mentés előtt is meghívni.

Form1.csC#
private void TorolKiemeleseket()
{
rtbDokumentum.SelectAll();
rtbDokumentum.SelectionBackColor = rtbDokumentum.BackColor;
rtbDokumentum.Select(0, 0);
}

private void btnTorolKiemel_Click(object sender, EventArgs e)
{
TorolKiemeleseket();
lblTalalat.Text = "";
}

Gyors önellenőrzés

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

1. Mit ad vissza az IndexOf, ha nincs találat?

-1-et.

2. Miért kiemelés az „összes találat kijelölése”?

Mert a RichTextBoxnak egyszerre csak egy kijelölése lehet. A többi találatot a háttérszínük megváltoztatásával tesszük láthatóvá.

3. Mire való a while ciklus?

Ismétli a kódot, amíg a feltétel igaz.

4. Miért kell ellenőrizni az üres keresőszöveget?

Mert üres szövegnél a hely sosem lép előre, ezért a ciklus végtelen lenne, és a program lefagyna.

5. Miért haladunk a hely + keresett.Length pozícióra?

Hogy a következő keresés a találat vége után induljon, és ne ugyanazt találja újra meg.

6. Miért kell törölni a kiemeléseket mentés előtt?

Mert a háttérszín a dokumentum formázásának része, így az RTF fájlba is belekerül.

Mit tudsz már?

  • Az IndexOf segítségével megtalálod a szöveg helyét.
  • A Select() metódussal kijelölöd a találatot.
  • A while ciklussal végigmész az összes találaton.
  • A SelectionBackColor tulajdonsággal kiemeled az összes előfordulást.
  • Kezeled az üres keresőszöveget és a kiemelések törlését.

Következő lecke: névmező, felugró ablak és About

A keresés kész. A következő leckében a MiniWord egy névmezőt és egy About ablakot kap.