Grafikus C# – Windows Forms tanfolyam › 2.16 lecke Windows Forms · 2.16 lecke · záró projekt A 2. fejezet minden funkciója megvan: megnyitás és mentés, formázás, igazítás, keresés, szerkesztés, vágólap, védelem a mentetlen munka ellen és nyomtatás. Most egyetlen, rendezett programmá fésüljük össze őket. Ez a lecke a legnehezebb részekre koncentrál: arra, hogy a különböző leckékből származó kódok ne ütközzenek egymással.Mini projekt: jegyzettömb és szövegszerkesztő

A lecke végére
- a 2.1–2.15 leckék kódját egyetlen projektben fésülöd össze;
- megtalálod és megoldod a gyakori ütközéseket (kétszer definiált metódusok, többször használt események);
- a Mentés és a Mentés másként funkciót szétválasztod (aktuális fájl megjegyzése);
- a címsor a fájl nevét és a módosítást is mutatja;
- tesztlistával végigellenőrzöd a teljes programot;
- önértékelő szempontok alapján eldöntöd, hogy kész-e a projekt.
Mielőtt elkezded
Ehhez a leckéhez a 2.1–2.15 leckék kódjára van szükséged. Ha valamelyik rész hiányzik, vagy hibás, előbb azt javítsd ki.
A funkciótérkép
Az összefésülés előtt nézd át, mit tud a program, és melyik lecke adta hozzá:
| Funkció | Fontosabb metódusok és események | Lecke |
|---|---|---|
| Karakterszámláló | rtbDokumentum_TextChanged | 2.1 |
| Megnyitás (RTF, TXT) | Megnyitas() | 2.2 |
| Mentés RTF-ként | Mentes() | 2.3, 2.14 |
| Félkövér, dőlt, aláhúzott | StilusValt() | 2.4 |
| Betűtípus és méret | cmbBetutipus_…, cmbMeret_… | 2.5 |
| Igazítás (balra, középre, jobbra, sorkizárt) | btnBalra_Click és társai, RichTextBoxHelper | 2.6, 2.7 |
| Keresés és kiemelés | btnKeres_Click, btnOsszes_Click, TorolKiemeleseket() | 2.8 |
| Névmező és About | btnNev_Click, miAbout_Click | 2.9 |
| Állapotsor, gombok követése | rtbDokumentum_SelectionChanged, GombokFrissitese() | 2.11 |
| Szerkesztés menü | miSzerkesztes_DropDownOpening és a Click eseménykezelők | 2.12, 2.13 |
| Védelem a mentetlen munka ellen | Modositva(), Mentve(), FolytathatE(), Form1_FormClosing | 2.14 |
| Nyomtatás | pdDokumentum_BeginPrint, pdDokumentum_PrintPage | 2.15 |
A fájl szerkezete
A Form1.cs fájl tetején állnak a mezők (valtozott, aktualisFajl, frissites, nyomtatandoSzoveg, pozicio), utána a segédmetódusok, végül az eseménykezelők. A hosszú fájlban a // ---- kezdetű megjegyzések (például // ---- Mentés ----) segítenek eligazodni.
Gyakori ütközések az összefésüléskor
Ha a különböző leckék kódját egymás alá másolod, ugyanabból az eseménykezelőből több változat kerülhet a fájlba. A C# egy osztályban nem enged két azonos nevű metódust (CS0111), ezért az ilyen kódokat egyesíteni kell. A három leggyakoribb eset:
A Form1_Load
Több leckében is volt Form1_Load (sorkizárt, súgók, kezdőállapot). A program csak egyet tartalmazhat, amelynek belseje az összes feladatot elvégzi:
private void Form1_Load(object sender, EventArgs e)
{
// 2.7: sorkizárt támogatás
RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum);
// 2.9, 1.11: súgók
ttSugo.SetToolTip(txtKeres, "Írd be a keresett szót.");
ttSugo.SetToolTip(txtTanar, "A tanár neve az About ablakhoz.");
// 2.14: tiszta kezdőállapot
Mentve();
}A TextChanged
A karakterszámláló (2.1) és a módosításjelző (2.14) ugyanahhoz az eseményhez tartozik:
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
Modositva();
}A SelectionChanged
Az állapotsor (2.11) az egyetlen rtbDokumentum_SelectionChanged eseménykezelőben él. Ha egy másik leckéből is létrejött egy ilyen eseménykezelő, a tartalmukat vond össze, és a feleslegeset töröld. A formázó gombok (StilusValt, igazítás, betűtípus és méret) legvégén hívd meg a Modositva(); metódust, hogy a formázás is módosításnak számítson (2.14).
Hogyan keresd meg az ütközést?
A Visual Studio a fordításkor a CS0111 hibát mutatja, és megmondja a duplán definiált metódus nevét. Az Error List ablakban a hibára duplán kattintva a kódra ugrik. Töröld az egyiket, a tartalmát pedig tedd át a másikba. Ugyanez a helyzet, ha az ablakot a tervezőben lévő dupla kattintással újra elindítod, és új, üres eseménykezelő jön létre: a régi mellett kétszer lenne meg.
Mentés és Mentés másként
A 2.3 és a 2.14 leckében a Mentes() mindig megkérdezte a fájl nevét. Egy igazi szövegszerkesztő viszont megjegyzi, melyik fájlon dolgozol, és a Mentés csak felülírja azt. A névkérdést a Mentés másként teszi fel. Ehhez egy új mezőre (aktualisFajl) és három egyszerű metódusra van szükség:
private string aktualisFajl = "";
// A tényleges kiírás. Igaz, ha sikerült.
private bool FajlbaIr(string ut)
{
try
{
rtbDokumentum.SaveFile(ut, RichTextBoxStreamType.RichText);
aktualisFajl = ut;
Mentve();
return true;
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült menteni a fájlt: " + ex.Message);
return false;
}
}
// Mentés másként: mindig megkérdezi a nevet.
private bool MentesMaskent()
{
sfdMentes.Title = "Dokumentum mentése";
sfdMentes.Filter = "RTF dokumentum (*.rtf)|*.rtf";
sfdMentes.DefaultExt = "rtf";
sfdMentes.AddExtension = true;
if (sfdMentes.ShowDialog() != DialogResult.OK)
{
return false;
}
return FajlbaIr(sfdMentes.FileName);
}
// Mentés: ha már van fájlnév, felülírja, különben a Mentés másként.
private bool Mentes()
{
if (aktualisFajl.Length == 0)
{
return MentesMaskent();
}
return FajlbaIr(aktualisFajl);
}aktualisFajl üres), a Mentés másként fut le. Ha van, azonnal ment.FolytathatE() továbbra is a Mentes() eredményét használja.A Megnyitas() metódusban is jegyezd meg a fájlt, a betöltés után: aktualisFajl = fajl; Az Új menüpontban pedig állítsd vissza üresre: aktualisFajl = ""; A File menübe tegyél két külön menüpontot (miMentes és miMentesMaskent), amelyek a Mentes(), illetve a MentesMaskent() metódust hívják.
Jósolj!
Megnyitsz egy level.rtf fájlt, módosítasz benne egy szót, és a Mentés menüpontot választod. Megjelenik a mentés ablak? Mi történik, ha a Mentés másként menüpontot választod?
Jóslat ellenőrzése
A Mentésnél nem jelenik meg ablak: az aktualisFajl értéke a level.rtf útvonala (a megnyitáskor megjegyezted), ezért a program közvetlenül felülírja. A Mentés másként viszont mindig megnyitja a mentés ablakot, hogy új nevet adhass.
A címsor: fájlnév és módosítás
A felhasználó a címsorból látja, melyik fájlon dolgozik, és hogy van-e mentetlen változtatás. Egy CimFrissit() metódus állítja össze a címet, és a Modositva() és a Mentve() is ezt hívja:
private void CimFrissit()
{
string nev = "Névtelen";
if (aktualisFajl.Length > 0)
{
nev = System.IO.Path.GetFileName(aktualisFajl);
}
string cim = "MiniWord – " + nev;
if (valtozott)
{
cim = cim + " *";
}
this.Text = cim;
}
private void Modositva()
{
valtozott = true;
CimFrissit();
}
private void Mentve()
{
valtozott = false;
CimFrissit();
}level.rtf).CimFrissit()-re bízzák.Mit tegyél ide: két kép egymás mellett a MiniWord címsoráról: „MiniWord – level.rtf” módosítás nélkül, és „MiniWord – level.rtf *” módosítás után.Javasolt fájlnév: winforms-2-16-cimsor.webp · Alt szöveg: A MiniWord címsora a fájl nevével, mentett és módosított állapotban
Tesztlista
Próbáld ki sorban a teljes programot:
| # | Teszt | Várt eredmény |
|---|---|---|
| 1 | Gépelj szöveget | A karakterszámláló frissül, a címsorban csillag jelenik meg. |
| 2 | Mentés (még nincs fájlnév) | Megjelenik a mentés ablak; mentés után a címsor a fájl nevét mutatja, csillag nélkül. |
| 3 | Módosítás, majd Mentés | Nem kérdez nevet, felülírja a fájlt. |
| 4 | Mentés másként | Mindig megkérdezi a nevet. |
| 5 | Új dokumentum módosított állapotban | Rákérdez a mentésre (Igen, Nem, Mégse). |
| 6 | Megnyitás RTF fájlra | A szöveg formázással betöltődik, nincs csillag a címsorban. |
| 7 | Megnyitás TXT fájlra, DOCX fájlra | A TXT betöltődik, a DOCX érthető üzenetet ad. |
| 8 | Félkövér, dőlt, aláhúzott, betűtípus, méret | A kijelölt szöveg változik, a gombok követik az állapotot. |
| 9 | Igazítások, köztük a sorkizárt | Mind a négy igazítás működik. |
| 10 | Keresés és az összes találat kiemelése | A találatok sárgák, a darabszám látszik. |
| 11 | Szerkesztés menü | A menüpontok állapota megfelel (szürke, ha nincs mit visszavonni vagy kijelölni). |
| 12 | Nyomtatás PDF-nyomtatóra | A dokumentum több oldalon is jól tördelődik. |
| 13 | Bezárás mentetlen módosítással, Mégse | Az ablak nyitva marad. |
| 14 | About | A készítő neve, a dátum és a tanár neve látszik. |
Önértékelés: kész a projekt?
Ellenőrizd az alábbi szempontokat, mielőtt kész állapotúnak nyilvánítod a munkát:
Ha mind teljesül
Akkor egy teljes értékű, többfunkciós szövegszerkesztőt építettél, amely túlmutat az alapfeladaton. A következő fejezet az adatbázisok világába vezet.
Mit tegyél ide: a kész szövegszerkesztő teljes ablaka: menüsor (File, Szerkesztés, About), eszközsor (formázógombok, listák, igazítások), a keresősáv, a dokumentum többféle formázással és az állapotsor.Javasolt fájlnév: winforms-2-16-kesz.webp · Alt szöveg: A kész szövegszerkesztő teljes ablaka
Három önálló bővítési feladat
Ha kész vagy, bővítsd a programot. Egyszerre csak egyet csinálj.
1. Gyorsbillentyűk
Add a Megnyitáshoz a Jelöld ki a menüpontot a tervezőben, és a Properties ablakban állítsd a Ctrl+O, a Mentéshez a Ctrl+S, a Mentés másként menüponthoz a Ctrl+Shift+S billentyűkombinációt.Egy lehetséges megoldás
ShortcutKeys tulajdonságot a legördülő listából. Kódot nem kell írni. Figyelj arra, hogy a Szerkesztés menü billentyűit (Ctrl+C, Ctrl+V) ne így állítsd be (2.12 lecke).
2. Sortörés ki- és bekapcsolása
Készíts egy Nézet → Sortörés menüpontot ( A menüpont miSortores, CheckOnClick: True), amely a RichTextBox.WordWrap tulajdonságot állítja.Egy lehetséges megoldás
private void miSortores_CheckedChanged(object sender, EventArgs e)
{
rtbDokumentum.WordWrap = miSortores.Checked;
}Checked értéke kezdetben legyen True, mert a RichTextBox alapból tördeli a sorokat.
3. Nagyítás
Tegyél az ablakra egy A trbNagyitas csúszkát (Minimum: 50, Maximum: 300, Value: 100), amellyel a szöveg megjelenítése nagyítható.Egy lehetséges megoldás
private void trbNagyitas_Scroll(object sender, EventArgs e)
{
rtbDokumentum.ZoomFactor = trbNagyitas.Value / 100f;
}ZoomFactor a nagyítás szorzója (1 = 100%). Az f betű a szám végén azt jelenti, hogy a szám float típusú, így a 150 / 100 eredménye 1,5, nem 1.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatok a teljes MiniWord összeállításához kapcsolódnak. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Hogyan találod meg az ütközést?
Az összefésülés után a fordító (1) Nyisd meg az Error List ablakot (View → Error List). (2) A hibára duplán kattintva a Visual Studio a duplán definiált metódusra ugrik, a hibaüzenet megnevezi a metódust (például CS0111 hibát jelez. Írd le lépésekben, hogyan találod meg és javítod a hibát.Megoldás megnyitása
Form1_Load). (3) Keresd meg a másik példányt (Ctrl+F a fájlban). (4) Másold át az egyik törzsét a másikba, hogy minden feladat egy helyen legyen. (5) Töröld a feleslegessé vált példányt. (6) Építsd újra a projektet (Ctrl+Shift+B). (7) Ellenőrizd a tervezőben a Properties → Events nézetben, hogy az esemény a megmaradt metódushoz kapcsolódik-e.
2. Új dokumentum menüpont
A File → Új menüpont törölje a szöveget és az aktuális fájlnevet, és a címsor induljon újra. (A Először a védelem kérdez (FolytathatE(), Mentve() és aktualisFajl a 2.14 és 2.16 szerint áll rendelkezésre.)Megoldás megnyitása
FolytathatE()), csak utána töröl. A Mentve() a jelzőt és a címsort is rendbe teszi.private void miUj_Click(object sender, EventArgs e)
{
if (!FolytathatE())
{
return;
}
rtbDokumentum.Clear();
rtbDokumentum.ClearUndo();
aktualisFajl = "";
Mentve();
}
Közepes szint
3. Utolsó fájl újranyitása
A File → Utolsó fájl menüpont nyissa meg újra az utoljára megnyitott vagy elmentett fájlt a fájlválasztó nélkül. A megnyitás kódját két részre bontjuk: a fájlválasztó ablak (Megoldás megnyitása
Megnyitas()) és maga a betöltés (FajlMegnyitasa(string ut)). Az útvonalat az aktualisFajl mező őrzi, így az Utolsó fájl menüpont csak a betöltést hívja.private void FajlMegnyitasa(string ut)
{
try
{
rtbDokumentum.LoadFile(ut, RichTextBoxStreamType.RichText);
rtbDokumentum.ClearUndo();
aktualisFajl = ut;
Mentve();
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült megnyitni a fájlt: " + ex.Message);
}
}
private void miUtolsoFajl_Click(object sender, EventArgs e)
{
if (aktualisFajl.Length == 0)
{
MessageBox.Show("Még nincs megnyitott vagy mentett fájl.");
return;
}
if (FolytathatE())
{
FajlMegnyitasa(aktualisFajl);
}
}
4. Ctrl+F a keresőmezőbe
A A Form Ctrl+F billentyűkombináció tegye a kurzort a txtKeres keresőmezőbe, bárhol áll éppen a fókusz az ablakban.Megoldás megnyitása
KeyPreview tulajdonságát állítsd True értékre: ekkor az ablak előbb látja a billentyűleütést, mint a vezérlők. A KeyDown eseményben megvizsgáljuk a kombinációt, és az SuppressKeyPress megakadályozza, hogy a leütés a szövegmezőbe is bekerüljön.private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.F)
{
txtKeres.Focus();
txtKeres.SelectAll();
e.SuppressKeyPress = true;
}
}
Kihívás szint
5. Szószám az összeállított programban
Egészítsd ki a A számításokat külön metódusba (TextChanged eseménykezelőt úgy, hogy a karakterszámláló, a módosításjelző és a szószám is frissüljön egy kódban, és az eseménykezelő rövid maradjon.Megoldás megnyitása
Statisztika()) tesszük, az eseménykezelő csak hívja őket. Így egyetlen TextChanged metódus marad, amely nem okoz ütközést.private void Statisztika()
{
int szavak = 0;
foreach (string sor in rtbDokumentum.Lines)
{
szavak = szavak + sor.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.TextLength} | Szavak: {szavak}";
}
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
Statisztika();
Modositva();
}
6. Hibakatalógus
Szándékosan idézz elő öt hibahelyzetet, és írd le, mit kell tennie a programnak.Megoldás megnyitása
Hibahelyzet Elvárt viselkedés Hiányzó fájl megnyitása (a fájlt közben törölték) Érthető hibaüzenet, a szöveg nem vész el. Mentés írásvédett mappába Érthető hibaüzenet, a jelző továbbra is módosítottat mutat. DOCX fájl megnyitása Üzenet, hogy a formátum nem támogatott. Formázás kijelölés nélkül A program jelez, vagy nem csinál semmit, de nem omlik össze. Keresés üres szöveggel Üzenet, a program nem fagy le.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Mert a C# egy osztályban nem enged két azonos nevű, azonos paraméterű metódust ( A Mentés felülírja az aktuális fájlt (ha van), a Mentés másként mindig fájlnevet kér. Hogy a metódusok között is megmaradjon, és a Mentés tudja, melyik fájlt kell felülírnia. Az útvonalból csak a fájl nevét adja vissza. Mert a formázás nem biztos, hogy kiváltja a TextChanged eseményt, de a dokumentum így is módosult.1. Miért nem lehet két Form1_Load metódus ugyanabban az osztályban?
CS0111). A tartalmukat egyetlen metódusba kell összevonni.2. Mi a különbség a Mentés és a Mentés másként között?
3. Miért tároljuk az aktualisFajl értékét mezőben?
4. Mit csinál a Path.GetFileName?
5. Miért kell a formázógombok eseménykezelőjében is a Modositva() hívás?
Mit tudsz már?
- Egyetlen programmá fésülöd össze a különböző leckékből származó kódot.
- Megoldod a duplán definiált metódusok ütközését.
- Szétválasztod a Mentés és a Mentés másként funkciót.
- A címsor a fájl nevét és a módosítást is mutatja.
- Tesztlistával ellenőrzöd a teljes szövegszerkesztőt.
Következő fejezet: SQLite és adatbázisos alkalmazások
A szövegszerkesztő kész. A 3. fejezetben adatokat tárolunk adatbázisban, és ablakos programmal kezeljük őket.





