Grafikus C# – Windows Forms tanfolyam › 2.10 lecke Windows Forms · 2.10 lecke · záró projekt Elkészült az összes darab: megnyitás, mentés, formázás, igazítás, keresés, névmező és About. Most összeállítjuk belőlük a teljes MiniWordöt. A lecke tartalmazza a vezérlők listáját, a teljes forráskódot, a File menü bekötését és egy tesztlistát, amellyel ellenőrizheted, hogy minden működik-e.MiniWord: az összetett szövegszerkesztő összeépítése

A lecke végére
- összeállítod a MiniWord felületét az összes vezérlővel;
- a File menü elemeit a
Megnyitas()és aMentes()metódusokkal kötöd össze; - ellenőrzöd, hogy a program teljesíti a feladat minden pontját;
- egy tesztlista alapján végigpróbálod a programot;
- felismered az összeépítés tipikus hibáit;
- a projektet úgy csomagolod, hogy másnak is elküldhesd.
Mielőtt elkezded
Ehhez a leckéhez a 2.1–2.9 leckék mindegyikét el kell készítened. Ha valamelyik rész még hiányzik, itt a teljes kód, de érdemes előbb a megfelelő leckét elvégezni.
Mit kell tudnia a MiniWordnek?
A MiniWord egy Word-szerű szövegszerkesztő. A következő táblázat összefoglalja, mit kell tudnia, és melyik lecke tanította:
| Mit tud a program? | Lecke |
|---|---|
| RTF fájlok megnyitása, a megnyitáskor választható fájltípusok: rtf, docx, minden fájl | 2.2 |
| A szöveges mezőbe írt szöveg mentése a felhasználó által megadott néven, rtf kiterjesztéssel | 2.3 |
| Bekezdésigazítás: balra, jobbra, középre, sorkizárt | 2.6 és 2.7 |
| A kijelölt szöveg formázása: félkövér, dőlt, aláhúzott | 2.4 |
| Betűméret (legalább 3 féle) és betűtípus (legalább 3 féle) választása legördülő listából | 2.5 |
| Keresés megadott szövegre, az összes előfordulás kiemelésével | 2.8 |
| Beviteli mező, amelybe írt nevet felugró ablak mutat meg | 2.9 |
| File menü (mentés, megnyitás, kilépés) és About menü (ki és mikor készítette, „Tanára:” után a névmező tartalma) | 2.9 és 2.10 |
| Címke, amely folyamatosan mutatja a szövegmezőben lévő karakterek számát | 2.1 |
Minden pont megvan
A leckék együtt lefedik a MiniWord összes funkcióját. Ebben a leckében a megmaradt darabot, a File menü bekötését készítjük el, és összeállítjuk az egészet.
A felület: vezérlők és nevek
A kód csak akkor működik, ha a vezérlők nevei pontosan egyeznek. Ellenőrizd a Properties ablakban a (Name) tulajdonságokat:
| Vezérlő | (Name) | Szerepe |
|---|---|---|
| RichTextBox | rtbDokumentum | A dokumentum szövege. HideSelection: False. |
| Label | lblKarakterszam | A karakterek száma. |
| OpenFileDialog | ofdMegnyitas | Fájl megnyitása. |
| SaveFileDialog | sfdMentes | Fájl mentése. |
| Button | btnFelkover, btnDolt, btnAlahuzott | F, D, A stílusgombok. |
| ComboBox | cmbBetutipus, cmbMeret | Betűtípus és méret (DropDownList). |
| Button | btnBalra, btnKozep, btnJobbra, btnSorkizart | Igazítás. |
| TextBox és Button | txtKeres, btnKeres, btnOsszes | Keresés és kiemelés. |
| TextBox és Button | txtTanar, btnNev | Névmező és felugró ablak. |
| MenuStrip | miFile, miMegnyitas, miMentes, miKilepes, miAbout | Menüsor. |
Mit tegyél ide: a kész MiniWord teljes ablaka tervezőnézetben vagy futás közben. Felül a menüsor (File, About), alatta a gombok és legördülő listák, a keresősáv, a névmező, középen a nagy szövegdoboz, alul a karakterszámláló.Javasolt fájlnév: winforms-2-10-miniword-felulet.webp · Alt szöveg: A teljes MiniWord szövegszerkesztő felülete
Az elrendezés rajtad múlik
A vezérlők pontos helye nincs megszabva: tedd őket úgy, hogy áttekinthető legyen. Az Anchor (2.1) segít, hogy a szövegdoboz együtt nőjön az ablakkal. Az elrendezés finomhangolását a 6. fejezetben tanuljuk.
A teljes Form1.cs
Az alábbi kód a 2.1–2.9 leckék összes részletét tartalmazza. Hasonlítsd össze a saját megoldásoddal. Ha valami nálad másképp van, keresd meg a különbséget:
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace MiniWord
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Az ablak betöltése: sorkizárt támogatás bekapcsolása
private void Form1_Load(object sender, EventArgs e)
{
RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum);
}
// 2.1: karakterszámláló
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
}
// 2.2: megnyitás
private void Megnyitas()
{
ofdMegnyitas.Title = "Dokumentum megnyitása";
ofdMegnyitas.Filter = "RTF dokumentum (*.rtf)|*.rtf|Word dokumentum (*.docx)|*.docx|Szövegfájl (*.txt)|*.txt|Minden fájl (*.*)|*.*";
if (ofdMegnyitas.ShowDialog() == DialogResult.OK)
{
string fajl = ofdMegnyitas.FileName;
string kiterjesztes = Path.GetExtension(fajl).ToLower();
try
{
if (kiterjesztes == ".rtf")
{
rtbDokumentum.LoadFile(fajl, RichTextBoxStreamType.RichText);
}
else if (kiterjesztes == ".txt")
{
rtbDokumentum.Text = File.ReadAllText(fajl);
}
else
{
MessageBox.Show("Ezt a fájltípust a MiniWord egyelőre nem tudja megnyitni: " + kiterjesztes);
}
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült megnyitni a fájlt: " + ex.Message);
}
}
}
// 2.3: mentés
private void Mentes()
{
sfdMentes.Title = "Dokumentum mentése";
sfdMentes.Filter = "RTF dokumentum (*.rtf)|*.rtf";
sfdMentes.DefaultExt = "rtf";
sfdMentes.AddExtension = true;
sfdMentes.FileName = "dokumentum";
if (sfdMentes.ShowDialog() == DialogResult.OK)
{
try
{
rtbDokumentum.SaveFile(sfdMentes.FileName, RichTextBoxStreamType.RichText);
MessageBox.Show("A dokumentum mentve.");
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült menteni a fájlt: " + ex.Message);
}
}
}
// 2.4: félkövér, dőlt, aláhúzott
private void StilusValt(FontStyle stilus)
{
Font regi = rtbDokumentum.SelectionFont;
if (regi != null)
{
rtbDokumentum.SelectionFont = new Font(regi, regi.Style ^ stilus);
}
else
{
MessageBox.Show("A kijelölésben többféle formázás van. Jelölj ki egységes szövegrészt!");
}
rtbDokumentum.Focus();
}
private void btnFelkover_Click(object sender, EventArgs e) { StilusValt(FontStyle.Bold); }
private void btnDolt_Click(object sender, EventArgs e) { StilusValt(FontStyle.Italic); }
private void btnAlahuzott_Click(object sender, EventArgs e) { StilusValt(FontStyle.Underline); }
// 2.5: betűtípus és betűméret
private void cmbBetutipus_SelectedIndexChanged(object sender, EventArgs e)
{
Font regi = rtbDokumentum.SelectionFont;
if (regi == null || cmbBetutipus.SelectedItem == null)
{
MessageBox.Show("A kijelölésben többféle formázás van, vagy nincs kiválasztva betűtípus.");
}
else
{
string csalad = cmbBetutipus.SelectedItem.ToString();
rtbDokumentum.SelectionFont = new Font(csalad, regi.Size, regi.Style);
}
rtbDokumentum.Focus();
}
private void cmbMeret_SelectedIndexChanged(object sender, EventArgs e)
{
Font regi = rtbDokumentum.SelectionFont;
float meret;
if (regi == null)
{
MessageBox.Show("A kijelölésben többféle formázás van. Jelölj ki egységes szövegrészt!");
}
else if (float.TryParse(cmbMeret.Text, out meret))
{
rtbDokumentum.SelectionFont = new Font(regi.FontFamily, meret, regi.Style);
}
rtbDokumentum.Focus();
}
// 2.6 és 2.7: igazítás
private void btnBalra_Click(object sender, EventArgs e)
{
rtbDokumentum.SelectionAlignment = HorizontalAlignment.Left;
rtbDokumentum.Focus();
}
private void btnKozep_Click(object sender, EventArgs e)
{
rtbDokumentum.SelectionAlignment = HorizontalAlignment.Center;
rtbDokumentum.Focus();
}
private void btnJobbra_Click(object sender, EventArgs e)
{
rtbDokumentum.SelectionAlignment = HorizontalAlignment.Right;
rtbDokumentum.Focus();
}
private void btnSorkizart_Click(object sender, EventArgs e)
{
RichTextBoxHelper.Sorkizart(rtbDokumentum);
rtbDokumentum.Focus();
}
// 2.8: keresés
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.");
}
}
}
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);
}
// 2.9: névmező és About
private void btnNev_Click(object sender, EventArgs e)
{
string nev = txtTanar.Text.Trim();
if (nev.Length == 0)
{
MessageBox.Show("Kérlek, írj be egy nevet!");
}
else
{
MessageBox.Show("A beírt név: " + nev);
}
}
private void miAbout_Click(object sender, EventArgs e)
{
string tanar = txtTanar.Text.Trim();
if (tanar.Length == 0)
{
tanar = "(még nincs megadva)";
}
string uzenet = "MiniWord szövegszerkesztő" + Environment.NewLine
+ "Készítette: Minta Anna" + Environment.NewLine
+ "Dátum: 2026. október 24." + Environment.NewLine
+ "Tanára: " + tanar;
MessageBox.Show(uzenet, "About");
}
// 2.9 és 2.10: File menü
private void miMegnyitas_Click(object sender, EventArgs e)
{
Megnyitas();
}
private void miMentes_Click(object sender, EventArgs e)
{
Mentes();
}
private void miKilepes_Click(object sender, EventArgs e)
{
this.Close();
}
}
}Nem kell mindent átgépelned
Ez a kód a korábbi leckék darabjaiból áll össze. Ha az előző leckéket elvégezted, a legtöbb részlet már megvan nálad. Csak azt kell ellenőrizned, hogy minden metódus egyszer szerepel, a nevek egyeznek, és a menüpontok a Megnyitas() és Mentes() metódust hívják. A RichTextBoxHelper.cs külön fájlban van (2.7 lecke).
Tesztlista: működik-e minden?
Egy program akkor kész, ha kipróbáltad. Végezd el sorban az alábbi teszteket, és pipáld ki, ami működik:
| # | Teszt | Várt eredmény |
|---|---|---|
| 1 | Írj be szöveget | A karakterszámláló azonnal frissül. |
| 2 | File → Megnyitás, válaszd az RTF típust, nyiss meg egy RTF fájlt | A szöveg formázással együtt megjelenik, a számláló frissül. |
| 3 | A megnyitás ablakban nézd meg a típuslistát | RTF, DOCX, TXT és minden fájl lehetőség látszik. |
| 4 | Nyiss meg egy DOCX fájlt | Érthető üzenet jelenik meg, a program nem omlik össze. |
| 5 | Jelölj ki egy szót, majd F, D, A gomb | A szó formázása változik, újra megnyomva visszaáll. |
| 6 | Válassz másik betűtípust és méretet | A kijelölt szöveg változik, a félkövér megmarad. |
| 7 | Balra, középre, jobbra, sorkizárt gomb | A bekezdés igazítása ennek megfelelően változik. |
| 8 | Keress egy szót, majd Összes kiemelése | Minden előfordulás sárga, az üzenet mutatja a számot. |
| 9 | Üres keresés | Üzenet jelenik meg, a program nem fagy le. |
| 10 | Írj nevet a névmezőbe, nyomd meg a gombot | A felugró ablak megmutatja a nevet. |
| 11 | About menü | A készítő neve, a dátum és a „Tanára:” után a névmező tartalma látszik. |
| 12 | File → Mentés, majd újranyitás | A mentett fájl .rtf kiterjesztésű, és a formázás megmarad. |
| 13 | File → Kilépés | Az ablak bezárul. |
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. Kétszer szerepel a metódus
// CS0111: Type 'Form1' already defines a member called 'Mentes' with the same parameter typesOk és javítás
Fordítási hiba: a kódot kétszer másoltad be, ezért ugyanaz a metódus kétszer szerepel a Form1 osztályban. Töröld az egyik példányt. Használd a Visual Studio keresését (Ctrl+F) a metódus nevére.
2. Kétszer lefut az eseménykezelő
A Mentés gomb megnyomására a mentés ablak kétszer jelenik meg. Az eseményhez az eseménykezelő kétszer van hozzákötve. Ez akkor fordul elő, ha a Designer is létrehozta, és te is kézzel hozzáadtad. Nyisd meg a Properties ablak Events nézetét, nézd meg a Click sort, és hagyd meg egyetlen eseménykezelőt.Ok és javítás
3. Ismeretlen Path vagy Color
// CS0103: The name 'Path' does not exist in the current contextOk és javítás
Fordítási hiba: hiányzik a using System.IO; sor (a Path és a File miatt), vagy a using System.Drawing; sor (a Color és a Font miatt). A fájl elejére add hozzá.
4. Sárga szavak a mentett fájlban
A mentett dokumentum megnyitásakor a keresett szavak sárgák maradtak. A kiemelés a dokumentum formázásának része. Mentés előtt hívd meg a Ok és javítás
TorolKiemeleseket() metódust a Mentes() elején, vagy törölj minden kiemelést kézzel.
5. A sorkizárt nem működik
A sorkizárt gombra kattintasz, de nem történik semmi. Ellenőrizd, hogy a Ok és javítás
RichTextBoxHelper.cs a projekt része-e, és a kód pontosan egyezik-e a 2.7 leckében lévővel. Írj egy több soros bekezdést, mert egysoros szövegen a sorkizárt nem látszik.
Csomagolás: így add oda másnak
Ha a programot be kell mutatnod vagy be kell adnod, a következőképpen csomagold össze:
- Zárd be a Visual Studiót, és a Fájlkezelőben keresd meg a solution mappát (0.5 lecke).
- Töröld a projekt mappájából a
binés azobjmappát, vagy hagyd ki őket a csomagolásnál. Ezeket a Visual Studio újra elkészíti. - Tömörítsd az egész solution mappát ZIP fájlba.
- Próbáld ki: csomagold ki egy másik helyre, nyisd meg a
.slnfájlt, és futtasd az F5-tel. - Ellenőrizd a tesztlistát még egyszer a kicsomagolt példányon.
Kész a MiniWord
Ha a tesztlista mind a 13 pontja működik, a MiniWord teljesíti a feladat minden követelményét. Gratulálok, egy valódi szövegszerkesztőt építettél.
Önálló gyakorlófeladatok
Ha a MiniWord már kész, bővítheted. Először készíts saját megoldást, utána ellenőrizd.
1. Új dokumentum
Adj a File menühöz egy Új menüpontot (miUj), amely kitörli a dokumentumot.Egy lehetséges megoldás
private void miUj_Click(object sender, EventArgs e)
{
rtbDokumentum.Clear();
}
2. Gyorsbillentyűk
A Mentés menüponthoz tartozzon a Ctrl+S, a Megnyitáshoz a Ctrl+O billentyűkombináció. Jelöld ki a menüpontot a tervezőben, és a Properties ablakban állítsd be a Egy lehetséges megoldás
ShortcutKeys tulajdonságot (legördülő lista: Ctrl és a betű kiválasztása). Kódot nem kell írnod.
3. Sorok száma
Tegyél a karakterszámláló mellé egy Tedd a lblSorok címkét, amely a dokumentum sorainak számát mutatja.Egy lehetséges megoldás
lblSorok.Text = $"Sorok: {rtbDokumentum.Lines.Length}";rtbDokumentum_TextChanged eseménykezelőbe. A Lines a dokumentum sorainak tömbje, a Length pedig a sorok számát adja.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Ezek a feladatok az összeépítés tipikus nehézségeit gyakoroltatják: nevek, eseménykezelők, tesztelés. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. A vezérlőnév hibája
A fordító hibát jelez: CS1061: A „Form1” nem tartalmaz „rtbDokumentum” nevű definíciót (vagy hasonlót). Mi lehet az ok, és hol javítod? A kódban használt név nem egyezik a vezérlő Megoldás megnyitása
(Name) tulajdonságával (elírás, kis- és nagybetű, vagy a vezérlő még az alapértelmezett richTextBox1 nevet viseli). Jelöld ki a vezérlőt a tervezőben, és a Properties ablak (Name) mezőjében javítsd, vagy írd át a kódot. A Visual Studio az átnevezéskor a kódot is át tudja írni (jobb gomb a névre, Rename), így nem marad ellentmondás.
2. Egy metódus két helyről
A Megnyitás gomb és a File → Megnyitás menüpont ugyanazt csinálja. Hogyan írod meg úgy, hogy a kód csak egyszer legyen meg? A kódot egy külön metódusba tesszük (Megoldás megnyitása
Megnyitas()), és mindkét eseménykezelő csak ezt hívja. Így egy javítás mindkét helyen érvényesül.private void Megnyitas()
{
// a megnyitás kódja egy helyen
}
private void btnMegnyitas_Click(object sender, EventArgs e)
{
Megnyitas();
}
private void miMegnyitas_Click(object sender, EventArgs e)
{
Megnyitas();
}
Közepes szint
3. Gyorsbillentyűk a menühöz
Add a Megnyitás menüponthoz a A tervezőben jelöld ki a menüpontot, és a Properties ablakban a Ctrl+O, a Mentés menüponthoz a Ctrl+S gyorsbillentyűt.Megoldás megnyitása
ShortcutKeys tulajdonságnál pipáld be a Ctrl módosítót, és válaszd az O, illetve S billentyűt. Kódot nem kell írnod: a billentyű a menüpont Click eseményét hívja.
4. A karakterszámláló megnyitás után
Egy fájl megnyitása után a A lblKarakterszam még a régi értéket mutatja. Miért nem, ha jól van megírva? Mi a hiba, ha mégis így lenne?Megoldás megnyitása
LoadFile a szöveget megváltoztatja, ezért a TextChanged esemény magától lefut, és frissíti a számlálót. Ha mégis a régi érték látszik, valószínűleg a TextChanged eseménykezelő nincs a vezérlő eseményéhez kötve (a metódus megvan, de a tervezőben az Events nézet TextChanged sora üres). Ellenőrizd a Properties ablak villám ikonja alatt.
Kihívás szint
5. Saját tesztterv
Írj tesztet a MiniWordhöz legalább 10 lépésben. Minden lépésnél legyen: mit csinálsz, mi az elvárt eredmény.Megoldás megnyitása
# Teszt Elvárt eredmény 1 Gépelj szöveget A karakterszám azonnal frissül. 2 Mentés új néven Az .rtf fájl létrejön. 3 Megnyitás, majd Mégse Nem történik semmi. 4 A mentett fájl újranyitása A szöveg és a formázás megegyezik. 5 Félkövér és dőlt egy szón Mindkét stílus megjelenik, a többi szó nem változik. 6 Betűméret váltása félkövér szón A szó félkövér marad. 7 Keresés többször előforduló szóra Minden előfordulás kiemelt. 8 Keresés üres szöveggel Üzenet, nem fagy le. 9 Név beírása és About A név a Tanára: sorban látszik. 10 DOCX fájl megnyitása Érthető üzenet, nem omlik össze.
6. Három hibás kód
Keresd meg a hibát mindhárom kódrészletben, és javítsd őket. A) Mégse gomb után is mentés: Javítás: az eredményt vizsgálni kell: B) A félkövér elvész a betűváltáskor: Javítás: a régi stílust át kell adni: C) A keresés lefagy: Javítás: a következő keresés a találat vége után induljon: Megoldás megnyitása
sfdMentes.ShowDialog();
rtbDokumentum.SaveFile(sfdMentes.FileName, RichTextBoxStreamType.RichText);if (sfdMentes.ShowDialog() == DialogResult.OK) { ... }. Különben a Mégse gomb után üres fájlnévvel próbál menteni, és hibát kapsz.rtbDokumentum.SelectionFont = new Font("Arial", 14);new Font("Arial", 14, regi.Style), ahol a regi a SelectionFont korábbi értéke (és előbb null ellenőrzés kell).int hely = szoveg.IndexOf(keresett);
while (hely >= 0)
{
rtbDokumentum.Select(hely, keresett.Length);
rtbDokumentum.SelectionBackColor = Color.Yellow;
hely = szoveg.IndexOf(keresett, hely);
}IndexOf(keresett, hely + keresett.Length). A hibás kódban mindig ugyanazt a helyet találja meg, ezért a ciklus végtelen.
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Hogy a kódot egyszer kelljen megírni és javítani, és a kettő mindig ugyanúgy működjön. A kódban használt név és a Properties ablakban megadott (Name) pontosan egyezik. Ha nem, fordítási hibát kapsz. Mert a kiemelés a dokumentum formázása, így a fájlba is belekerül. Elmented a dokumentumot, majd újra megnyitod, és ellenőrzöd, hogy a tartalom és a formázás megmaradt-e. A 1. Miért hívja a menüpont és a gomb ugyanazt a metódust?
2. Mit jelent a Name tulajdonság egyezése?
3. Miért kell törölni a kiemeléseket mentés előtt?
4. Mi az újranyitási teszt?
5. Mit kell kihagyni a projekt csomagolásakor?
bin és az obj mappát, mert a Visual Studio újra elkészíti őket.
Mit tudsz már?
- Összeállítod a teljes MiniWord felületét és kódját.
- A File menü a gombokkal azonos metódusokat hívja.
- Végigteszteled a programot egy tesztlista alapján.
- Felismered az összeépítés tipikus hibáit.
- Elcsomagolod a projektet másnak.
Következő lecke: Kijelölés és SelectionChanged
Az alapfeladat kész. A 2.11–2.16 leckék a MiniWordöt igazi szövegszerkesztővé fejlesztik: állapotsor, szerkesztés menü, vágólap, védelem a mentetlen munka ellen és nyomtatás.





