Grafikus C# – Windows Forms tanfolyam › 1.8 lecke Windows Forms · 1.8 lecke · önálló gyakorlás A folyamatjelző csík (ProgressBar) azt mutatja, hol tart egy művelet. A kézenfekvő megoldás, a ciklus és a várakozás azonban lefagyasztja az ablakot. Ebben a leckében megnézzük, miért, és megtanuljuk a helyes megoldást: az időzítőt (Timer).ProgressBar és Timer – a felület ne fagyjon be

A lecke végére
- a
ProgressBarMinimum,MaximumésValuetulajdonságát használod; - érted, miért fagyaszt be egy ablakot a ciklus és a
Thread.Sleep; - a
TimerkomponenstInterval,Start(),Stop()ésTicksegítségével használod; - az
Increment()metódussal biztonságosan növeled a folyamatjelzőt; - megállítható, újraindítható folyamatot készítesz;
- a gombot a futás idejére letiltod, utána visszakapcsolod.
Mielőtt elkezded
Ehhez a leckéhez kell az 1.7 lecke és az if–else ismerete. A for ciklust a lecke közben, egy kísérletnél egyszer használjuk, magyarázattal.
A felület és a ProgressBar
Tegyél az ablakra négy elemet:
ProgressBar (folyamatjelző). Az előtag pgb.Text: Indítás.Label, a Text: Készen áll.Timer komponens (Toolbox, Components csoport). Nem látható: az ablak alatti komponenssávban jelenik meg. Az előtag tmr.A ProgressBar legfontosabb tulajdonságai:
Blocks (blokkos), Continuous (folytonos), Marquee (végtelenített, nem mutat százalékot).Mit tegyél ide: a tervezőnézet a ProgressBarral, az Indítás gombbal és a felirattal. Az ablak alatt a komponenssávban a tmrFolyamat Timer látszik.Javasolt fájlnév: winforms-1-8-felulet.webp · Alt szöveg: Az ablak ProgressBarral, gombbal és a Timer komponenssel a tervezőben
Kísérlet: a ciklus lefagyasztja az ablakot
Először nézzük meg, mi történik a kézenfekvő, de hibás megoldással. A for ciklus ismétli a blokkot: itt a számláló 0-tól 100-ig megy, és minden körben egy kicsit vár a program (Thread.Sleep, milliszekundumban). A fájl elejére add hozzá a using System.Threading; sort.
private void btnInditas_Click(object sender, EventArgs e)
{
for (int i = 0; i <= 100; i++)
{
pgbFolyamat.Value = i;
Thread.Sleep(50);
}
}Jósolj, majd próbáld ki!
Mit gondolsz: a gombra kattintás után a csík szépen, fokozatosan telik meg? Mozgatható közben az ablak?
Mi történik valójában?
A csík nem telik meg fokozatosan: az ablak körülbelül öt másodpercre befagy (a címsorban „Nem válaszol” jelenhet meg), és csak a ciklus végén frissül egyszerre. Az ablak közben nem mozgatható. Ennek az az oka, hogy ugyanaz a szál rajzolja újra az ablakot, amelyik a ciklust futtatja. Amíg a ciklus fut, az ablak nem tud frissülni.
Miért baj ez?
A felhasználó azt hiszi, hogy a program lefagyott. A hosszú, várakozást vagy sok számolást végző kódnak ezért nem szabad az eseménykezelőben egyetlen ciklusban futnia. Később, a 11. fejezetben tanuljuk meg a háttérben futó műveleteket. Most egy egyszerűbb megoldás következik.
A Timer: egy lépés egy időzítésnél
A Timer komponens meghatározott időközönként kiváltja a Tick eseményt. Ahelyett, hogy egy ciklus várakozna, a Timer minden időzítéskor csak egyetlen kis lépést végrehajt, és visszaadja a vezérlést az ablaknak, amely közben frissül és mozgatható marad.
Tick eseményt időnként kiváltja.private void btnInditas_Click(object sender, EventArgs e)
{
pgbFolyamat.Value = 0;
lblAllapot.Text = "Folyamatban...";
tmrFolyamat.Start();
}
private void tmrFolyamat_Tick(object sender, EventArgs e)
{
if (pgbFolyamat.Value < pgbFolyamat.Maximum)
{
pgbFolyamat.Increment(2);
lblAllapot.Text = $"Folyamatban: {pgbFolyamat.Value}%";
}
else
{
tmrFolyamat.Stop();
lblAllapot.Text = "Kész!";
}
}Jósolj!
Az Interval értéke 100, az Increment értéke 2. Körülbelül mennyi ideig tart, amíg a csík 0-ról 100-ra tölt?
Jóslat ellenőrzése
A 100-at 2-es lépésekkel 50 lépésben éri el, és mindegyik lépés 100 ezredmásodperc, vagyis 0,1 másodperc: ez összesen kb. 5 másodperc. Az ablak közben mozgatható és reagál.
Mit tegyél ide: két kép a futó programról: félig kitöltött csík a „Folyamatban: 48%” felirattal, majd a teljes csík a „Kész!” felirattal.Javasolt fájlnév: winforms-1-8-folyamatban.webp · Alt szöveg: A folyamatjelző félig, majd teljesen kitöltve
Leállítás, újraindítás és a gomb letiltása
Ha a folyamat közben a felhasználó újra rákattint az Indítás gombra, az újraindítja a csíkot. Ez zavaró lehet. Két megoldás van: egy Leállítás gomb, és az Indítás letiltása futás közben:
private void btnInditas_Click(object sender, EventArgs e)
{
pgbFolyamat.Value = 0;
lblAllapot.Text = "Folyamatban...";
btnInditas.Enabled = false;
tmrFolyamat.Start();
}
private void btnLeallitas_Click(object sender, EventArgs e)
{
tmrFolyamat.Stop();
lblAllapot.Text = "Megállítva.";
btnInditas.Enabled = true;
}
private void tmrFolyamat_Tick(object sender, EventArgs e)
{
if (pgbFolyamat.Value < pgbFolyamat.Maximum)
{
pgbFolyamat.Increment(2);
lblAllapot.Text = $"Folyamatban: {pgbFolyamat.Value}%";
}
else
{
tmrFolyamat.Stop();
lblAllapot.Text = "Kész!";
btnInditas.Enabled = true;
}
}Leállítás felirattal, amely megállítja a Timert. A csík ott marad, ahol tartott.Szünet és folytatás
Ha a Leállítás gomb csak a Stop() metódust hívja, a Start() hívás ott folytatja, ahol a csík tartott (ha nem nullázod az értéket). Így egy Szünet és egy Folytatás gombot is könnyű készíteni.
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. Befagy az ablak
A gombnyomás után az ablak nem reagál, a csík csak a végén telik meg. A Ok és javítás
for ciklus és a Thread.Sleep az ablakot kezelő szálat várakoztatja. Ismétlődő felületfrissítéshez Timert használj, amely minden Tick-nél csak egy kis lépést tesz.
2. Nem indul el a csík
A gombra kattintasz, de a csík nem mozdul. Lehetséges okok: nem hívtad meg a Ok és javítás
tmrFolyamat.Start() metódust, vagy a Tick eseménykezelő nincs összekötve a Timerrel (kézzel gépelted be). Kattints duplán a Timerre a komponenssávban, hogy létrejöjjön az összekötött eseménykezelő.
3. Túl nagy érték
pgbFolyamat.Value = pgbFolyamat.Value + 5;
// az érték 98-ról 103-ra lépne, a Maximum 100Hiba és javítás
Futás közbeni hiba (ArgumentOutOfRangeException): a Value nem lehet nagyobb a Maximum értéknél. Használd az Increment() metódust, amely a maximumnál megáll: pgbFolyamat.Increment(5);
4. Soha nem áll le
A csík megtelik, de az időzítő tovább fut, és a felirat sem változik „Kész!” szövegre. Nincs meghívva a Ok és javítás
tmrFolyamat.Stop() a Tick eseménykezelő else ágában, vagy az if feltétel rossz (például <= helyett < kell). A leállítás nélkül az időzítő akkor is dolgozik, amikor már nincs mit csinálni.
5. Ismétlődő kattintás
A folyamat közben többször rákattintasz az Indítás gombra, és a csík újra és újra elölről indul. Az Indítás gomb futás közben is használható. Tiltsd le a Ok és javítás
btnInditas.Enabled = false; sorral, és kapcsold vissza, amikor a folyamat véget ér vagy leáll.
Mini projekt: letöltés-szimulátor
Készíts egy „letöltés” programot:
- ProgressBar (0–100), Indítás és Leállítás gomb, állapotfelirat, és egy Timer (
Interval: 50). - Az Indítás nullázza a csíkot, letiltja magát, és elindítja a Timert.
- A Tick 2-vel növeli a csíkot, és kiírja a százalékot.
- A csík megtelte után a program kiírja: A letöltés kész!, leállítja a Timert, és visszakapcsolja az Indítás gombot.
- A Leállítás gomb megállítja a folyamatot, és kiírja: Megszakítva.
A teljes kód a 4. szakaszban látható. Próbáld ki mozgatás közben is az ablakot: a csík fut, az ablak pedig nem fagy be. Mit tegyél ide: a kész letöltés-szimulátor futás közben: félig kitöltött csík, „Folyamatban: 62%” felirat, szürke (letiltott) Indítás gomb és aktív Leállítás gomb.Javasolt fájlnév: winforms-1-8-letoltes.webp · Alt szöveg: A letöltés-szimulátor futás közben, letiltott Indítás gombbal
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. Gyorsabb folyamat
Növeld a csíkot Tick-enként 5-tel. Mi történik a vége felé? Az Egy lehetséges megoldás
pgbFolyamat.Increment(5);Increment a maximumnál megáll, ezért nem kell külön védened a túllépést. A pgbFolyamat.Value = pgbFolyamat.Value + 5 hibát okozna.
2. Százalék a címkén
Minden Tick után írd ki a százalékot, például 37%.Egy lehetséges megoldás
lblAllapot.Text = pgbFolyamat.Value + "%";
3. Szünet és folytatás
Készíts egy Szünet és egy Folytatás gombot, amelyek megállítják, illetve ott folytatják a folyamatot, ahol tartott. A Folytatás nem nullázza a csíkot, ezért ott folytatódik, ahol megállt.Egy lehetséges megoldás
private void btnSzunet_Click(object sender, EventArgs e)
{
tmrFolyamat.Stop();
lblAllapot.Text = "Szünet";
}
private void btnFolytatas_Click(object sender, EventArgs e)
{
tmrFolyamat.Start();
}
Gyakorlótár: 6 további feladat
Három szinten gyakorolhatsz: alap, közepes és kihívás. A Timer vezérlőt a Toolbox Components csoportjából húzd az ablakra (a komponenssávba kerül). Csak a próbálkozás után nyisd le a megoldást.
Alap szint
1. Másodpercszámláló
Egy A számláló értékét a metódusok között is meg kell őrizni, ezért mezőben (a metóduson kívül) tároljuk. A tmrFolyamat Timer (Interval: 1000) másodpercenként növeljen egy számlálót, amelyet a lblSzam felirat mutat. A btnStart gomb indítsa a Timert.Megoldás megnyitása
Tick esemény az Interval eltelte után fut le.private int szamlalo = 0;
private void btnStart_Click(object sender, EventArgs e)
{
tmrFolyamat.Start();
}
private void tmrFolyamat_Tick(object sender, EventArgs e)
{
szamlalo = szamlalo + 1;
lblSzam.Text = szamlalo.ToString();
}
2. Léptetés gombbal
A A btnLeptet gomb minden megnyomásra 10-zel növelje a pgbFolyamat ProgressBar értékét. A maximumnál (100) ne lépje túl.Megoldás megnyitása
Value nem lehet nagyobb a Maximum-nál, különben futás közben hibát kapsz. Ezért a növelés előtt ellenőrizzük a határt.private void btnLeptet_Click(object sender, EventArgs e)
{
if (pgbFolyamat.Value + 10 <= pgbFolyamat.Maximum)
{
pgbFolyamat.Value = pgbFolyamat.Value + 10;
}
else
{
pgbFolyamat.Value = pgbFolyamat.Maximum;
}
}
Közepes szint
3. Töltősáv indítással és leállítással
A Start gomb indítsa el a Timert ( A Interval: 200), amely 5-tel növeli a pgbFolyamat értékét. A 100 elérésekor álljon le, és jelenjen meg: Kész! A Stop gomb szakítsa meg.Megoldás megnyitása
Stop() megállítja a Timert. Az ellenőrzést a Tick eseményben végezzük, mert ott derül ki, hogy elértük-e a végét.private void btnStart_Click(object sender, EventArgs e)
{
pgbFolyamat.Value = 0;
lblAllapot.Text = "Folyamatban...";
tmrFolyamat.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
tmrFolyamat.Stop();
lblAllapot.Text = "Megszakítva.";
}
private void tmrFolyamat_Tick(object sender, EventArgs e)
{
if (pgbFolyamat.Value < pgbFolyamat.Maximum)
{
pgbFolyamat.Value = Math.Min(pgbFolyamat.Value + 5, pgbFolyamat.Maximum);
}
if (pgbFolyamat.Value >= pgbFolyamat.Maximum)
{
tmrFolyamat.Stop();
lblAllapot.Text = "Kész!";
}
}
4. Visszaszámláló
A gomb indítson egy 10 másodperces visszaszámlálást ( A hátralévő időt mezőben tároljuk. Indításkor 10-re állítjuk, a Interval: 1000). A lblSzam mutassa a hátralévő másodperceket, és a 0 elérésekor jelenjen meg: Lejárt az idő!Megoldás megnyitása
Tick minden másodpercben csökkenti. Nullánál megállítjuk a Timert, és csak ekkor jelzünk.private int hatralevo = 10;
private void btnIndit_Click(object sender, EventArgs e)
{
hatralevo = 10;
lblSzam.Text = hatralevo.ToString();
tmrFolyamat.Start();
}
private void tmrFolyamat_Tick(object sender, EventArgs e)
{
hatralevo = hatralevo - 1;
lblSzam.Text = hatralevo.ToString();
if (hatralevo == 0)
{
tmrFolyamat.Stop();
MessageBox.Show("Lejárt az idő!");
}
}
Kihívás szint
5. Tizedmásodperces stopper
Készíts stoppert Start, Stop és Nulláz gombbal. A Timer A tizedmásodperceket számoljuk (egész számként). A másodperc az osztás hányadosa (Interval értéke 100, az idő formátuma 12.3 mp.Megoldás megnyitása
/ 10), a tizedes a maradék (% 10).private int tizedek = 0;
private void Kijelzes()
{
lblIdo.Text = $"{tizedek / 10}.{tizedek % 10} mp";
}
private void tmrFolyamat_Tick(object sender, EventArgs e)
{
tizedek = tizedek + 1;
Kijelzes();
}
private void btnStart_Click(object sender, EventArgs e)
{
tmrFolyamat.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{
tmrFolyamat.Stop();
}
private void btnNullaz_Click(object sender, EventArgs e)
{
tmrFolyamat.Stop();
tizedek = 0;
Kijelzes();
}
6. Mozgó felirat
Egy A lblMozgo felirat vándoroljon balról jobbra (Interval: 30). Ha elhagyta az ablak jobb szélét, jelenjen meg újra a bal oldalon.Megoldás megnyitása
Left a vezérlő bal szélének helye az ablakban. A ClientSize.Width az ablak használható szélessége. Ha a felirat bal széle túlment, a negatív szélességre állítjuk, így balról csúszik vissza.private void tmrMozgas_Tick(object sender, EventArgs e)
{
lblMozgo.Left = lblMozgo.Left + 4;
if (lblMozgo.Left > this.ClientSize.Width)
{
lblMozgo.Left = -lblMozgo.Width;
}
}
Gyors önellenőrzés
Válaszolj fejben, és csak utána nyisd le a választ. Minden időköz letelte után lefut, így ismétlődően végrehajthatunk egy kis lépést. A csík legnagyobb értékét. Ha a Value eléri, a csík teljesen kitöltött. Az ablakot kezelő szálat várakoztatja, ezért az ablak nem tud frissülni, és lefagyottnak tűnik. Az első hibát okoz, ha az érték túllépi a maximumot. Az Hogy a felhasználó ne indíthassa újra véletlenül a folyamatot, miközben még fut. A Tick események közötti időt adja meg milliszekundumban.1. Mit csinál a Timer Tick eseménye?
2. Mit jelent a ProgressBar Maximum tulajdonsága?
3. Miért veszélyes a Thread.Sleep az eseménykezelőben?
4. Mi a különbség a Value = Value + 5 és az Increment(5) között?
Increment a maximumnál megáll.5. Miért tiltjuk le az Indítás gombot futás közben?
6. Mire való az Interval?
Mit tudsz már?
- Használod a ProgressBar tulajdonságait.
- Érted, miért fagy be az ablak ciklusnál és Thread.Sleep esetén.
- Használod a Timer Interval, Start, Stop és Tick elemeit.
- Biztonságosan növeled a csíkot az Increment segítségével.
- Letiltod és visszakapcsolod a gombot a folyamat idejére.
Következő lecke: TextBox, MaskedTextBox és NumericUpDown
A folyamatjelzést már ismered. A következő leckében a beviteli vezérlőket nézzük meg részletesebben.




