Grafikus C# – Windows Forms tanfolyam › 12.2 lecke

Windows Forms · 12.2 lecke · Designer

partial class és a Designer generált kódja

Amikor a Toolboxból gombot húzol a Formra, a Visual Studio kódot ír helyetted. Ez a kód egy külön fájlban él, a Form1.Designer.cs-ben, és ugyanannak az osztálynak a másik fele, mint a Form1.cs. A két fájlt a partial kulcsszó köti össze. Aki érti, mi van a Designer fájlban, az tudja, mit szabad kézzel módosítani, hogyan javítja ki a „furcsa” hibákat (például egy törölt eseménykezelő miatti fordítási hibát), és hogyan oszt fel egy túl nagy Formot átláthatóbb fájlokra.

  • kb. 90 perc
  • közepes szint
  • partial class
Diáklány C# programot készít laptopon

A lecke végére

  • elmagyarázod, mit jelent a partial class, és hogy a fordító a darabokat egyetlen osztállyá egyesíti;
  • elolvasod és értelmezed a Form1.Designer.cs tartalmát (InitializeComponent, mezők, Dispose);
  • tudod, mit szabad és mit nem szabad szerkeszteni a generált fájlban;
  • javítod az eltört eseménykezelő-bekötések hibáit;
  • egy nagy Formot partial fájlokra osztasz, és tudod, hol a korlátja;
  • a felületet kódból is felépíted, ahogy a Designer teszi.

Mielőtt elkezded

Ehhez a leckéhez a 12.1 lecke (osztály, objektum, konstruktor) és az 5.1 lecke (események) szükséges.

12.1 Osztályok →

A partial class: egy osztály, két fájl

Form1.cs és Form1.Designer.csC#
// Form1.cs: a TE kódod (eseménykezelők, logika)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); // ez a metódus a MÁSIK fájlban van
}

private void btnMent_Click(object sender, EventArgs e) { /* ... */ }
}

// Form1.Designer.cs: a Visual Studio generálja
partial class Form1
{
private System.ComponentModel.IContainer components = null;
private Button btnMent; // a vezérlők mezői

private void InitializeComponent() { /* vezérlők létrehozása és beállítása */ }
}
Egy osztályA partial jelzi: az osztály több fájlban van leírva. A fordító a darabokat egyetlen Form1 osztállyá egyesíti: a két fájl tagjai látják egymást (a btnMent mezőt a te kódod is eléri).
Miért van rá szükség?Hogy a generált kód ne keveredjen a tiéddel: a Designer a saját fájlját újraírhatja, a tiédet nem bántja.
FeltételMinden darabnál ott a partial, azonos névtérben és névvel. A hozzáférési szint (public) elég az egyik darabnál. Az ősosztályt (: Form) elég egyszer megadni.
Nem csak FormhozBármelyik osztályt fel lehet így osztani; a generált kód és a kézi kód szétválasztására találták ki.

Jósolj!

A Form1.cs fájlból kitörlöd a partial szót. Mi történik a fordításkor?

Jóslat ellenőrzése

Fordítási hiba (CS0260: hiányzik a partial módosító; és CS0101: a névtér már tartalmaz ilyen nevű osztályt): a két Form1 kettős definíciónak számít.

A Form1.Designer.cs tartalma

Form1.Designer.cs (egyszerűsítve)C#
partial class Form1
{
private IContainer components = null;

protected override void Dispose(bool disposing) // a nem látható komponensek (Timer) felszabadítása
{
if (disposing && components != null) { components.Dispose(); }

base.Dispose(disposing);
}

#region Windows Form Designer generated code

private void InitializeComponent()
{
this.btnMent = new System.Windows.Forms.Button(); // 1. létrehozás
this.SuspendLayout();

this.btnMent.Location = new System.Drawing.Point(24, 20); // 2. tulajdonságok (a Properties ablak értékei)
this.btnMent.Name = "btnMent";
this.btnMent.Size = new System.Drawing.Size(96, 32);
this.btnMent.Text = "Mentés";
this.btnMent.Click += new System.EventHandler(this.btnMent_Click); // 3. eseménykezelő bekötése

this.ClientSize = new System.Drawing.Size(320, 200);
this.Controls.Add(this.btnMent); // 4. a Formra helyezés
this.Name = "Form1";
this.ResumeLayout(false);
}

#endregion
}
Mit teszel a DesignerbenMit ír a Designer a fájlba
Gombot húzol a Formraúj mező (private Button btnMent;) és new Button() az InitializeComponent-ben
Átírod a Text tulajdonságotthis.btnMent.Text = "Mentés";
Dupla kattintás a gombona Click += sor a Designerben és az üres kezelő a Form1.cs-ben
Törölsz egy vezérlőta mező, a létrehozás és a tulajdonságsorok eltűnnek; az eseménykezelő metódusa a te fájlodban megmarad
Átnevezed (Name)a mező és minden hivatkozás átíródik a Designerben (a te kódodban is, ha a Designer-ből nevezed át)
Ne szerkeszd kézzel!A fájl tetején figyelmeztetés áll. A Designer a fájlt a Properties ablak alapján újraírhatja, a kézzel módosított részek elveszhetnek, vagy a Designer megjelenítése összeomlik. A tulajdonságokat a Properties ablakban állítsd.
Mégis szerkeszthetedSzükség lehet rá: például ha törölt kezelő miatt a fájl CS1061 hibát ad (a Click += sor olyan metódusra mutat, amely már nincs): ilyenkor törölheted ezt az egy sort. A művelet után a Designer újra működik.
Saját kód helyeA konstruktorban az InitializeComponent() után: public Form1() { InitializeComponent(); sajatBeallitas(); }. Itt állítod be, amit a Properties nem tud (listák feltöltése, kezdőértékek).
Mi van még?A Form1.resx a Form erőforrásait (képek, szövegek) tartalmazza; a Program.cs a belépési pont. Mindhármat a Visual Studio kezeli.

Jósolj!

A Designerben kitörlöd a btnMent gombot, de a btnMent_Click metódus a Form1.cs-ben megmarad. Hibás lesz-e a program?

Jóslat ellenőrzése

Nem: az üres metódus árván marad, de nem hibás (felesleges kód). Fordított esetben (a metódust törlöd, a Designer sorát nem) fordítási hiba lesz.

Nagy Form felosztása partial fájlokra

Form1.cs, Form1.Fajlok.cs, Form1.Rajz.csC#
// Form1.cs: konstruktor, életciklus
public partial class Form1 : Form { public Form1() { InitializeComponent(); } }

// Form1.Fajlok.cs: csak a fájlkezeléssel kapcsolatos kezelők és metódusok
public partial class Form1
{
private void btnMegnyit_Click(object sender, EventArgs e) { /* ... */ }
private void MentFajlba(string ut) { /* ... */ }
}

// Form1.Rajz.cs: a rajzolás
public partial class Form1
{
private void panel1_Paint(object sender, PaintEventArgs e) { /* ... */ }
}
Áttekinthetőbb fájlokEgy 2000 soros Form1.cs helyett téma szerinti fájlok. A fájlnév tükrözi a tartalmat (Form1.Fajlok.cs); a Solution Explorerben a Form1.cs alá csoportosulnak.
De csak kozmetikaAz osztály továbbra is egyetlen osztály: egy nagy halmaz mező és metódus, mindegyik látja mindegyiket. A felosztás nem oldja meg a rossz szerkezetet.
A valódi megoldásA fájlok és mappák kezelését, a rajzolási logikát külön osztályba kell tenni (12.7 lecke), amelyet a Form példányosít. A partial felosztás legfeljebb átmeneti lépés.
Mikor jó a partial?Generált kód mellett (Designer, adatmodell-generátor), és átmenetileg, a refaktorálás közben, hogy a nagy fájl kezelhető legyen.

Jósolj!

A Form1.Fajlok.cs-ben egy private metódust írsz. Hívható-e a Form1.Rajz.cs-ből?

Jóslat ellenőrzése

Igen: a private az osztályra vonatkozik, és a két fájl ugyanaz az osztály. Pont ezért nem ad a partial valódi elkülönítést.

📷 Képernyőkép helye

Mit tegyél ide: a Solution Explorer a Form1.cs, Form1.Designer.cs, Form1.resx és a partial fájlokkal.Javasolt fájlnév: winforms-12-2-fajlok.webp · Alt szöveg: A Form fájljai a Solution Explorerben

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. CS1061: nincs ilyen metódus

A Form1.Designer.cs: Form1 does not contain a definition for btnMent_Click.

Ok és javítás

Törölted az eseménykezelő metódusát, de a Designer még bekötötte. Add vissza a metódust, vagy töröld a Click += sort (vagy a Properties/Events ablakban töröld a kezelő nevét).

2. A Designer nem nyílik meg

A tervező nézet hibát jelez (The designer could not be shown).

Ok és javítás

Gyakori ok: a konstruktor vagy a Load kód olyasmit tesz, ami tervezési időben hibát ad (adatbázis, fájl). Védd: if (DesignMode) { return; }, vagy a futásidejű kódot a Load-ba / Shown-ba tedd. Másik ok: kézzel elrontott Designer fájl.

3. Eltűnt a beállításom

A Designer fájlba kézzel írt sor megszűnt.

Ok és javítás

A Designer újraírta a fájlt. Az egyedi beállítást a konstruktorba, az InitializeComponent() hívás után írd.

4. A kód a konstruktorban nem hat

public Form1()
{
lblCim.Text = "Üdv"; // a vezérlő még nincs létrehozva
InitializeComponent();
}
Hiba és javítás

A vezérlők az InitializeComponent() hívásban jönnek létre: előtte NullReferenceException. A hívás legyen az első sor.

5. CS0101: kettős definíció

Új fájlba bemásoltad a Form1 osztályt partial nélkül.

Ok és javítás

Minden darabon partial kell, azonos névtérben és névvel.

6. Átnevezés után fordítási hibák

A vezérlőt a Designer fájlban kézzel nevezted át.

Ok és javítás

A Name tulajdonságot a Properties ablakban (vagy Rename refaktorálással, Ctrl+R, R) módosítsd, így minden hivatkozás átíródik.

Mini projekt: Designer-felfedező

Értsd meg a Designer munkáját úgy, hogy te magad építesz fel egy Formot kódból:

  1. Elemzés: készíts Designerrel egy egyszerű Formot (címke, szövegmező, gomb), nyisd meg a .Designer.cs fájlt, és írd meg a naplódba: melyik sor mit csinál (mezők, new, tulajdonságok, Controls.Add, eseménybekötés).
  2. Kódból épített Form: új osztály NevjegyForm : Form Designer nélkül; a konstruktor new hívásokkal létrehozza a Label, TextBox, Button vezérlőket, beállítja a tulajdonságaikat, és hozzáadja a Controls gyűjteményhez.
  3. Elrendezés: egy TableLayoutPanel kétoszlopos rácsa (címke–mező), nem fix pozíciók; Dock/Anchor.
  4. Eseménykezelő: a gomb Click eseménye += operátorral, névtelen metódussal vagy névvel; az OK a mezőket egy MessageBox-ban mutatja.
  5. Felszabadítás: a Dispose(bool) felülírása a saját Timer és Font erőforrásokhoz (9.8 lecke).
  6. Felosztás: a nagy Form1-et (a 11.9 mappa-elemző) oszd három partial fájlra (.cs, .Munka.cs, .Kijelzes.cs), és állapítsd meg: mi lett jobb, és mi maradt ugyanaz.
  7. Teszteld: a kódból épített Form megjelenése, átméretezése, a Tab sorrend (TabIndex), a fájlok felosztása utáni fordítás, a törölt kezelő hibájának javítása.
Megoldás megnyitása (próbáld ki előbb önállóan!)
NevjegyForm.csC#
public class NevjegyForm : Form
{
private TextBox txtNev, txtEmail;
private Button btnOk;
private Font kiemeltBetu = new Font("Segoe UI", 11, FontStyle.Bold);

public NevjegyForm()
{
// ugyanazt csináljuk, mint az InitializeComponent: létrehozás, beállítás, elhelyezés, események
Text = "Névjegy";
ClientSize = new Size(360, 150);
StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.FixedDialog;
MaximizeBox = false;

TableLayoutPanel tabla = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 3, Padding = new Padding(12) };

tabla.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 80));
tabla.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));

txtNev = new TextBox { Dock = DockStyle.Fill, Name = "txtNev", TabIndex = 0 };
txtEmail = new TextBox { Dock = DockStyle.Fill, Name = "txtEmail", TabIndex = 1 };
btnOk = new Button { Text = "OK", Name = "btnOk", TabIndex = 2, Font = kiemeltBetu, AutoSize = true };

tabla.Controls.Add(new Label { Text = "Név:", AutoSize = true, Anchor = AnchorStyles.Left }, 0, 0);
tabla.Controls.Add(txtNev, 1, 0);
tabla.Controls.Add(new Label { Text = "E-mail:", AutoSize = true, Anchor = AnchorStyles.Left }, 0, 1);
tabla.Controls.Add(txtEmail, 1, 1);
tabla.Controls.Add(btnOk, 1, 2);

btnOk.Click += (s, e) => MessageBox.Show(this, txtNev.Text + " <" + txtEmail.Text + ">", "Névjegy"); // eseménybekötés kódból

Controls.Add(tabla);
AcceptButton = btnOk;
}

protected override void Dispose(bool disposing) // mint a Designer Dispose-a, kiegészítve a saját erőforrással
{
if (disposing) { kiemeltBetu.Dispose(); }

base.Dispose(disposing);
}
}

// hívás: using (NevjegyForm f = new NevjegyForm()) { f.ShowDialog(this); }
📷 Képernyőkép helye

Mit tegyél ide: a kódból épített névjegy-ablak és mellette a Designer fájl megfelelő sorai.Javasolt fájlnév: winforms-12-2-kesz.webp · Alt szöveg: A kódból épített Form

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. Dinamikus vezérlők

Egy gombnyomásra hozz létre kódból öt gombot egy FlowLayoutPanel-ben. Hogyan?

Egy lehetséges megoldás

Ciklusban new Button { Text = i.ToString() }, btn.Click += ... bekötés, flow.Controls.Add(btn). Bezáráskor a gombokat nem kell külön felszabadítani, a szülő Dispose metódusa megteszi; a törölt vezérlőt viszont Controls.Remove után Dispose() hívással szabadítod fel.

2. Tervezési mód védelme

A konstruktor adatbázist hív, és a Designer nem nyílik meg. Mit teszel?

Egy lehetséges megoldás

A DesignMode tulajdonságot ellenőrzöd (konstruktorban nem megbízható: a Load-ban vagy a LicenseManager.UsageMode alapján), a hívást az OnLoad/Shown eseménybe teszed.

3. Régi kezelő eltávolítása

Egy esemény bekötését kell kikapcsolnod futás közben. Hogyan?

Egy lehetséges megoldás

A -= operátor: btn.Click -= btn_Click; (névvel megadott kezelő kell; a névtelen lambda nem vehető ki).

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. Mit jelent a partial?

Fogalmazd meg egy mondatban.

Megoldás megnyitása

Az osztály több fájlban van leírva, a fordító egyetlen osztállyá egyesíti.

2. Hol a kódod?

Melyik fájlba írod az eseménykezelőt, és melyikben a Designer a vezérlőt?

Megoldás megnyitása

A kezelő a Form1.cs-be; a vezérlő létrehozása a Form1.Designer.cs InitializeComponent metódusába kerül.

Közepes szint

3. Hova tennéd?

Egy ComboBox feltöltése kezdéskor: hova írod?

Megoldás megnyitása

A konstruktorba az InitializeComponent() után, vagy a Load eseménybe (nem a Designer fájlba).

4. InitializeComponent

Mit csinál az InitializeComponent, és miért az első a konstruktorban?

Megoldás megnyitása

Létrehozza és beállítja a vezérlőket; előtte a mezők null értékűek.

Kihívás szint

5. Több Form, közös elrendezés

Két Formnak azonos eszköztára van. Mit tehetsz Designer nélkül?

Megoldás megnyitása

Közös UserControl (újrahasználható vezérlő), vagy közös ősosztály (AlapForm : Form), amelyből mindkét Form örököl.

6. A Dispose szerepe

Mikor kell felülírni a Dispose(bool) metódust?

Megoldás megnyitása

Ha a Form saját IDisposable erőforrást tart (Font, Pen, Bitmap, folyam): a disposing ágban felszabadítod, majd a base.Dispose(disposing) hívást meghívod. Kézzel írt változatot ne a Designer fájlba tedd, hanem a Form1.cs-be (a Designer fájl már tartalmaz egyet; ott helyezd el a hívást, vagy mozgasd át).

Gyors önellenőrzés

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

1. Miért két fájl a Form?

A generált és a kézi kód szétválasztására: a Designer a sajátját újraírhatja.

2. Mit tartalmaz az InitializeComponent?

A vezérlők létrehozását, tulajdonságait, eseménybekötéseit és a Controls.Add hívásokat.

3. Szerkeszthető a Designer fájl?

Általában ne; vészhelyzetben egy-egy sor (törött kezelő) igen.

4. Hova kerül az egyedi beállítás?

A konstruktorba az InitializeComponent() után.

5. Mi a CS1061 oka?

A Designer olyan kezelőt köt be, amely már nincs.

6. Megoldja-e a partial felosztás a rossz szerkezetet?

Nem: az osztály egy marad; külön osztályok kellenek.

7. Hogyan kapcsolsz ki egy eseménykezelőt?

A -= operátorral.

Mit tudsz már?

  • Érted a partial class működését és a Designer fájl szerepét.
  • Tudod, mit szabad és mit nem szabad a generált kódban szerkeszteni.
  • Javítod a törött eseménykezelő-bekötéseket.
  • A felületet kódból is felépíted, és a nagy Formot partial fájlokra osztod.

Következő lecke: Mezők, propertyk és metódusok

Az osztály építőkövei: mezők, tulajdonságok (get/set), metódusok és a köztük lévő munkamegosztás.