Grafikus C# – Windows Forms tanfolyam › 6.1 lecke

Windows Forms · 6.1 lecke · az ablak tulajdonságai

Form mérete, helye, címe és ikonja

Az ablak a program arca: a felhasználó először a méretét, a címét és az ikonját látja. Ebben a leckében a Form legfontosabb megjelenési tulajdonságait ismered meg: hogyan állítsd be a méretet (Size és ClientSize), a helyet (Location, StartPosition), a keretet és a gombokat (FormBorderStyle, ControlBox), a címet (Text) és az ikont (Icon). Megtanulod azt is, hogyan igazítsd az ablakot a képernyőhöz, és hogyan kerüld el a leggyakoribb csapdákat.

  • kb. 100 perc
  • kezdő–közepes szint
  • az ablak megjelenése
Diák C# programozást gyakorol egy tanulósarokban

A lecke végére

  • megkülönbözteted a Size és a ClientSize tulajdonságot, és tudod, melyiket mikor használd;
  • beállítod az ablak helyét (StartPosition, Location), és igazítod a képernyő munkaterületéhez;
  • korlátozod az ablak méretét (MinimumSize, MaximumSize), és kiválasztod a megfelelő keretstílust (FormBorderStyle);
  • kezeled a címsor gombjait és megjelenését (ControlBox, MinimizeBox, MaximizeBox, ShowInTaskbar);
  • dinamikus címet írsz, és ikont állítasz be (a tervezőben és kódból);
  • kezeled az ablak állapotát (WindowState) és a méretváltozás eseményeit (Resize, ResizeEnd).

Mielőtt elkezded

Ehhez a leckéhez az 0.5–0.7 lecke (Form1.Designer.cs, Properties ablak), az 5.5 lecke (változás-események, jelző) és az 5.6 lecke (RestoreBounds, Screen) szükséges. Új Windows Forms projektben próbáld ki a példákat.

5.9 Záró mini projekt →

Az ablak mérete: Size és ClientSize

Az ablaknak két mérete van, és a kettő nem azonos:

Size

Az ablak teljes mérete: a keret, a címsor és a tartalom együtt. Ez az, amit a képernyőn látsz, és a tervezőben az ablak szélein húzással állítasz.

ClientSize

Az ablak belső, használható területe (kliensterület): a vezérlők ide kerülnek. A keret és a címsor nélküli méret, ezért mindig kisebb a Size értéknél.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
lblInfo.Text = $"Size: {this.Size.Width} x {this.Size.Height}\u000a"
+ $"ClientSize: {this.ClientSize.Width} x {this.ClientSize.Height}";
}

// méret beállítása:
this.Size = new Size(800, 600); // a teljes ablak 800 x 600
this.ClientSize = new Size(800, 600); // a használható terület 800 x 600 (az ablak nagyobb)
this.Width = 800; // csak a szélesség
this.Height = 600; // csak a magasság
Mikor Size, mikor ClientSize?Ha az ablakot úgy akarod méretezni, hogy a tartalom pontosan elférjen (például 400 × 400-as játéktér), a ClientSize a jó. Ha a képernyőhöz viszonyítva igazítasz (a jobb alsó sarokba), a Size kell.
A keret vastagságaA különbség a Windows beállításától és a keretstílustól függ (a címsor kb. 30–40 képpont, a keret néhány). Ezért ne számolj fix különbséggel: használd a ClientSize értéket.
Size típusaA Size egy struct (Width, Height). Nem módosítható közvetlenül (this.Size.Width = 5 hiba), mindig új értéket kell adni, vagy a this.Width tulajdonságot használni.
MinimumSize és MaximumSizeMegadják, mekkorára méretezheti a felhasználó az ablakot. A (0, 0) érték azt jelenti: nincs korlát.

Jósolj!

A Form1_Load eseményben ezt írod: this.ClientSize = new Size(400, 300);. Mekkora lesz a this.Size: nagyobb, kisebb vagy ugyanennyi, mint a 400 × 300?

Jóslat ellenőrzése

Nagyobb: a Size tartalmazza a keretet és a címsort is, így a szélessége kb. 16, a magassága kb. 39 képponttal nagyobb (például 416 × 339), ha az ablak normál, méretezhető kerettel rendelkezik. A pontos érték a Windows verziójától és a megjelenítési beállításoktól függ. A tanulság: a tartalom méretéhez a ClientSize a megbízható.

Az ablak helye: StartPosition és Location

Az ablak induló helyét a StartPosition tulajdonság határozza meg:

ÉrtékJelentése
WindowsDefaultLocationAz alapértelmezés: a Windows választ helyet (általában kissé eltolva az előző ablakhoz képest).
CenterScreenA képernyő közepén.
CenterParentA gazdaablak (owner) közepén. Párbeszédablakoknál ez a jó (7. fejezet).
ManualPontosan a Location tulajdonság szerint.
WindowsDefaultBoundsA Windows választ helyet és méretet is.
Form1.csC#
// 1. a képernyő közepére
this.StartPosition = FormStartPosition.CenterScreen;

// 2. pontos helyre: Manual kell, különben a Location nem számít
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(100, 50);

// 3. futás közben középre (a megjelenés után is működik)
this.CenterToScreen();

// 4. a jobb alsó sarokba, a tálca felett (értesítő ablak)
Rectangle munka = Screen.PrimaryScreen.WorkingArea;
this.Location = new Point(munka.Right - this.Width - 10, munka.Bottom - this.Height - 10);
Screen.WorkingAreaA képernyő munkaterülete: a tálca nélküli rész. A Screen.PrimaryScreen.Bounds a teljes képernyő. Az ablak igazításánál a munkaterület a jó, így nem lóg a tálca alá.
CenterToScreen()Futás közben is középre teszi az ablakot (a StartPosition csak indulásnál számít).
Több monitorA Screen.AllScreens a monitorok tömbje, a Screen.FromControl(this) azt a monitort adja, amelyen az ablak van (5.6 lecke).
LocationAz ablak bal felső sarka a képernyőn (a képernyő bal felső sarka a (0, 0) pont). Az ablak elhelyezése tehát képernyő-koordinátákban történik.

Jósolj!

A tervezőben a Location értéke (300, 200), de a StartPosition az alapértelmezett WindowsDefaultLocation. Hol jelenik meg az ablak?

Jóslat ellenőrzése

A Windows által választott helyen, nem a (300, 200) pontnál: a Location csak StartPosition = Manual esetén érvényesül. Ez az egyik leggyakoribb „miért nem működik a Location?” hiba.

Keretstílus és a címsor gombjai

Az ablak megjelenését és méretezhetőségét a FormBorderStyle tulajdonság szabályozza:

ÉrtékMegjelenésMikor használd?
SizableAlapértelmezett: méretezhető keret.Főablak, szerkesztő.
FixedSingleVékony, nem méretezhető keret.Fix elrendezésű program (játék, kalkulátor).
Fixed3DDomború, nem méretezhető keret.Régi stílusú ablak.
FixedDialogPárbeszédablak: nem méretezhető, nincs ikon a címsorban.Beállítás, About, adatbekérő ablak.
FixedToolWindow, SizableToolWindowVékony címsor, nincs tálcagomb (a ShowInTaskbar hamis).Eszköztár, kis segédablak.
NoneNincs keret és címsor.Indítóképernyő (splash), saját kinézetű ablak (de nincs mozgatás és bezárás magától).

A címsor gombjait ezek a tulajdonságok adják: a ControlBox (a címsor gombjai és a rendszermenü), a MinimizeBox, a MaximizeBox, valamint a ShowIcon (az ikon a címsorban) és a ShowInTaskbar (a tálcán):

Form1.cs (Load)C#
// egy rögzített méretű játékablak
this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.MaximizeBox = false; // nem nagyítható
this.StartPosition = FormStartPosition.CenterScreen;

// egy segédablak: nincs tálcagomb, mindig felül
segedAblak.ShowInTaskbar = false;
segedAblak.TopMost = true;

// méretkorlát egy szerkesztő ablaknál
this.MinimumSize = new Size(640, 420); // ennél kisebbre nem húzható
FixedSingle + MaximizeBox = falseA FixedSingle ablak kézzel nem méretezhető, de ha a MaximizeBox igaz marad, a nagyítógomb megmarad, és az ablak teljes képernyősre nőhet úgy, hogy a vezérlői nem követik. Ezért fix elrendezésű ablaknál kapcsold ki a nagyítógombot.
ControlBox = falseEltűnik a címsor minden gombja (bezárás is). Ilyenkor az ablakot a programnak kell bezárnia (egy gombbal), különben a felhasználó nem tud kilépni.
ShowInTaskbarHamis értéknél az ablak nem kap tálcagombot. Segédablakoknál jó, a főablaknál ne használd (a felhasználó nem találná meg).
TopMostAz ablak minden más ablak fölött marad. Óvatosan használd: zavaró lehet.

Tilos zsákutcát építeni

Ha egy ablaknak ControlBox = false és nincs benne bezáró gomb, vagy a FormBorderStyle = None ablakot nem lehet mozgatni és bezárni, a felhasználó csak a Feladatkezelőből tud kilépni. Mielőtt eltávolítod a beépített gombokat, gondoskodj a helyettesítésükről (kilépés gomb, Esc billentyű, mozgatás a 5.3 lecke Gyakorlótárának mintájára).

Jósolj!

A FormBorderStyle értéke FixedDialog, a MinimizeBox és MaximizeBox is igaz. Mit látsz a címsorban?

Jóslat ellenőrzése

A FixedDialog stílusnál az ablaknak általában nincs kicsinyítő és nagyító gombja, csak a bezáró gomb, akkor is, ha a MinimizeBox és a MaximizeBox igaz. A párbeszédablak természete ez. Ha mégis szükséged van a kicsinyítésre, a Sizable vagy a FixedSingle stílust használd. (A viselkedést a saját Windowsodon érdemes kipróbálni, a megjelenés a verziótól függhet.)

Az ablak címe és ikonja

Cím: a Text tulajdonság

A címsor szövegét a Text tulajdonság adja. Nemcsak fix szöveg lehet: a program állapotát is mutathatja (nyitott fájl, módosítás jelzés):

Form1.csC#
private string fajlNev = "";
private bool modosult = false;

private void CimFrissit()
{
string nev = fajlNev.Length == 0 ? "Névtelen" : System.IO.Path.GetFileName(fajlNev);
string csillag = modosult ? "*" : "";

this.Text = nev + csillag + " – MiniWord"; // például: jegyzet.rtf* – MiniWord
}

A cím szokás szerint a dokumentum neve – a program neve alakú, a módosítást csillag (*) jelzi (2.14 lecke). A cím a tálcán és az Alt+Tab váltóban is látszik, ezért legyen rövid és beszédes.

Ikon: az Icon tulajdonság

Az ablak ikonja a címsor bal oldalán és a tálcán jelenik meg. Beállítása:

  1. Készíts vagy tölts le egy .ico fájlt (az ikon több méretet is tartalmazhat: 16, 32, 48, 256 képpont). PNG képből online átalakítók vagy képszerkesztők készítenek ikont.
  2. A tervezőben jelöld ki az ablakot, a Properties ablakban az Icon sorban kattints a … gombra, és válaszd ki az .ico fájlt. A Visual Studio az ikont a Form1.resx fájlba ágyazza (a futtatható fájlba kerül, külön fájl nem kell).
  3. A program EXE fájljának ikonját (a Fájlkezelőben látszik) a projekt tulajdonságainál állítod: Project → Properties → Application → Icon. Ez más beállítás, mint az ablak ikonja!
Form1.csC#
// ikon betöltése fájlból (a fájlnak a program mellett kell lennie)
this.Icon = new Icon("ikon.ico");

// ikon az erőforrások közül (Properties → Resources → hozzáadott .ico): nem kell külön fájl
this.Icon = Properties.Resources.ikonPiros;

// az ikon elrejtése a címsorban
this.ShowIcon = false;
Fájlból vagy erőforrásból?A new Icon("ikon.ico") a futtatás helyétől függő útvonalat használ: másik gépen vagy mappából indítva hibát (FileNotFoundException) adhat. Az erőforrásba ágyazott ikon a programmal együtt utazik, ezért megbízhatóbb.
ShowIcon = falseAz ablak címsorából eltűnik az ikon (a tálcán továbbra is látszik). A FormBorderStyle = FixedDialog ablakoknál amúgy sincs ikon a címsorban.
Ikon váltásFutás közben az Icon tulajdonság átállítható (például értesítésnél piros ikonra): a tálca is frissül.
Fejlécen kívüli ikonA NotifyIcon komponens az értesítési területre (a tálca órájának környékére) tesz ikont, ez külön eszköz.

Jósolj!

Beállítod az ablak Icon tulajdonságát egy saját ikonra, de a projekt Application → Icon beállítását nem változtatod. Milyen ikont látsz (a) a címsorban, (b) a tálcán, (c) a Fájlkezelőben az EXE fájlnál?

Jóslat ellenőrzése

(a) és (b): a saját ikonod (az ablak ikonja). (c): az alapértelmezett ikon (a projekt alkalmazásikonja), mert az EXE fájl ikonját a projekt tulajdonságai adják, nem az ablaké. Egy kész programnál mindkettőt érdemes ugyanarra az ikonra állítani.

Ablakállapot és a méretváltozás eseményei

A WindowState tulajdonság az ablak állapotát adja: Normal, Minimized (kicsinyített) vagy Maximized (maximalizált). Kódból is beállítható (this.WindowState = FormWindowState.Maximized;), és az ablak méretváltozásakor több esemény fut:

EseményMikor fut?Használat
ResizeAz ablak mérete változik (húzás közben folyamatosan, sokszor).Gyors, egyszerű frissítés (felirat).
SizeChangedA Size megváltozott (a Resize párja).Hasonló a Resize-hoz.
ResizeEndA felhasználó befejezte a méretezést (elengedte az egeret).Lassú műveletek (újrarajzolás, számolás), mert csak egyszer fut.
LocationChanged, MoveAz ablak helye változott.Hely követése, képernyőn belül tartás.
Resize a WindowState váltáskorKicsinyítésnél és maximalizálásnál is lefut.Az állapot ellenőrzése a WindowState alapján.
Form1.csC#
private void Form1_Resize(object sender, EventArgs e)
{
// gyors: csak egy felirat (húzás közben sokszor fut)
lblMeret.Text = $"{this.ClientSize.Width} x {this.ClientSize.Height}";

if (this.WindowState == FormWindowState.Minimized)
{
return; // kicsinyített ablaknál ne számoljunk (a mérete 0 is lehet)
}
}

private void Form1_ResizeEnd(object sender, EventArgs e)
{
// lassú munka itt: egyszer fut a méretezés végén
NagyRajzFrissit();
}

private void btnTeljes_Click(object sender, EventArgs e)
{
this.WindowState = this.WindowState == FormWindowState.Maximized
? FormWindowState.Normal
: FormWindowState.Maximized;
}
Resize és terhelésA Resize húzás közben másodpercenként sokszor fut. Ha a kezelő lassú (nagy kép újraszámolása, adatbázis-lekérdezés), az ablak akadozik. Lassú munkára a ResizeEnd való (de a maximalizálásnál a ResizeEnd nem fut, ott a Resize vagy a SizeChanged kell).
Kicsinyített ablakA kicsinyített ablak mérete és a vezérlők mérete nulla vagy érvénytelen is lehet. A Resize eseményben ellenőrizd a WindowState értékét, mielőtt osztasz a mérettel.
RestoreBoundsMaximalizált vagy kicsinyített ablaknál a visszaállítás utáni méretet és helyet adja (5.6 lecke).
OpacityAz ablak átlátszósága (0,0 teljesen átlátszó, 1,0 teljesen látszik). Ne állítsd túl alacsonyra, mert az ablak használhatatlan lesz.

Jósolj!

A Resize eseményben egy másodpercig tartó számítás van. Mit tapasztalsz az ablak szélének húzásakor? Mit változtat, ha a számítást a ResizeEnd eseménybe teszed?

Jóslat ellenőrzése

A Resize esetén az ablak rendkívül lassan reagál (minden egérmozdulatnál egy másodperc), gyakorlatilag használhatatlan. A ResizeEnd esetén a húzás gördülékeny, és a számítás egyszer, a méretezés végén fut. (A kísérlethez használj System.Threading.Thread.Sleep(1000); sort számítás helyett.)

📷 Képernyőkép helye

Mit tegyél ide: egy ablak a címsorral, ikonnal, és mellette a Size és ClientSize értékeit mutató felirat, hogy a különbség látszódjon.Javasolt fájlnév: winforms-6-1-size.webp · Alt szöveg: Az ablak Size és ClientSize értéke

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 Location nem számít

this.Location = new Point(100, 100);   // az ablak mégsem ott jelenik meg
Hiba és javítás

A StartPosition nem Manual. Állítsd FormStartPosition.Manual értékre (a tervezőben vagy a konstruktorban, az InitializeComponent után), így a Location érvényesül.

2. A tartalom kilóg vagy levágódik

A vezérlőket 400 × 300-as területre terveztem, és az ablakot Size = (400, 300) értékre állítottam, de a vezérlők alja levágódik.

Ok és javítás

A Size a keretet és a címsort is tartalmazza, ezért a használható terület kisebb. A tartalom méretéhez a ClientSize tulajdonságot állítsd.

3. Nem lehet kilépni a programból

this.ControlBox = false;
// de nincs kilépés gomb, és Esc kezelés sincs
Hiba és javítás

A ControlBox = false eltünteti a bezáró gombot is. Tegyél az ablakra egy Kilépés gombot (this.Close();), vagy kezeld az Esc billentyűt (5.4 lecke, 6.7 lecke CancelButton).

4. A tálcán más ikon látszik, mint amit beállítottam

Az ablak ikonja jó, de az EXE fájl a Fájlkezelőben az alapértelmezett ikonnal jelenik meg.

Ok és javítás

Az ablak ikonja (Form.Icon) és az EXE ikonja (Project → Properties → Application → Icon) két külön beállítás. Állítsd be mindkettőt ugyanarra az .ico fájlra.

5. FileNotFoundException az ikonnál

this.Icon = new Icon("C:\\Munka\\ikon.ico");   // másik gépen nincs ilyen mappa
Hiba és javítás

A fix útvonal csak a fejlesztő gépén létezik. Ágyazd az ikont az erőforrások közé (Properties → Resources, vagy a tervezőben az Icon tulajdonságon át), így a programmal együtt utazik.

6. Az ablak akadozik átméretezéskor

A méret húzása közben az ablak sokat késik.

Ok és javítás

A Resize kezelő lassú munkát végez (újrarajzolás, adatbetöltés), és húzás közben sokszor fut. Vidd a lassú munkát a ResizeEnd eseménybe, a Resize csak gyors frissítést (felirat) tartalmazzon.

Mini projekt: ablakbeállító

Készíts egy ablakot, amely önmagának a tulajdonságait állítja élőben, és mutatja az aktuális értékeket:

  1. Az ablakon: lblInfo (az aktuális Size, ClientSize, Location, WindowState, többsoros), cmbKeret (FormBorderStyle értékei), cmbPozicio (Középre, Bal felső sarok, Jobb alsó sarok) és egy btnPozicio, nudSzel és nudMag (ClientSize), btnMeret, trbAtlatszo (30–100, az Opacity), chkTopMost, chkTaskbar, chkMinimize, chkMaximize, txtCim (az ablak címe).
  2. A cmbKeret a FormBorderStyle felsorolás értékeit tartalmazza (Enum.GetNames), és a kiválasztás azonnal átállítja az ablak keretét.
  3. A txtCim szövege (TextChanged) azonnal az ablak címe lesz; üres cím esetén Névtelen.
  4. A btnMeret a nudSzel és nudMag értékére állítja a ClientSize tulajdonságot; a MinimumSize 420 × 480, így az ablak nem lehet kisebb.
  5. A btnPozicio a választott helyre teszi az ablakot (a munkaterülethez igazítva, Screen.WorkingArea).
  6. A jelölőnégyzetek a TopMost, ShowInTaskbar, MinimizeBox, MaximizeBox tulajdonságokat kapcsolják; a trbAtlatszo az Opacity értékét.
  7. A lblInfo a Resize és LocationChanged eseményre frissül, a nudSzel és nudMag is követi az ablak méretét (jelzővel, hogy ne legyen hurok).
  8. Teszteld: minden keretstílus, méret beállítása és húzása, a három pozíció, az átlátszóság, a jelölőnégyzetek, a kicsinyítés és maximalizálás.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A FormBorderStyle felsorolás nevei szövegként kerülnek a listába, és a választásnál a Enum.Parse alakítja vissza a nevet értékké. A frissites jelző akadályozza meg, hogy a méret frissítése a számmezők eseményeit is lefuttassa (5.5 lecke).

Form1.csC#
private bool frissites = false;

private void Form1_Load(object sender, EventArgs e)
{
this.MinimumSize = new Size(420, 480);

cmbKeret.Items.AddRange(Enum.GetNames(typeof(FormBorderStyle)));
cmbKeret.SelectedItem = this.FormBorderStyle.ToString();

cmbPozicio.Items.AddRange(new object[] { "Középre", "Bal felső sarok", "Jobb alsó sarok" });
cmbPozicio.SelectedIndex = 0;

trbAtlatszo.Minimum = 30;
trbAtlatszo.Maximum = 100;
trbAtlatszo.Value = 100;

chkTopMost.Checked = this.TopMost;
chkTaskbar.Checked = this.ShowInTaskbar;
chkMinimize.Checked = this.MinimizeBox;
chkMaximize.Checked = this.MaximizeBox;
txtCim.Text = this.Text;

Frissit();
}

private void Frissit()
{
frissites = true;

try
{
lblInfo.Text = $"Size: {this.Width} x {this.Height}\r\n"
+ $"ClientSize: {this.ClientSize.Width} x {this.ClientSize.Height}\r\n"
+ $"Location: {this.Location.X}, {this.Location.Y}\r\n"
+ $"Állapot: {this.WindowState}";

if (this.WindowState == FormWindowState.Normal)
{
nudSzel.Value = Math.Max(nudSzel.Minimum, Math.Min(nudSzel.Maximum, this.ClientSize.Width));
nudMag.Value = Math.Max(nudMag.Minimum, Math.Min(nudMag.Maximum, this.ClientSize.Height));
}
}
finally
{
frissites = false;
}
}

private void Form1_Resize(object sender, EventArgs e)
{
Frissit();
}

private void Form1_LocationChanged(object sender, EventArgs e)
{
Frissit();
}

private void cmbKeret_SelectionChangeCommitted(object sender, EventArgs e)
{
this.FormBorderStyle = (FormBorderStyle)Enum.Parse(typeof(FormBorderStyle), cmbKeret.SelectedItem.ToString());
}

private void txtCim_TextChanged(object sender, EventArgs e)
{
this.Text = txtCim.Text.Trim().Length == 0 ? "Névtelen" : txtCim.Text;
}

private void btnMeret_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Normal;
this.ClientSize = new Size((int)nudSzel.Value, (int)nudMag.Value);
}

private void btnPozicio_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Normal;
Rectangle munka = Screen.FromControl(this).WorkingArea;

if (cmbPozicio.SelectedIndex == 0)
{
this.CenterToScreen();
}
else if (cmbPozicio.SelectedIndex == 1)
{
this.Location = new Point(munka.Left, munka.Top);
}
else
{
this.Location = new Point(munka.Right - this.Width, munka.Bottom - this.Height);
}
}

private void trbAtlatszo_Scroll(object sender, EventArgs e)
{
this.Opacity = trbAtlatszo.Value / 100.0;
}

private void chkTopMost_CheckedChanged(object sender, EventArgs e)
{
this.TopMost = chkTopMost.Checked;
}

private void chkTaskbar_CheckedChanged(object sender, EventArgs e)
{
this.ShowInTaskbar = chkTaskbar.Checked;
}

private void chkMinimize_CheckedChanged(object sender, EventArgs e)
{
this.MinimizeBox = chkMinimize.Checked;
}

private void chkMaximize_CheckedChanged(object sender, EventArgs e)
{
this.MaximizeBox = chkMaximize.Checked;
}
📷 Képernyőkép helye

Mit tegyél ide: a kész ablakbeállító program: a beállítóvezérlők és a feliratban a Size, ClientSize, Location és állapot értékei.Javasolt fájlnév: winforms-6-1-kesz.webp · Alt szöveg: Az ablakbeállító program

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. Értesítő ablak a jobb alsó sarokban

Készíts egy kis ErtesitoAblak űrlapot, amely a képernyő munkaterületének jobb alsó sarkában jelenik meg (a tálca felett), nem kap tálcagombot, mindig felül van, és három másodperc múlva magától bezárul. Mit állítasz a tervezőben, és mit a kódban?

Egy lehetséges megoldás

A tervezőben: FormBorderStyle: FixedToolWindow (vagy None), ShowInTaskbar: False, TopMost: True, StartPosition: Manual, egy Timer (3000 ezredmásodperc, Enabled: True). A hely a Load eseményben számítható, mert csak ekkor ismert a végleges méret.

ErtesitoAblak.csC#
private void ErtesitoAblak_Load(object sender, EventArgs e)
{
Rectangle munka = Screen.PrimaryScreen.WorkingArea;
this.Location = new Point(munka.Right - this.Width - 10, munka.Bottom - this.Height - 10);
}

private void tmrBezar_Tick(object sender, EventArgs e)
{
tmrBezar.Stop();
this.Close();
}

2. Méret a címsorban

Az ablak címe mutassa az aktuális kliensterület méretét (Szerkesztő – 640 × 420). Melyik eseményt használod, hogy az átméretezés közben is frissüljön, és ne akadozzon?

Egy lehetséges megoldás

A Resize eseményt: a felirat frissítése gyors, ezért húzás közben is jó. Kicsinyített ablaknál a méret nulla, ezért kihagyjuk a frissítést.

Form1.csC#
private void Form1_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
return;
}

this.Text = $"Szerkesztő – {this.ClientSize.Width} × {this.ClientSize.Height}";
}

3. Ikon váltás állapotjelzésre

Az ablak ikonja zöld, ha minden rendben, piros, ha hiba van (egy chkHiba jelölőnégyzet szimulálja). Hogyan tárolod a két ikont?

Egy lehetséges megoldás

Add hozzá a két .ico fájlt a projekt Properties → Resources oldalához (Add Resource → Add Existing File), és nevezd el őket (ikonZold, ikonPiros). Az erőforrás a programba épül, ezért másik gépen is működik.

Form1.csC#
private void chkHiba_CheckedChanged(object sender, EventArgs e)
{
this.Icon = chkHiba.Checked ? Properties.Resources.ikonPiros : Properties.Resources.ikonZold;
this.Text = chkHiba.Checked ? "Hiba!" : "Minden rendben";
}

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. Size és ClientSize

Egy mondatban mondd el a különbséget, és hogy melyiket használnád egy 400 × 400 pixeles játéktér méretezésére.

Megoldás megnyitása

A Size az ablak teljes mérete (keret és címsor is), a ClientSize a használható belső terület. A játéktérhez a ClientSize kell (ClientSize = new Size(400, 400)), így a keret vastagságától függetlenül pontosan elfér a tartalom.

2. Középre igazítás

Egy Középre gomb kattintására az ablak a képernyő közepére ugrik. Melyik metódussal, és mi a különbség a StartPosition beállításhoz képest?

Megoldás megnyitása

A CenterToScreen() metódussal. A StartPosition csak az ablak induló helyét határozza meg, a CenterToScreen() bármikor, futás közben is középre teszi.

Form1.csC#
private void btnKozepre_Click(object sender, EventArgs e)
{
this.CenterToScreen();
}

Közepes szint

3. Indítóképernyő

Készíts IndulasiKep nevű keret nélküli ablakot, amely a program indulásakor két másodpercig látszik a képernyő közepén, majd a főablak nyílik meg. Hol indítod?

Megoldás megnyitása

A Program.cs Main metódusában, az Application.Run előtt, ShowDialog() hívással: addig vár, amíg az indítóképernyő be nem záródik (a saját Timer-e zárja be). A kép ablakának FormBorderStyle értéke None, a StartPosition CenterScreen.

Program.cs és IndulasiKep.csC#
// Program.cs
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

using (IndulasiKep kep = new IndulasiKep())
{
kep.ShowDialog();
}

Application.Run(new Form1());
}

// IndulasiKep.cs (tmrKep: Timer, Interval: 2000, Enabled: True)
private void tmrKep_Tick(object sender, EventArgs e)
{
tmrKep.Stop();
this.Close();
}

4. Minimális méret védelme

Egy űrlap vezérlői 600 × 400 képpontnál kisebb ablakban összezsúfolódnak. Hogyan akadályozod meg, hogy a felhasználó ennél kisebbre húzza? Mi a különbség a Size és ClientSize megadása között ebben az esetben?

Megoldás megnyitása

A MinimumSize tulajdonság a teljes ablakméretre (Size) vonatkozik, nem a kliensterületre. Ha a kliensterületre akarsz garantálni egy méretet, számold ki a különbséget: this.MinimumSize = new Size(600 + (this.Width - this.ClientSize.Width), 400 + (this.Height - this.ClientSize.Height)); (a Load eseményben, mert ekkor már ismert a keret vastagsága).

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
int keretSzel = this.Width - this.ClientSize.Width;
int keretMag = this.Height - this.ClientSize.Height;

this.MinimumSize = new Size(600 + keretSzel, 400 + keretMag);
}

Kihívás szint

5. A második monitorra

Egy gomb a második monitor közepére teszi az ablakot, ha van ilyen. Ha nincs, üzenet jelenik meg. Hogyan számolod ki a középpontot?

Megoldás megnyitása

A Screen.AllScreens tömb elemei a monitorok. Az ablakot a munkaterület közepére tesszük: a bal felső sarok a terület közepétől az ablak méretének fele.

Form1.csC#
private void btnMasodik_Click(object sender, EventArgs e)
{
if (Screen.AllScreens.Length < 2)
{
MessageBox.Show("Csak egy monitor csatlakozik.");
return;
}

Screen aktualis = Screen.FromControl(this);
Screen masik = Screen.AllScreens[0] == aktualis ? Screen.AllScreens[1] : Screen.AllScreens[0];

Rectangle terulet = masik.WorkingArea;

this.WindowState = FormWindowState.Normal;
this.StartPosition = FormStartPosition.Manual;
this.Location = new Point(
terulet.Left + (terulet.Width - this.Width) / 2,
terulet.Top + (terulet.Height - this.Height) / 2);
}

6. A képernyőn belül tartás

Az ablak nem húzható a képernyő munkaterületén kívülre: ha a felhasználó mégis oda húzná, az ablak visszaugrik a határra. Melyik eseményt használod, és mire kell vigyázni, hogy ne legyen végtelen ciklus?

Megoldás megnyitása

A LocationChanged eseményt. A Location átállítása maga is kiváltja az eseményt, ezért jelző kell (5.5 lecke), különben a kezelő önmagát hívná. Maximalizált és kicsinyített ablaknál nem szabad beavatkozni.

Form1.csC#
private bool igazitas = false;

private void Form1_LocationChanged(object sender, EventArgs e)
{
if (igazitas || this.WindowState != FormWindowState.Normal)
{
return;
}

Rectangle munka = Screen.FromControl(this).WorkingArea;

int x = Math.Max(munka.Left, Math.Min(this.Left, munka.Right - this.Width));
int y = Math.Max(munka.Top, Math.Min(this.Top, munka.Bottom - this.Height));

if (x != this.Left || y != this.Top)
{
igazitas = true;

try
{
this.Location = new Point(x, y);
}
finally
{
igazitas = false;
}
}
}

Gyors önellenőrzés

Válaszolj fejben, és csak utána nyisd le a választ.

1. Mi a különbség a Size és a ClientSize között?

A Size a teljes ablak (keret, címsor), a ClientSize a belső használható terület.

2. Mikor számít a Location?

Ha a StartPosition értéke Manual. Más értéknél az induló helyet a StartPosition határozza meg.

3. Mi a Screen.WorkingArea?

A képernyő munkaterülete a tálca nélkül. Az ablak igazításánál ezt használd.

4. Mit jelent a FormBorderStyle.FixedDialog?

Nem méretezhető, párbeszédablak-szerű keret, általában csak bezáró gombbal és ikon nélkül.

5. Miért veszélyes a ControlBox = false?

A bezáró gomb is eltűnik. Ha nincs más kilépési lehetőség (gomb, Esc), a felhasználó nem tudja bezárni az ablakot.

6. Mi a különbség az ablak és az EXE ikonja között?

Az ablak ikonját a Form.Icon, az EXE fájl ikonját a projekt Application → Icon beállítása adja. Két külön beállítás.

7. Mikor használd a ResizeEnd eseményt a Resize helyett?

Lassú műveletekhez, mert a Resize húzás közben sokszor, a ResizeEnd csak a méretezés végén egyszer fut. (Maximalizálásnál a ResizeEnd nem fut.)

Mit tudsz már?

  • Megkülönbözteted a Size és a ClientSize tulajdonságot.
  • Beállítod az ablak helyét, méretkorlátját, keretstílusát és a címsor gombjait.
  • Dinamikus címet írsz, és ikont állítasz be (tervezőben, erőforrásból).
  • Kezeled az ablak állapotát és a méretváltozás eseményeit.
  • Felismered a gyakori hibákat (Location/StartPosition, ControlBox zsákutca, ikon útvonala).

Következő lecke: Anchor és Dock beállítások

Az ablak méretét már tudod állítani. A következő leckében a vezérlők követik az ablak méretét: így lesz az űrlap átméretezhető és igényes.