Grafikus C# – Windows Forms tanfolyam › 2.14 lecke Windows Forms · 2.14 lecke Nincs bosszantóbb, mint az elveszett munka. Ha a felhasználó mentés nélkül zárja be a szövegszerkesztőt, a program kérdezzen rá: mented a változtatásokat? Ebben a leckében a MiniWord megjegyzi, hogy módosult-e a dokumentum, és a bezárás, a megnyitás és az új dokumentum előtt megvédi a nem mentett munkát.Mentetlen dokumentum és a bezárás megerősítése

A lecke végére
- jelzőváltozóval (mező) megjegyzed, hogy a dokumentum módosult;
- a címsorban csillaggal jelzed a nem mentett állapotot;
- a
FormClosingeseménye.Canceltulajdonságával megállítod a bezárást; - Igen–Nem–Mégse kérdést teszel fel (
MessageBoxButtons.YesNoCancel); - a
Mentes()metódust úgy alakítod át, hogy visszaadja, sikerült-e a mentés; - ugyanazt a védelmet használod a megnyitás és az Új dokumentum előtt is.
Mielőtt elkezded
Ehhez a leckéhez kell a MiniWord a 2.13 lecke után, a bool visszatérési értékű metódus (1.11 lecke), a mező fogalma (1.12), a DialogResult (1.7) és a Form életciklusa (0.8).
Megjegyezzük, hogy módosult a dokumentum
A programnak tudnia kell, hogy az utolsó mentés óta történt-e változás. Ehhez egy bool típusú mezőt használunk (1.12 lecke), amelyet a módosításkor true, a mentéskor és a megnyitáskor false értékre állítunk. A címsorban egy csillag (*) jelzi a nem mentett állapotot, mint a legtöbb szerkesztőben:
private bool valtozott = false;
private void Modositva()
{
if (!valtozott)
{
valtozott = true;
this.Text = "MiniWord *";
}
}
private void Mentve()
{
valtozott = false;
this.Text = "MiniWord";
}
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
lblKarakterszam.Text = $"Karakterek: {rtbDokumentum.Text.Length}";
Modositva();
}A formázás nem biztos, hogy számít
Ha a felhasználó csak formáz (félkövér, igazítás), a szöveg nem változik, ezért a TextChanged esemény nem feltétlenül fut le. Ezért hívd meg a Modositva() metódust a formázó gombok eseménykezelőiből is (StilusValt, igazítás, betűtípus és méret). Ha a TextChanged amúgy is lefut, a duplázás nem baj, mert a metódus csak egyszer dolgozik.
Jósolj!
Megnyitsz egy RTF fájlt a LoadFile metódussal. A szöveg betöltése közben a TextChanged lefut. Milyen lesz a címsor a betöltés után, ha nem teszel semmit?
Jóslat ellenőrzése
A címsor MiniWord * lesz, mintha a dokumentum módosult volna, pedig csak megnyitottad. Ezért kell a LoadFile után meghívni a Mentve() metódust: az nullázza a jelzőt.
A Mentes() metódus válaszol: sikerült-e?
A bezárás előtt a program megkérdezi: mented? Ha a felhasználó az Igent választja, a program mentést kezd. De a mentés kudarcot is vallhat: a felhasználó a mentés ablakban a Mégse gombot nyomja, vagy a fájl nem írható. Ilyenkor a program nem zárhat be, mert elveszne a munka. Ezért a Mentes() metódusnak vissza kell adnia, hogy sikerült-e (az 1.11 leckében látott módon bool típussal):
private bool Mentes()
{
sfdMentes.Title = "Dokumentum mentése";
sfdMentes.Filter = "RTF dokumentum (*.rtf)|*.rtf";
sfdMentes.DefaultExt = "rtf";
sfdMentes.AddExtension = true;
if (sfdMentes.ShowDialog() != DialogResult.OK)
{
return false; // a felhasználó a Mégse gombot nyomta
}
try
{
rtbDokumentum.SaveFile(sfdMentes.FileName, RichTextBoxStreamType.RichText);
Mentve();
return true;
}
catch (Exception ex)
{
MessageBox.Show("Nem sikerült menteni a fájlt: " + ex.Message);
return false;
}
}void helyett bool: a metódus választ ad vissza.!= azt jelenti: nem egyenlő. Ha nem az OK gombot nyomták meg, azonnal kilépünk false értékkel.false értékkel tér vissza.A gombok és menüpontok ugyanúgy hívják
A Mentes() metódus ugyanúgy hívható a gombról és a menüpontból: Mentes(); A visszatérési értéket nem kötelező felhasználni. A megerősítő kérdésnél viszont szükségünk lesz rá. (A korábbi, void típusú változatot ez a metódus váltja le.)
A FormClosing esemény és az e.Cancel
A Form bezárása előtt lefut a FormClosing esemény (0.8 lecke). Az eseménykezelő második paramétere (FormClosingEventArgs e) lehetővé teszi a bezárás megállítását: ha az e.Cancel értékét true-ra állítod, az ablak nyitva marad. Írjunk egy FolytathatE() metódust, amely eldönti, hogy mehet-e tovább a program:
private bool FolytathatE()
{
if (!valtozott)
{
return true;
}
DialogResult valasz = MessageBox.Show(
"A dokumentum módosult. Mented a változtatásokat?",
"MiniWord",
MessageBoxButtons.YesNoCancel,
MessageBoxIcon.Question);
if (valasz == DialogResult.Yes)
{
return Mentes();
}
if (valasz == DialogResult.No)
{
return true;
}
return false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!FolytathatE())
{
e.Cancel = true;
}
}true).false).this.Close() is lefuttatja a FormClosing eseményt, ezért a menüpontban nem kell külön kérdezni (lásd a hibavadászatot).Jósolj!
A dokumentum módosult. Bezárod az ablakot, az Igen gombra kattintasz, a mentés ablakban viszont a Mégse gombot nyomod. Bezárul a program?
Jóslat ellenőrzése
Nem. A Mentes() false értéket ad vissza (a felhasználó megszakította), ezért a FolytathatE() is false, a FormClosing pedig e.Cancel = true értéket állít. Az ablak nyitva marad, a munka nem vész el.
Windows leállításakor ne kérdezz
Ha a számítógép leáll, a Windows bezárja a programokat. Ilyenkor a kérdés felesleges, és akadályozhatja a leállást. Az e.CloseReason megmondja az okot: a kérdést csak akkor tedd fel, ha a felhasználó zárja be az ablakot (e.CloseReason == CloseReason.UserClosing).
Mit tegyél ide: a MiniWord „MiniWord *” címsorral, és előtte a „A dokumentum módosult. Mented a változtatásokat?” kérdés Igen, Nem és Mégse gombbal.Javasolt fájlnév: winforms-2-14-mentes-kerdes.webp · Alt szöveg: A mentetlen dokumentum bezárásakor megjelenő Igen–Nem–Mégse kérdés
Ugyanaz a védelem a megnyitásnál és az Új dokumentumnál
A nem mentett munka nem csak a bezáráskor veszhet el: egy új fájl megnyitása vagy egy üres dokumentum indítása is felülírná a szöveget. Mindkettő elé ugyanaz a FolytathatE() ellenőrzés kerül:
private void Megnyitas()
{
if (!FolytathatE())
{
return;
}
// ... a 2.2 leckében megírt megnyitás ...
// a LoadFile után:
rtbDokumentum.ClearUndo();
Mentve();
}
private void miUj_Click(object sender, EventArgs e)
{
if (FolytathatE())
{
rtbDokumentum.Clear();
rtbDokumentum.ClearUndo();
Mentve();
}
}TextChanged eseményt, ezért kell utána a Mentve().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. Bezár, pedig a Mégse gombot választottam
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
FolytathatE();
}Hiba és javítás
Rossz működés: a metódus visszaadott értéke nincs felhasználva, és az e.Cancel sincs beállítva, ezért az ablak mindenképpen bezárul. Helyesen: if (!FolytathatE()) { e.Cancel = true; }
2. Megnyitás után csillag a címsorban
Egy fájl megnyitása után a címsor „MiniWord *” lesz. A Ok és javítás
LoadFile közben a TextChanged lefut, és a jelző igazra áll. A betöltés után hívd meg a Mentve() metódust.
3. Mentés nélkül is bezárul
A felhasználó az Igen gombot választja, a mentési ablakban a Mégse gombot, a program mégis bezár. A Ok és javítás
Mentes() metódus mindig true értéket ad vissza, vagy void típusú. A mentés ablak Mégse gombjánál false kell visszatérnie: if (sfdMentes.ShowDialog() != DialogResult.OK) { return false; }
4. Kétszer kérdez
A Kilépés menüpont után a kérdés kétszer jelenik meg. A Ok és javítás
miKilepes_Click maga is megerősítést kér, majd a this.Close() kiváltja a FormClosing eseményt, amely újra kérdez. Hagyd meg a FormClosing védelmet, és a menüpontban csak a this.Close(); maradjon.
5. A formázás után nincs csillag
Félkövérre formázol egy szót, de a címsor nem változik. A formázás nem biztos, hogy kiváltja a Ok és javítás
TextChanged eseményt. Hívd meg a Modositva() metódust a formázó gombok eseménykezelőiből is.
Mini projekt: a MiniWord nem engedi elveszni a munkát
Építsd be a védelmet a MiniWordbe:
- Mező és két metódus:
valtozott,Modositva(),Mentve(). ATextChangedés a formázó gombok hívják aModositva()metódust. - A
Mentes()booltípusú, az 1. szakasz szerint. - A
FolytathatE()metódus és aForm1_FormClosinga 3. szakasz kódja szerint. - A
Megnyitas()és az Új menüpont a 4. szakasz szerint védett. - A
miKilepes_Clickcsak athis.Close();sort tartalmazza.
Mit tegyél ide: két kép egymás mellett: a mentett állapotú MiniWord („MiniWord” címsor) és a módosított állapotú („MiniWord *” címsor).Javasolt fájlnév: winforms-2-14-cimsor.webp · Alt szöveg: A MiniWord címsora mentett és módosított állapotban
Három önálló gyakorlófeladat
Egyszerre csak egyet csinálj: tervezd meg, írd meg, futtasd, ellenőrizd, és csak utána nyisd ki a megoldást.
1. A fájl neve a címsorban
A címsor mutassa a megnyitott vagy elmentett fájl nevét: MiniWord – level.rtf, módosítva csillaggal. A Egy lehetséges megoldás
private string fajlNev = "Névtelen";
private void CimFrissit()
{
string cim = "MiniWord – " + fajlNev;
if (valtozott)
{
cim = cim + " *";
}
this.Text = cim;
}Modositva() és a Mentve() a this.Text beállítása helyett a CimFrissit() metódust hívja. A fájl nevét System.IO.Path.GetFileName(fajl) adja meg, amelyet a megnyitás és a mentés után tárolsz a fajlNev mezőben.
2. Állapot az állapotsorban
Az lblAllapot feliraton jelenjen meg: Módosítva vagy Mentve.Egy lehetséges megoldás
// a Modositva() metódusban:
lblAllapot.Text = "Módosítva";
// a Mentve() metódusban:
lblAllapot.Text = "Mentve";
3. Windows leállítása
Úgy módosítsd a FormClosing eseménykezelőt, hogy a kérdést csak akkor tegye fel, ha a felhasználó zárja be az ablakot.Egy lehetséges megoldás
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.UserClosing)
{
return;
}
if (!FolytathatE())
{
e.Cancel = true;
}
}
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. A feladatok a MiniWord mentetlen munka elleni védelmét gyakoroltatják. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. A módosítás jelzése
Készíts egy A jelző mezőben él (a metódusok között is megmarad). A valtozott jelzőt, amely igaz lesz, ha a szöveg módosult. A címsorban jelenjen meg ilyenkor egy csillag (MiniWord *).Megoldás megnyitása
TextChanged állítja igazra, és a címsort is frissíti.private bool valtozott = false;
private void rtbDokumentum_TextChanged(object sender, EventArgs e)
{
if (!valtozott)
{
valtozott = true;
this.Text = "MiniWord *";
}
}
2. A bezárás megállítása
Ha a dokumentum módosult, a bezárás előtt jelenjen meg egy kérdés: Biztosan kilépsz mentés nélkül? Nem esetén az ablak maradjon nyitva. A Megoldás megnyitása
FormClosing esemény a bezárás előtt fut. Az e.Cancel = true megállítja a bezárást.private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!valtozott)
{
return;
}
DialogResult valasz = MessageBox.Show("Biztosan kilépsz mentés nélkül?", "MiniWord", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (valasz == DialogResult.No)
{
e.Cancel = true;
}
}
Közepes szint
3. Igen–Nem–Mégse
Alakítsd át a kérdést háromgombossá: Mented a változtatásokat? Igen: mentés, Nem: bezárás mentés nélkül, Mégse: maradás. A mentés sikertelensége esetén az ablak maradjon nyitva. A Megoldás megnyitása
Mentes() bool értéket ad vissza: igaz, ha a mentés sikerült. Csak ekkor engedjük a bezárást. A Mégse és a Nem gomb ágai eltérnek: a Nem a változtatásokat eldobja, a Mégse megállítja a bezárást.private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!valtozott)
{
return;
}
DialogResult valasz = MessageBox.Show("Mented a változtatásokat?", "MiniWord", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (valasz == DialogResult.Cancel)
{
e.Cancel = true;
}
else if (valasz == DialogResult.Yes)
{
if (!Mentes())
{
e.Cancel = true;
}
}
}
4. Védett Új dokumentum
Az Új menüpont törölje a szöveget, de módosított dokumentumnál előbb kérdezzen rá a mentésre ugyanazzal a logikával, mint a bezárásnál. A kérdés logikáját külön metódusba tesszük (Megoldás megnyitása
FolytathatE()), amelyet a bezárás és az Új menüpont is hív. Igaz értéket ad, ha folytathatjuk (nincs mit menteni, mentettünk, vagy eldobtuk a változtatásokat).private bool FolytathatE()
{
if (!valtozott)
{
return true;
}
DialogResult valasz = MessageBox.Show("Mented a változtatásokat?", "MiniWord", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (valasz == DialogResult.Yes)
{
return Mentes();
}
return valasz == DialogResult.No;
}
private void miUj_Click(object sender, EventArgs e)
{
if (FolytathatE())
{
rtbDokumentum.Clear();
valtozott = false;
this.Text = "MiniWord";
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (!FolytathatE())
{
e.Cancel = true;
}
}
Kihívás szint
5. Automatikus mentés
Egy Az automatikus mentés biztonsági másolat: a felhasználó saját fájlját nem írja felül, hanem az ideiglenes mappába ment (tmrAutoMentes Timer 30 másodpercenként mentse a módosított dokumentumot egy ideiglenes fájlba (a felhasználó fájlját ne írja felül). Hogyan és hová?Megoldás megnyitása
Path.GetTempPath()). A Timer Interval értéke 30000 (ezredmásodperc). Hiba esetén csendben kihagyja a mentést, nem zavarja a felhasználót hibaüzenettel.private void tmrAutoMentes_Tick(object sender, EventArgs e)
{
if (!valtozott)
{
return;
}
try
{
string utvonal = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "miniword_biztonsagi.rtf");
rtbDokumentum.SaveFile(utvonal, RichTextBoxStreamType.RichText);
}
catch (Exception)
{
// a biztonsági mentés sikertelensége nem zavarja a munkát
}
}
6. Üres dokumentumnál nincs kérdés
Ha a dokumentum üres (nincs benne szöveg), a bezáráskor ne kérdezzen a program akkor sem, ha a jelző szerint módosult (például beírt, majd mindent kitörölt). Hogyan módosítod a A feltételt kiegészítjük egy második vizsgálattal: ha nincs módosítás, vagy a szöveg üres, nincs mit menteni. Ez a tervezői döntés a program viselkedését egyszerűsíti. Figyelem: formázott üres dokumentumnál ezzel elvész a beállított formázás, ezért vitatható döntés.FolytathatE() metódust?Megoldás megnyitása
private bool FolytathatE()
{
if (!valtozott || rtbDokumentum.TextLength == 0)
{
return true;
}
DialogResult valasz = MessageBox.Show("Mented a változtatásokat?", "MiniWord", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (valasz == DialogResult.Yes)
{
return Mentes();
}
return valasz == DialogResult.No;
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Megjegyzi, hogy az utolsó mentés óta módosult-e a dokumentum. Megállítja a bezárást: az ablak nyitva marad. Hogy a hívó tudja, sikerült-e a mentés. Ha nem, a bezárás nem folytatódhat. Igen: mentés (a mentés eredményét adja vissza). Nem: a változtatások eldobása, mehet tovább. Mégse: ne menjen tovább. Mert a betöltés kiváltja a TextChanged eseményt, és enélkül a frissen megnyitott dokumentum is módosítottnak látszana. Mert a 1. Mire való a valtozott mező?
2. Mit csinál az e.Cancel = true?
3. Miért kell a Mentes() metódusnak bool értéket visszaadnia?
4. Mit jelent az Igen, a Nem és a Mégse a FolytathatE() metódusban?
5. Miért kell a Mentve() hívás a LoadFile után?
6. Miért nem kell a Kilépés menüpontban külön megerősítés?
this.Close() kiváltja a FormClosing eseményt, amely már elvégzi az ellenőrzést.
Mit tudsz már?
- Jelzővel követed a mentetlen módosítást.
- A FormClosing eseményben megállítod a bezárást.
- Igen–Nem–Mégse kérdést teszel fel és kezelsz.
- A Mentes() metódus válaszol, hogy sikerült-e.
- Ugyanazt a védelmet használod a megnyitásnál és az Új dokumentumnál.
Következő lecke: nyomtatás és nyomtatási előnézet
A munka védett. A következő leckében a MiniWord dokumentumát ki is tudjuk nyomtatni.




