Grafikus C# – Windows Forms tanfolyam › 10.4 lecke Windows Forms · 10.4 lecke · párbeszédablakok A 7.8 leckében az Fájl- és mappaválasztó párbeszédablakok
OpenFileDialog, a SaveFileDialog és a FolderBrowserDialog alapjait már megismerted. Egy valódi programban ugyanazt a kódot sok helyen írnád újra: ugyanaz a szűrő, ugyanaz a kezdőmappa, ugyanaz a hibakezelés. Ebben a leckében a fájlválasztást újrahasznosítható segédosztályba szervezed, megjegyezteted a felhasználó utoljára használt mappáját, ellenőrzöd az útvonalat (létezik-e, írható-e), kezeled a legutóbbi fájlok listáját és a húzással megnyitást. Ez a program kényelmes és megbízható használatát adja.
A lecke végére
- segédosztályba szervezed a fájlválasztást (szűrők, kezdőmappa,
owner); - megjegyzed az utolsó mappát és a legutóbbi fájlokat (listaként, a következő indításig);
- ellenőrzöd, hogy a választott fájl létezik, olvasható, és a cél mappa írható;
- a
FilterIndexalapján választod a formátumot, és kezeled a kiterjesztést; - húzással és a parancssori argumentummal (Megnyitás ezzel) nyitsz meg fájlt;
- elkerülöd a tipikus hibákat (hiányzó
owner, munkamappa változása,Disposehiánya).
Mielőtt elkezded
Ehhez a leckéhez a 7.8 lecke (a három párbeszédablak alapjai), a 10.1 lecke (Path, Directory) és a 7.1 lecke (menü) szükséges.
Fájlválasztó segédosztály
Egy statikus osztály, amely a program összes fájlválasztását egy helyre gyűjti: a szűrők, a cím és a hibatűrés egységesek, a hívó egy sorban kéri a fájlt. A párbeszédablakot using blokkban hozza létre (9.8 lecke), és mindig megkapja a owner ablakot (a párbeszéd az alkalmazás ablakához kötődik, nem „lóg” mellé):
public static class FajlValaszto
{
public const string JegyzetSzuro = "Jegyzet (*.txt;*.md)|*.txt;*.md|Minden fájl (*.*)|*.*";
public const string KepSzuro = "Képek (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp";
// egy fájl megnyitása: null, ha a felhasználó megszakította
public static string Megnyit(IWin32Window tulajdonos, string szuro, string kezdoMappa, string cim = "Megnyitás")
{
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Title = cim;
ofd.Filter = szuro;
ofd.CheckFileExists = true;
ofd.CheckPathExists = true;
ofd.RestoreDirectory = true; // a munkamappát nem változtatja meg (10.1 lecke csapdája)
if (kezdoMappa != null && Directory.Exists(kezdoMappa)) { ofd.InitialDirectory = kezdoMappa; }
return ofd.ShowDialog(tulajdonos) == DialogResult.OK ? ofd.FileName : null;
}
}
// mentés: a szűrő sorszáma alapján a kiterjesztés; a felülírás-kérdést a párbeszéd teszi fel
public static string Ment(IWin32Window tulajdonos, string szuro, string kezdoMappa, string javasoltNev, out int szuroIndex)
{
szuroIndex = 0;
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = szuro;
sfd.FileName = javasoltNev;
sfd.AddExtension = true; // a hiányzó kiterjesztést a szűrő szerint kiegészíti
sfd.OverwritePrompt = true;
sfd.RestoreDirectory = true;
if (kezdoMappa != null && Directory.Exists(kezdoMappa)) { sfd.InitialDirectory = kezdoMappa; }
if (sfd.ShowDialog(tulajdonos) != DialogResult.OK) { return null; }
szuroIndex = sfd.FilterIndex; // 1-től számozott: melyik formátumot választotta
return sfd.FileName;
}
}
public static string Mappa(IWin32Window tulajdonos, string kezdoMappa, string leiras)
{
using (FolderBrowserDialog fbd = new FolderBrowserDialog())
{
fbd.Description = leiras;
fbd.ShowNewFolderButton = true;
if (kezdoMappa != null && Directory.Exists(kezdoMappa)) { fbd.SelectedPath = kezdoMappa; }
return fbd.ShowDialog(tulajdonos) == DialogResult.OK ? fbd.SelectedPath : null;
}
}
}
// használat a Formban: egy sor, nincs ismétlődő beállítás
string ut = FajlValaszto.Megnyit(this, FajlValaszto.JegyzetSzuro, utolsoMappa);
if (ut != null) { Betolt(ut); }RestoreDirectory és az owner beállítása ismétlődik. Egy helyen javítod: egységes viselkedés, kevesebb hiba. A kapott érték null a megszakításra (egyszerűbb, mint a DialogResult kezelése minden hívásnál).ShowDialog(this) az ablakhoz köti a párbeszédet: a háttér letiltott, a párbeszéd nem tűnik el az ablak mögött. A paraméter nélküli ShowDialog() gyakori hiba (a párbeszéd egy másik ablak mögé kerülhet).RestoreDirectory = true megakadályozza, hogy a párbeszéd a program munkamappáját átállítsa (10.1 lecke csapdája).FilterIndex 1-től számozott: a mentésnél ebből tudod, melyik formátumot kérte a felhasználó (a szűrő sorrendje a program döntése).Jósolj!
Az OpenFileDialog-ot RestoreDirectory = false (alapértelmezett) beállítással nyitja meg a felhasználó, kiválaszt egy fájlt a Letöltések mappában, majd a program a File.WriteAllText("beallitas.cfg", ...) relatív útvonalra ment. Hová kerül a fájl?
Jóslat ellenőrzése
Gyakran a Letöltések mappába: a párbeszéd megváltoztathatja a folyamat munkamappáját. A RestoreDirectory = true megakadályozza, és az adatot teljes útvonalra kell menteni (10.1 lecke).
Az utolsó mappa megjegyzése
A felhasználó megszokja, hogy a program ott nyílik meg, ahol utoljára dolgozott. A mappát az alkalmazás beállításaiban tárolod (10.8 lecke: Properties.Settings); addig is egy egyszerű szöveges fájl is megteszi:
private string utolsoMappa = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
private void miMegnyit_Click(object sender, EventArgs e)
{
string ut = FajlValaszto.Megnyit(this, FajlValaszto.JegyzetSzuro, utolsoMappa);
if (ut == null) { return; }
utolsoMappa = Path.GetDirectoryName(ut); // a következő párbeszéd itt nyílik
Betolt(ut);
}Directory.Exists értéket, és ha nincs, a párbeszéd az alapértelmezett helyen nyílik.Jósolj!
Az utolsó mappa egy hálózati meghajtó (Z:\Munka), amely a következő indításkor nem elérhető. A program InitialDirectory = utolsoMappa beállítással nyitja meg a párbeszédet, ellenőrzés nélkül. Mi történik?
Jóslat ellenőrzése
A párbeszéd a nem létező kezdőmappát figyelmen kívül hagyja (a rendszer alapértelmezett mappájában nyílik), vagy lassan, hálózati időtúllépés után nyílik meg. Az ellenőrzés (Directory.Exists) hálózati meghajtónál is lassú lehet: a program rövid ideig „lefagy”.
Útvonal-ellenőrzés: olvasható, írható
A CheckFileExists a választáskor ellenőriz, de a fájl a választás és a megnyitás között változhat. Mentésnél érdemes előre kipróbálni, hogy a cél mappa írható-e (különben a felhasználó hosszú munka végén kap hibát):
// érvényes-e az útvonal és a fájlnév? (hiba esetén a szövege a hibaüzenet)
private bool UtvonalEllenorzes(string ut, out string hiba)
{
hiba = "";
if (string.IsNullOrWhiteSpace(ut)) { hiba = "Nincs megadva fájl."; return false; }
if (ut.IndexOfAny(Path.GetInvalidPathChars()) >= 0) { hiba = "Az útvonal érvénytelen karaktert tartalmaz."; return false; }
string mappa = Path.GetDirectoryName(Path.GetFullPath(ut));
if (!Directory.Exists(mappa)) { hiba = "A mappa nem létezik."; return false; }
return true;
}
// írásteszt: ideiglenes fájl létrehozása a célmappában
private bool MappaIrhato(string mappa)
{
try
{
string proba = Path.Combine(mappa, Guid.NewGuid().ToString("N") + ".tmp");
using (FileStream fs = new FileStream(proba, FileMode.CreateNew, FileAccess.Write, FileShare.None, 1, FileOptions.DeleteOnClose)) { }
return true;
}
catch (UnauthorizedAccessException) { return false; }
catch (IOException) { return false; }
}FileOptions.DeleteOnClose az ideiglenes fájlt a lezáráskor automatikusan törli, ezért nem marad szemét a mappában, még hiba esetén sem.try–catch védje (10.9 lecke); az előzetes teszt a felhasználó kényelmét szolgálja.GetFullPath érvénytelen karakternél ArgumentException kivételt dob, ezért előbb a GetInvalidPathChars ellenőrzés. A párbeszéd maga érvényes útvonalat ad; a kézzel beírt vagy a fájlból olvasott útvonalat kell védeni.PathTooLongException). A mélyen egymásba ágyazott mappák ezt átléphetik: a hibát érthetően jelezd.Jósolj!
A felhasználó a C:\Program Files mappát választja mentési helynek egy átlagos (nem rendszergazda) fiókkal. A SaveFileDialog elfogadja, és a File.WriteAllText hívás fut le. Mi történik?
Jóslat ellenőrzése
UnauthorizedAccessException (vagy a rendszer a fájlt a VirtualStore mappába irányítja régebbi programoknál). Az előzetes MappaIrhato teszt jelezhette volna, mielőtt a felhasználó a munkáját „elmentettnek” gondolja.
Legutóbbi fájlok és húzással megnyitás
private const int MaxLegutobbi = 8;
private List<string> legutobbi = new List<string>();
// egy fájl megnyitása után: előre kerül, nincs duplikátum, korlátozott darabszám
private void LegutobbiHozzaad(string ut)
{
legutobbi.RemoveAll(x => string.Equals(x, ut, StringComparison.OrdinalIgnoreCase)); // a Windows útvonalai betűnagyság-függetlenek
legutobbi.Insert(0, ut);
if (legutobbi.Count > MaxLegutobbi) { legutobbi.RemoveRange(MaxLegutobbi, legutobbi.Count - MaxLegutobbi); }
LegutobbiMenuFrissit();
}
private void LegutobbiMenuFrissit()
{
miLegutobbi.DropDownItems.Clear();
for (int i = 0; i < legutobbi.Count; i++)
{
string ut = legutobbi[i];
ToolStripMenuItem elem = new ToolStripMenuItem("&" + (i + 1) + " " + Path.GetFileName(ut));
elem.ToolTipText = ut;
elem.Click += (s, ev) =>
{
if (File.Exists(ut)) { Betolt(ut); }
else
{
MessageBox.Show(this, "A fájl már nem létezik, eltávolítom a listáról.", "Megnyitás");
legutobbi.Remove(ut);
LegutobbiMenuFrissit();
}
};
miLegutobbi.DropDownItems.Add(elem);
}
miLegutobbi.Enabled = legutobbi.Count > 0;
}
// húzással megnyitás (AllowDrop = true a Formon)
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
string[] fajlok = (string[])e.Data.GetData(DataFormats.FileDrop);
if (fajlok.Length > 0 && File.Exists(fajlok[0])) { Betolt(fajlok[0]); }
}
// a program indításakor: a parancssori argumentum (a „Megnyitás ezzel” vagy dupla kattintás átadja a fájlt)
// Program.cs: Application.Run(new Form1(args.Length > 0 ? args[0] : null));OrdinalIgnoreCase, különben ugyanaz a fájl kétszer szerepelne.FileDrop a húzott fájlok útvonalait adja: bármi lehet (mappa, nem támogatott típus). A File.Exists és a típus ellenőrzése kötelező.Main(string[] args) így továbbadható a Form1 konstruktorának.Jósolj!
A legutóbbi lista kezelése legutobbi.Add(ut) (a végére), ellenőrzés nélkül. A felhasználó háromszor nyitja meg ugyanazt a fájlt. Mit mutat a menü?
Jóslat ellenőrzése
Ugyanazt a fájlt háromszor, és a legutóbb megnyitott a lista végén áll. A RemoveAll és az Insert(0, ...) páros kiszűri a duplikátumot, és az újat előre teszi.
Mit tegyél ide: a dokumentumkezelő Fájl menüje a legutóbbi fájlok listájával, és egy ablak, amelyre fájlt húznak.Javasolt fájlnév: winforms-10-4-legutobbi.webp · Alt szöveg: Legutóbbi fájlok menü és húzással megnyitás
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. A párbeszéd az ablak mögé kerül
ofd.ShowDialog(); // owner nélkülHiba és javítás
Add át a tulajdonos ablakot: ofd.ShowDialog(this). Így a párbeszéd az ablak előtt marad, és a háttér letiltott.
2. A beállítás-fájl rossz helyre kerül a párbeszéd után
Fájl megnyitása után a relatív útvonalú mentés „eltűnik”. A Ok és javítás
RestoreDirectory hamis: a munkamappa megváltozott. Állítsd true értékre, és teljes útvonalat használj (10.1 lecke).
3. Dupla kiterjesztés: jegyzet.txt.txt
string ut = sfd.FileName + ".txt"; // a felhasználó már beírta a .txt-tHiba és javítás
Az AddExtension = true és a DefaultExt a hiányzó kiterjesztést adja; kézi hozzáfűzés előtt ellenőrizd a Path.GetExtension(ut) értékét.
4. A felhasználó a „Minden fájl” szűrőnél nem támogatott fájlt nyit meg
A program egy .exe fájlt próbál szövegként beolvasni. A „Minden fájl” szűrő bármit enged. A betöltés előtt ellenőrizd a kiterjesztést vagy a tartalmat (10.9 lecke), és érthető hibát mutass.Ok és javítás
5. Nincs Mégse kezelés
ofd.ShowDialog(this);
Betolt(ofd.FileName); // Mégse esetén üres útvonalHiba és javítás
Vizsgáld a DialogResult.OK eredményt (vagy a segédosztály null visszatérését).
6. A legutóbbi fájlok lista nem őrződik meg
Újraindítás után üres a lista. A listát csak a memóriában tartod. Mentsd el a beállításokba (10.8 lecke) vagy fájlba a Ok és javítás
FormClosing-ben, és töltsd be induláskor.
Mini projekt: dokumentumkezelő segédosztállyal
Készíts egyszerű szövegszerkesztőt, amelynek a fájlkezelése tiszta és megbízható:
- Felület: menü (Fájl: Új, Megnyitás..., Legutóbbi fájlok, Mentés, Mentés másként..., Kilépés),
txtSzoveg(többsoros,Dock = Fill), állapotsor (fájlnév, mappa, módosított jelző). - Segédosztály: a lecke
FajlValasztoosztálya (Megnyit,Ment,Mappa); minden párbeszéd ezen megy át. - Megnyitás: kiterjesztés-szűrő (.txt, .md); a betöltés előtt a méret ellenőrzése (> 5 MB megerősítés); a kódolás felismerése (10.3 lecke: BOM → érvényes UTF-8 → Windows-1250); hibatűrés (
try–catch). - Mentés és Mentés másként: a Mentés a megnyitott fájlba ír, ha nincs, a Mentés másként ablakot nyitja; a
MappaIrhatoelőzetes teszt; a mentés kódolása UTF-8 (BOM nélkül), aFilterIndexszerint .txt vagy .md; a felülírás kérdése a párbeszéden. - Módosított jelző: a cím
*jelet kap, a kilépés/új/megnyitás előtt megerősítés (Igen/Nem/Mégse, 7.4 lecke). - Utolsó mappa: a megnyitás és a mentés közös
utolsoMappaértékét a program megjegyzi; ha a mappa megszűnt, az alapértelmezett helyen nyílik. - Legutóbbi fájlok: legfeljebb 8, duplikátum nélkül, előre kerül az új; megszűnt fájlnál a program eltávolítja; a lista a
legutobbi.txtfájlban az adatmappában marad (induláskor betöltve, kilépéskor mentve: 10.1 leckeAdatMappa()). - Húzás és parancssor: a fájl az ablakra húzással megnyílik; a program első parancssori argumentumát is megnyitja.
- Teszteld: Mégse minden párbeszédnél, nem létező és csak olvasható mappa, fájl megnyitása időközben törölt mappából, ugyanaz a fájl többször (lista), kiterjesztés nélküli név mentése, húzás mappával, nagy fájl, ékezetes és Windows-1250 fájl.
Megoldás megnyitása (próbáld ki előbb önállóan!)
A segédosztály a leckében látható. Itt a Form vezérlése és a legutóbbi fájlok tárolása áll.
private string fajl = "";
private bool modosult = false;
private string utolsoMappa = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
private List<string> legutobbi = new List<string>();
private string indulo;
public Form1(string induloFajl)
{
InitializeComponent();
indulo = induloFajl;
this.AllowDrop = true;
}
private string AdatFajl()
{
string mappa = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MiniSzerkeszto");
Directory.CreateDirectory(mappa);
return Path.Combine(mappa, "legutobbi.txt");
}
private void Form1_Load(object sender, EventArgs e)
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
try { if (File.Exists(AdatFajl())) { legutobbi = File.ReadAllLines(AdatFajl()).Where(s => s.Trim().Length > 0).Take(8).ToList(); } }
catch (IOException) { } // a lista nem létfontosságú
LegutobbiMenuFrissit();
CimFrissit();
if (indulo != null && File.Exists(indulo)) { Betolt(indulo); }
}
private void CimFrissit()
{
this.Text = (fajl.Length == 0 ? "Névtelen" : Path.GetFileName(fajl)) + (modosult ? "*" : "") + " – Szerkesztő";
tsslFajl.Text = fajl.Length == 0 ? "Nincs mentve" : fajl;
}
private bool Folytathat()
{
if (!modosult) { return true; }
switch (MessageBox.Show(this, "Mented a változtatásokat?", "Szerkesztő", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Warning))
{
case DialogResult.Yes: return Ment(false);
case DialogResult.No: return true;
default: return false;
}
}
private void txtSzoveg_TextChanged(object sender, EventArgs e)
{
if (!modosult) { modosult = true; CimFrissit(); }
}
private bool Betolt(string ut)
{
if (!Folytathat()) { return false; }
try
{
FileInfo fi = new FileInfo(ut);
if (fi.Length > 5 * 1024 * 1024 && MessageBox.Show(this, "A fájl nagyobb 5 MB-nál. Betöltsem?", "Nagy fájl", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) != DialogResult.Yes) { return false; }
byte[] bajtok = File.ReadAllBytes(ut);
Encoding kod;
if (bajtok.Length >= 3 && bajtok[0] == 0xEF && bajtok[1] == 0xBB && bajtok[2] == 0xBF) { kod = new UTF8Encoding(true); }
else
{
try { new UTF8Encoding(false, true).GetString(bajtok); kod = new UTF8Encoding(false); }
catch (DecoderFallbackException) { kod = Encoding.GetEncoding(1250); }
}
string szoveg = kod.GetString(bajtok);
if (szoveg.Length > 0 && szoveg[0] == '\uFEFF') { szoveg = szoveg.Substring(1); } // a BOM karakter levágása
txtSzoveg.TextChanged -= txtSzoveg_TextChanged;
txtSzoveg.Text = szoveg;
txtSzoveg.TextChanged += txtSzoveg_TextChanged;
fajl = ut;
modosult = false;
utolsoMappa = Path.GetDirectoryName(ut);
LegutobbiHozzaad(ut);
CimFrissit();
return true;
}
catch (IOException ex) { MessageBox.Show(this, "A fájl nem olvasható: " + ex.Message, "Megnyitás", MessageBoxButtons.OK, MessageBoxIcon.Error); }
catch (UnauthorizedAccessException) { MessageBox.Show(this, "Nincs jogosultság a fájlhoz.", "Megnyitás", MessageBoxButtons.OK, MessageBoxIcon.Error); }
return false;
}
private bool Ment(bool maskent)
{
string ut = fajl;
if (maskent || ut.Length == 0)
{
int szuroIndex;
ut = FajlValaszto.Ment(this, "Szöveg (*.txt)|*.txt|Markdown (*.md)|*.md", utolsoMappa, fajl.Length == 0 ? "jegyzet" : Path.GetFileNameWithoutExtension(fajl), out szuroIndex);
if (ut == null) { return false; }
if (Path.GetExtension(ut).Length == 0) { ut += szuroIndex == 2 ? ".md" : ".txt"; }
}
string mappa = Path.GetDirectoryName(ut);
if (!MappaIrhato(mappa)) { MessageBox.Show(this, "Ebbe a mappába nem lehet írni. Válassz másik helyet.", "Mentés", MessageBoxButtons.OK, MessageBoxIcon.Warning); return false; }
try
{
File.WriteAllText(ut, txtSzoveg.Text, new UTF8Encoding(false));
fajl = ut;
modosult = false;
utolsoMappa = mappa;
LegutobbiHozzaad(ut);
CimFrissit();
return true;
}
catch (IOException ex) { MessageBox.Show(this, "A mentés nem sikerült: " + ex.Message, "Mentés", MessageBoxButtons.OK, MessageBoxIcon.Error); }
catch (UnauthorizedAccessException) { MessageBox.Show(this, "Nincs jogosultság a fájlhoz.", "Mentés", MessageBoxButtons.OK, MessageBoxIcon.Error); }
return false;
}
private bool MappaIrhato(string mappa)
{
try
{
string proba = Path.Combine(mappa, Guid.NewGuid().ToString("N") + ".tmp");
using (FileStream fs = new FileStream(proba, FileMode.CreateNew, FileAccess.Write, FileShare.None, 1, FileOptions.DeleteOnClose)) { }
return true;
}
catch (UnauthorizedAccessException) { return false; }
catch (IOException) { return false; }
}
private void miMegnyit_Click(object sender, EventArgs e)
{
string ut = FajlValaszto.Megnyit(this, FajlValaszto.JegyzetSzuro, utolsoMappa);
if (ut != null) { Betolt(ut); }
}
private void miMent_Click(object sender, EventArgs e) { Ment(false); }
private void miMentMaskent_Click(object sender, EventArgs e) { Ment(true); }
private void miUj_Click(object sender, EventArgs e)
{
if (!Folytathat()) { return; }
txtSzoveg.TextChanged -= txtSzoveg_TextChanged;
txtSzoveg.Clear();
txtSzoveg.TextChanged += txtSzoveg_TextChanged;
fajl = "";
modosult = false;
CimFrissit();
}
private void LegutobbiHozzaad(string ut)
{
legutobbi.RemoveAll(x => string.Equals(x, ut, StringComparison.OrdinalIgnoreCase));
legutobbi.Insert(0, ut);
if (legutobbi.Count > 8) { legutobbi.RemoveRange(8, legutobbi.Count - 8); }
LegutobbiMenuFrissit();
}
private void LegutobbiMenuFrissit()
{
miLegutobbi.DropDownItems.Clear();
for (int i = 0; i < legutobbi.Count; i++)
{
string ut = legutobbi[i];
ToolStripMenuItem elem = new ToolStripMenuItem("&" + (i + 1) + " " + Path.GetFileName(ut)) { ToolTipText = ut };
elem.Click += (s, ev) =>
{
if (File.Exists(ut)) { Betolt(ut); }
else { MessageBox.Show(this, "A fájl már nem létezik, eltávolítom a listáról.", "Megnyitás"); legutobbi.Remove(ut); LegutobbiMenuFrissit(); }
};
miLegutobbi.DropDownItems.Add(elem);
}
miLegutobbi.Enabled = legutobbi.Count > 0;
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.Data.GetDataPresent(DataFormats.FileDrop) ? DragDropEffects.Copy : DragDropEffects.None;
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
string[] fajlok = (string[])e.Data.GetData(DataFormats.FileDrop);
if (fajlok.Length > 0 && File.Exists(fajlok[0])) { Betolt(fajlok[0]); }
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing && !Folytathat()) { e.Cancel = true; return; }
try { File.WriteAllLines(AdatFajl(), legutobbi); }
catch (IOException) { } // a lista mentése nem akadályozhatja a kilépést
}Mit tegyél ide: a kész szövegszerkesztő a Fájl menüvel és az állapotsorral.Javasolt fájlnév: winforms-10-4-kesz.webp · Alt szöveg: A kész szövegszerkesztő
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. Többes fájlválasztás
Több képfájlt választ ki a felhasználó ( A Multiselect). Hogyan adja vissza a segédosztály, és mit tesz a nagy darabszámmal?Egy lehetséges megoldás
MegnyitTobb metódus string[] értéket ad (ofd.FileNames). A hívó korlátozhatja a darabszámot (például 200).public static string[] MegnyitTobb(IWin32Window tulajdonos, string szuro, string kezdoMappa){ using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.Filter = szuro; ofd.Multiselect = true; ofd.RestoreDirectory = true; if (kezdoMappa != null && Directory.Exists(kezdoMappa)) { ofd.InitialDirectory = kezdoMappa; } return ofd.ShowDialog(tulajdonos) == DialogResult.OK ? ofd.FileNames : null; }}
2. Mentés biztonsági másolattal
Mentés előtt a régi fájl Előbb a régi fájl másolása (.bak másolatot kap. Mi a sorrend, és mi történik hiba esetén?Egy lehetséges megoldás
File.Copy(ut, ut + ".bak", true)), utána az új tartalom írása; ha az írás hibázik, a .bak a régi állapotot őrzi. A biztonságosabb módszert (ideiglenes fájl + csere) a 10.9 lecke mutatja.
3. Fájltípus megjelenítése
A legutóbbi fájlok menüjében a fájl típusát ikon jelezze. Mit használsz?Egy lehetséges megoldás
Icon.ExtractAssociatedIcon(ut) (a fájlhoz társított ikon); az Icon erőforrás: a ToolStripMenuItem.Image beállítása után az ikont és a belőle készült képet is fel kell szabadítani (9.8 lecke). Nagy listánál gyorsítótárazd kiterjesztésenként.
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Szűrő összeállítása
Írd meg a szűrőt: Képek (png, jpg) és Minden fájl.Megoldás megnyitása
"Képek (*.png;*.jpg)|*.png;*.jpg|Minden fájl (*.*)|*.*": párosával: leírás|minta.
2. Mégse kezelése
Mit ad vissza az ofd.ShowDialog(this), ha a felhasználó a Mégse gombot nyomja?Megoldás megnyitása
DialogResult.Cancel; ilyenkor nem szabad a FileName értékét használni.
Közepes szint
3. Kiterjesztés kikényszerítése
A mentés csak .txt és .md fájlt engedjen. Mit teszel, ha a felhasználó .exe nevet ír? A kiterjesztés ellenőrzése (Megoldás megnyitása
Path.GetExtension, OrdinalIgnoreCase), nem engedélyezett esetén érthető üzenet és a párbeszéd újranyitása, vagy a kiterjesztés cseréje a szűrő szerinti értékre.
4. A legutóbbi lista tárolása
Hol tárolod a legutóbbi fájlok listáját, és miért nem a program mappájában? A felhasználó %AppData% mappájában (10.1 lecke): a program mappája telepítve általában nem írható, és a lista felhasználónként más.Megoldás megnyitása
Kihívás szint
5. Rögzített fájlok a listán
A felhasználó „kitűzhet” fájlokat a legutóbbi lista tetejére. Hogyan bővíted a modellt? Kis osztály (Megoldás megnyitása
LegutobbiElem { Ut, Kituzott }); a rendezés a kitűzöttek elöl, a többi dátum szerint; a limit csak a nem kitűzötteket érinti. Mentéskor a kitűzés jelzője (például * előtag, vagy JSON a 10.6 leckéből).
6. Fájl nyitva tartása közben megváltozik
A megnyitott fájlt egy másik program módosítja. Hogyan veszi észre a szerkesztő? A Megoldás megnyitása
FileSystemWatcher (Changed esemény) vagy a mentés előtti LastWriteTime összehasonlítás a betöltéskor tárolt értékkel; eltérésnél a program rákérdez: Felülírod a másik programban végzett módosítást?
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Az ismétlődő beállítások (szűrő, kezdőmappa, owner) egy helyen vannak; egységes és könnyen javítható. A párbeszéd az ablakhoz kötődik: nem kerül mögé, a háttér letiltott. Megakadályozza, hogy a párbeszéd átállítsa a program munkamappáját. Megmondja, melyik szűrő (formátum) volt kiválasztva (1-től számozva). Ideiglenes fájl létrehozásával ( Előre kerül az új, nincs duplikátum ( Bármi lehet (mappa, nem támogatott típus): a 1. Miért segédosztály?
2. Miért kell az owner?
3. Mit tesz a RestoreDirectory?
4. Mire jó a FilterIndex?
5. Hogyan ellenőrzöl írhatóságot?
DeleteOnClose); a tényleges mentést ettől függetlenül try–catch védi.6. Milyen szabályai vannak a legutóbbi listának?
OrdinalIgnoreCase), korlátozott darabszám, a megszűnt fájl eltávolítása.7. Miért kell a húzott fájlokat ellenőrizni?
File.Exists és a típusvizsgálat kötelező.
Mit tudsz már?
- A fájlválasztást egységes segédosztályba szervezed.
- Megjegyzed az utolsó mappát és a legutóbbi fájlokat.
- Ellenőrzöd az útvonalat és az írhatóságot, és kezeled a Mégse esetet.
- Húzással és parancssorral is megnyitsz fájlt.
- Elkerülöd a gyakori párbeszéd-hibákat.
Következő lecke: CSV fájl kezelése
A 8.9 lecke mélyítése: idézőjeles és többsoros mezők, elválasztó felismerése, oszlopok megfeleltetése, Excel-barát mentés és a képletbeszúrás elleni védelem.



