Grafikus C# – Windows Forms tanfolyam › 4.2 lecke

Windows Forms · 4.2 · önálló gyakorlás

Önálló feladat II.: MiniWord – grafikus szövegszerkesztő

Ez a tanfolyam legnagyobb önálló feladata: egy Word-szerű szövegszerkesztőt építesz meg a 2. fejezet tudásával, időre. A leírás szerepel, a megoldás nem: csak támpontokat kapsz. Ha egy rész nehéz, a megfelelő lecke ott részletesen megoldja, de a program összeállítása a te munkád.

  • 180 perc
  • önálló munka
  • 100 pontos feladat
Diáklány C# programot készít laptopon

A feladat végére

  • önállóan megtervezel és felépítesz egy többfunkciós szövegszerkesztőt;
  • fájlt nyitsz meg és mentesz a felhasználó által választott néven, hibakezeléssel;
  • a kijelölt szöveget formázod: stílus, betűtípus, betűméret, igazítás;
  • keresel a szövegben, és kiemeled az összes találatot;
  • menüt, névmezőt, About ablakot és karakterszámlálót építesz;
  • a tesztlistával és az önértékeléssel ellenőrzöd a munkádat.

Mielőtt elkezded

Ehhez a feladathoz a 2. fejezet leckéire támaszkodsz. A munka közben használhatod a leckéket és a saját jegyzeteidet. A sorkizárt igazításhoz a 2.7 lecke segédosztálya használható.

2.16 Szövegszerkesztő záró projekt →

2.10 MiniWord összerakása →

A feladat leírása

Készíts MiniWord néven egy Windows Forms szövegszerkesztőt, amelynek tulajdonságai:

  • A fő felület egy RichTextBox, alatta vagy mellette egy címke mutatja a szövegben lévő karakterek számát, és ez gépelés közben azonnal frissül.
  • Az RTF fájlok megnyithatók egy fájlválasztóval. A típusszűrő felsorolja az RTF, a DOCX és az összes fájl lehetőségét. (A DOCX megnyitását a program nem tudja, ilyenkor érthető üzenet jelenik meg.)
  • A szöveg RTF fájlba menthető, a felhasználó által megadott néven, az .rtf kiterjesztéssel.
  • A kijelölt szöveg félkövér, dőlt és aláhúzott lehet, kapcsolóként működő gombokkal.
  • A kijelölt szöveghez legalább három betűméret és három betűtípus választható legördülő listából.
  • A bekezdések balra, jobbra, középre és sorkizártra igazíthatók.
  • Egy keresőmezőbe beírt szöveg minden előfordulását a program megtalálja és kiemeli.
  • Egy névmezőbe beírt nevet egy gomb egy felugró ablakban megjelenít.
  • A File menü tartalmazza a Mentés, a Megnyitás és a Kilépés elemet. Az About menü felugró ablakban megmutatja, ki és mikor készítette a programot, majd egy Tanára: sort a névmezőbe beírt névvel.
📷 Képernyőkép helye

Mit tegyél ide: egy példa a kész szövegszerkesztő ablakáról: menüsor, formázógombok, listák, keresősáv, névmező, a szöveges terület és a karakterszámláló.Javasolt fájlnév: winforms-4-2-minta.webp · Alt szöveg: A MiniWord szövegszerkesztő mintaablaka

Követelmények és pontozás

RészKövetelményPont
Fájlműveletek (30)Megnyitás fájlválasztóval, a típusszűrő RTF, DOCX és minden fájl lehetőséggel; az RTF fájl betöltődik.12
Mentés RTF-ként, a megadott néven, .rtf kiterjesztéssel; a Mégse gomb nem okoz hibát.12
Hibás, hiányzó vagy nem támogatott fájlnál érthető üzenet jelenik meg, a program nem omlik össze.6
Szövegkezelés (40)Félkövér, dőlt és aláhúzott a kijelölt szövegre; a meglévő stílus nem vész el.10
Legalább 3 betűtípus és 3 betűméret legördülő listából, a kijelölt szövegre.10
Balra, középre és jobbra igazítás; a sorkizárt igazítás külön megoldással.10
Keresés: minden előfordulás kiemelve; üres kereséskor nem fagy le a program.10
Menü és kiegészítők (30)File menü: Mentés, Megnyitás, Kilépés, a megfelelő eseménykezelőkkel.10
About: a készítő neve, a dátum és a Tanára: sor a névmező tartalmával.8
Névmező és gomb, felugró ablakban a beírt névvel; üres névnél figyelmeztetés.6
Élő karakterszámláló a szöveg alatt.6

Összesen: 100 pont. A fájlműveletek 30, a szövegkezelés 40, a menü és a kiegészítők 30 pontot érnek.

Készíts előbb ellenőrzőlistát

Az első 15 percben írd fel a követelményeket egy listára, és munka közben pipáld ki őket. A feladat sok kis részből áll, és könnyű elfelejteni valamit. A legvégén a lista alapján ellenőrizd a kész programot.

Időbeosztás (180 perc)

PercMit csinálj?
0–15Írd fel az ellenőrzőlistát a követelményekből. Gondold végig az ablak elrendezését, és a vezérlők neveit.
15–35Hozd létre a projektet, a RichTextBox-ot, a karakterszámláló címkét, és kösd be a TextChanged eseményt.
35–60Megnyitás szűrővel (RTF, DOCX, minden fájl), mentés RTF-be. Próbáld ki a Mégse gombot és a rossz fájlokat.
60–95Félkövér, dőlt, aláhúzott, betűtípus és betűméret, csak a kijelölt szövegen, a meglévő stílus megtartásával.
95–115Balra, középre, jobbra igazítás; a sorkizárt igazítás külön megoldással.
115–140Szövegkeresés: először az első találat, majd az összes előfordulás kiemelése.
140–160File menü, About, névmező és felugró ablak.
160–180Teljes tesztlista, hibás bevitel, Mégse gombok, üres szöveg, több bekezdés. Mentés és újranyitás teszt.

Támpontok (kész megoldás nélkül)

Melyik tulajdonság, melyik metódus?
FeladatAmit használszLecke
KarakterszámText.Length, TextChanged2.1
MegnyitásOpenFileDialog, Filter, LoadFile, try–catch2.2
MentésSaveFileDialog, DefaultExt, SaveFile2.3
StílusokSelectionFont, FontStyle, a ^ operátor2.4
Betűtípus és méretComboBox, SelectedIndexChanged, new Font(...)2.5
IgazításSelectionAlignment; a sorkizárthoz a segédosztály2.6, 2.7
KeresésIndexOf, while, Select, SelectionBackColor2.8
Névmező és AboutMessageBox.Show, MenuStrip, Environment.NewLine2.9
A kijelölés hiánya és a vegyes formázás

A formázó gombok eseménykezelőjében gondolj arra, mi történik, ha nincs kijelölt szöveg, vagy a kijelölés vegyesen formázott (a SelectionFont ilyenkor null). Az üzenetet a felhasználónak érthetően kell megfogalmazni.

A stílus megtartása betűváltáskor

Új Font létrehozásakor adj át minden korábbi jellemzőt: a betűméret váltásakor a régi család és stílus, a betűtípus váltásakor a régi méret és stílus maradjon. Különben a félkövér vagy dőlt formázás elvész.

A Mégse gomb a fájlválasztónál

A ShowDialog() eredményét ellenőrizd: csak az OK esetén dolgozz a fájllal. A fájlműveleteket tedd try–catch blokkba.

A keresés kiemelései

A kiemelés a dokumentum formázása, ezért az új keresés előtt töröld a régi kiemeléseket, és mentés előtt is érdemes. Védekezz az üres keresőszöveg ellen: végtelen ciklus lehet belőle.

Az About ablak dátuma

A mikor készítette adatot rögzített szövegként add meg (a program elkészítésének napját), ne az aktuális időt.

Mintamegoldás

Előbb oldd meg önállóan!

A mintamegoldás csak az ellenőrzésre való, és nem az egyetlen helyes megoldás. Előbb próbálkozz saját kóddal, és csak elakadásnál vagy a végén nyisd meg. A részletes magyarázat a 2.1–2.10 leckékben található.

Megoldás: a felület és a nevek
Vezérlő(Name)Beállítás
RichTextBoxrtbDokumentumHideSelection: False
LabellblKarakterszamkarakterszám
OpenFileDialog, SaveFileDialogofdMegnyitas, sfdMentes
ButtonbtnFelkover, btnDolt, btnAlahuzottF, D, A
ComboBoxcmbBetutipus, cmbMeretDropDownList; Items: Arial, Times New Roman, Courier New, Verdana és 10, 12, 16, 20, 28
ButtonbtnBalra, btnKozep, btnJobbra, btnSorkizartigazítás
TextBox, ButtontxtKeres, btnKereskeresés és kiemelés
TextBox, ButtontxtTanar, btnNevnévmező és felugró ablak
MenuStripmiMegnyitas, miMentes, miKilepes, miAboutFile: Megnyitás, Mentés, Kilépés; About
Megoldás: RichTextBoxHelper.cs (a sorkizárthoz)

A sorkizárt igazítás segédosztálya a 2.7 leckében van, onnan másold a projektbe (Add → Class, a fájl neve RichTextBoxHelper.cs). A két használt metódus: RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum) (a Form1_Load-ban) és RichTextBoxHelper.Sorkizart(rtbDokumentum) (a gombban).

Megoldás: Form1.cs (teljes kód)
Form1.csC#
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

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

private void Form1_Load(object sender, EventArgs e)
{
RichTextBoxHelper.EngedelyezSorkizartot(rtbDokumentum);
}

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

private void Megnyitas()
{
ofdMegnyitas.Filter = "RTF dokumentum (*.rtf)|*.rtf|Word dokumentum (*.docx)|*.docx|Minden fájl (*.*)|*.*";

if (ofdMegnyitas.ShowDialog() != DialogResult.OK)
{
return;
}

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 nem tudja megnyitni: " + kiterjesztes);
}
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült megnyitni a fájlt: " + ex.Message);
}
}

private void Mentes()
{
sfdMentes.Filter = "RTF dokumentum (*.rtf)|*.rtf";
sfdMentes.DefaultExt = "rtf";
sfdMentes.AddExtension = true;

if (sfdMentes.ShowDialog() != DialogResult.OK)
{
return;
}

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);
}
}

private void StilusValt(FontStyle stilus)
{
Font regi = rtbDokumentum.SelectionFont;

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
{
rtbDokumentum.SelectionFont = new Font(regi, regi.Style ^ stilus);
}

rtbDokumentum.Focus();
}

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 betűtípus kiválasztva.");
}
else
{
rtbDokumentum.SelectionFont = new Font(cmbBetutipus.SelectedItem.ToString(), 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();
}

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); }

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(); }

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

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

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

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 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)";
}

MessageBox.Show("MiniWord szövegszerkesztő" + Environment.NewLine
+ "Készítette: Minta Anna" + Environment.NewLine
+ "Dátum: 2026. október 24." + Environment.NewLine
+ "Tanára: " + tanar, "About");
}

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(); }
}
}
Megoldás: a legfontosabb tervezési döntések

A gomb és a menü ugyanazt a metódust hívja (Megnyitas(), Mentes()), ezért a kódot egyszer kell megírni. A formázásnál az új Font a régi stílust vagy méretet is megkapja, így a félkövér nem vész el. A SelectionFont null értékét vegyes kijelölésnél külön kezeljük. A keresés előtt töröljük a régi kiemeléseket, és üres keresőszövegnél azonnal kilépünk, mert különben a while ciklus végtelen lenne. A DOCX fájlt a program felismeri a kiterjesztésből, és érthető üzenetet ad a LoadFile hibája helyett.

Tesztlista

#TesztVárt eredmény
1Gépelés a szövegdobozbaA karakterszámláló azonnal frissül.
2Megnyitás: a típusszűrő lenyitásaRTF, DOCX és minden fájl lehetőség látszik.
3RTF fájl megnyitásaA szöveg formázással betöltődik.
4DOCX vagy sérült fájl megnyitásaÉrthető üzenet, a program nem omlik össze.
5Megnyitás, majd MégseNem történik semmi.
6Mentés, majd újranyitásA fájl .rtf kiterjesztésű, a formázás megmarad.
7Mentés, majd MégseNem történik hiba.
8Félkövér, dőlt, aláhúzott egy szón, majd újraBekapcsol, majd kikapcsol, a többi stílus megmarad.
9Betűtípus- és méretváltás félkövér szónA szó félkövér marad.
10Formázás kijelölés nélkül és vegyes kijelölésnélÉrthető üzenet, nem omlik össze.
11Balra, középre, jobbra, sorkizárt gombA bekezdés ennek megfelelően igazodik.
12Keresés olyan szóra, amely többször szerepelMinden előfordulás kiemelt.
13Üres és nem létező szó kereséseÜzenet, nem fagy le.
14Névmező és gomb, üres névvel isFelugró ablak a névvel, üres névnél figyelmeztetés.
15About menüpontA készítő neve, a dátum és a Tanára: sor a névvel.
16File → KilépésAz ablak bezárul.

Gyakori hibák és önértékelés

Ezeken szoktak elcsúszni

Gondold végig, nálad előfordul-e valamelyik.

1. Minden egyetlen eseménykezelőben

A megnyitás, a mentés és az összes formázás kódját egyetlen gomb eseménykezelőjébe írod, és a gombok összekeverednek.

Ok és javítás

Minden felületi műveletnek legyen saját eseménykezelője (Click, SelectedIndexChanged, TextChanged), a közös kód pedig külön metódusokban álljon (Megnyitas(), Mentes(), StilusValt()). A gomb és a menü is ugyanazt a metódust hívja.

2. A stílus elvész

rtbDokumentum.SelectionFont = new Font("Arial", 14);
Hiba és javítás

Rossz működés: az új Font nem kapja meg a régi stílust, ezért a félkövér vagy dőlt formázás elvész. Add át a régi stílust (regi.Style) is.

3. A fájlra mentés felülírja a nevet

A mentés mindig ugyanazt a nevet használja, és a felhasználó nem tud mást megadni.

Ok és javítás

A felhasználónak SaveFileDialog segítségével kell megadnia a nevet. A DefaultExt = "rtf" és az AddExtension = true beállítás gondoskodik a kiterjesztésről.

4. A keresés lefagy

Üres kereséskor a program nem válaszol.

Ok és javítás

Üres keresőszövegnél a while ciklus nem lép előre, ezért végtelen. Ellenőrizd a keresőszöveg hosszát a ciklus előtt (2.8 lecke).

5. A kiemelés bekerül a mentett fájlba

A mentett dokumentum szavai sárgák maradnak.

Ok és javítás

A háttérszín formázás, ezért a fájlba is bekerül. Mentés előtt töröld a kiemeléseket.

6. A DOCX miatt összeomlik

A DOCX fájl kiválasztásakor a program leáll.

Ok és javítás

A RichTextBox nem tud DOCX fájlt betölteni. A kiterjesztés alapján ágazz el, és DOCX esetén mutass érthető üzenetet. A LoadFile hívást tedd try–catch blokkba.

Az értékeléshez ellenőrizd az alábbi szempontokat is:

MűködésA tesztlista mind a 16 pontja teljesül.
HibakezelésÜres, hibás, hiányzó és vegyes bevitelre is érthetően reagál a program, és nem omlik össze.
Nevek és szerkezetA vezérlők beszédes előtagos nevet kapnak, a kód metódusokra bontva áttekinthető.
Újranyitási tesztA mentett és újranyitott dokumentum tartalma és formázása megegyezik.

Bővítési ötletek, ha kész vagy

Ha maradt időd, fejleszd tovább a programot:

  • Mentetlen munka védelme: a bezárás előtt kérdezzen rá a program (2.14 lecke).
  • Szerkesztés menü: visszavonás, újra, kivágás, másolás, beillesztés (2.12 lecke).
  • Állapotsor: a sor és oszlop kijelzése, a gombok követik a kijelölést (2.11 lecke).
  • Nyomtatás és előnézet (2.15 lecke).
  • Gyorsbillentyűk: Ctrl+O, Ctrl+S, Ctrl+F.

Mit tudsz már?

  • Önállóan felépítesz egy többfunkciós szövegszerkesztőt.
  • Fájlt nyitsz meg és mentesz hibakezeléssel.
  • Formázod a kijelölt szöveget, és megtartod a meglévő stílust.
  • Keresel és kiemeled az összes találatot.
  • Menüt, névmezőt és About ablakot építesz.

Következő feladat: önálló SQLite-os alkalmazás

A szövegszerkesztő kész. A következő önálló feladatban adatbázissal dolgozol.