Grafikus C# – Windows Forms tanfolyam › 6.4 lecke

Windows Forms · 6.4 lecke · automatikus elrendezés

TableLayoutPanel és FlowLayoutPanel

Az Anchor és a Dock egy-egy vezérlőt rendez. Egy űrlapon azonban gyakran egész csoportok vannak: címke–mező párok sorai, gombok sora, kártyák, amelyek sorokba törnek. Erre két tároló való: a TableLayoutPanel rácsba (sorok és oszlopok) rendezi a vezérlőket, százalékos vagy fix méretekkel, a FlowLayoutPanel pedig folyamatosan egymás után, sortöréssel. Ebben a leckében megtanulod mindkettőt, és azt is, hogyan állítsd össze belőlük a leggyakoribb űrlapmintákat.

  • kb. 110 perc
  • közepes szint
  • rács és folyamatos elrendezés
Diák C# programozást gyakorol egy tanulósarokban

A lecke végére

  • rácsot építesz a TableLayoutPanel segítségével, és beállítod a sorok és oszlopok méretét (Absolute, Percent, AutoSize);
  • vezérlőt helyezel a cellákba (tervezőben és kódból), és összevonod a cellákat (ColumnSpan, RowSpan);
  • a címke–mező űrlap mintát gyorsan és szépen felépíted;
  • a FlowLayoutPanel segítségével folyamatos elrendezést készítesz (FlowDirection, WrapContents, SetFlowBreak);
  • dinamikus elemlistát építesz kódból, és tudod, mikor kell a SuspendLayout;
  • felismered a gyakori hibákat (nem nyúló vezérlő, átlapolt cella, rossz méretezési típus).

Mielőtt elkezded

Ehhez a leckéhez a 6.2 lecke (Anchor, Dock) és a 6.3 lecke (tárolók, Controls) szükséges. Új projektben próbáld ki a példákat.

6.3 Panel, GroupBox és SplitContainer →

TableLayoutPanel: rács a vezérlőknek

A TableLayoutPanel sorokból és oszlopokból álló rács, amelynek minden cellájába egy vezérlő kerülhet. A rács arányait a tároló méretétől függően számolja újra, ezért az ablak átméretezésekor a tartalom rendezett marad. A tervezőben a vezérlőt a kiválasztott cellába húzod, és a rácsvonalakat a panel jobb felső sarkában lévő kis nyíl (Smart Tag) menüjéből szerkeszted (Edit Rows and Columns).

TulajdonságJelentése
ColumnCount, RowCountAz oszlopok és sorok száma.
ColumnStyles, RowStylesAz oszlopok és sorok méretezési szabálya (SizeType és érték).
CellBorderStyleA cellák körüli rácsvonalak (Single, Inset…). Tervezéskor hasznos a cellák megjelenítésére.
GrowStyleMi történik, ha több vezérlő van, mint cella: AddRows (új sor), AddColumns (új oszlop), FixedSize (hiba).
DockA rácsot általában Fill értékre állítjuk, hogy az egész tárolót kitöltse.
Form1.csC#
// rács kódból: 2 oszlop, 3 sor
TableLayoutPanel tabla = new TableLayoutPanel();
tabla.Dock = DockStyle.Fill;
tabla.ColumnCount = 2;
tabla.RowCount = 3;

tabla.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); // 1. oszlop: a tartalomhoz igazodik
tabla.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100)); // 2. oszlop: a maradék hely

for (int i = 0; i < 3; i++)
{
tabla.RowStyles.Add(new RowStyle(SizeType.AutoSize));
}

// vezérlő a cellába: Add(vezérlő, oszlop, sor)
tabla.Controls.Add(new Label { Text = "Név:", Anchor = AnchorStyles.Left }, 0, 0);
tabla.Controls.Add(new TextBox { Dock = DockStyle.Fill }, 1, 0);

this.Controls.Add(tabla);

// lekérdezés és összevonás
Control mezo = tabla.GetControlFromPosition(1, 0); // (oszlop, sor)
tabla.SetColumnSpan(cimsor, 2); // a vezérlő két oszlopot foglal el
Add(vezérlő, oszlop, sor)A sorrend (oszlop, sor) az (x, y) koordinátát követi, nem a „sor, oszlop” szokást. A sorszámozás nullától kezdődik.
Egy cella, egy vezérlőA cellában egy vezérlő fér el. Ha több vezérlő kell egy cellába, tedd őket egy Panel vagy FlowLayoutPanel tárolóba, és a tároló kerüljön a cellába.
Anchor és Dock a cellán belülA vezérlő a cellához igazodik. A Dock = Fill kitölti a cellát, az Anchor = Left a cella bal oldalán, függőlegesen középre teszi (a Top nélküli Anchor középre igazít). Az alapértelmezett Anchor (Top, Left) a bal felső sarokba tesz.
MarginA vezérlő Margin tulajdonsága a cella és a vezérlő közötti távolság. Ez adja a rács „levegőjét”, ezért ne állítsd nullára.

Jósolj!

Egy TextBox a TableLayoutPanel egyik cellájában van, Anchor = Top, Left (az alapérték). Az oszlop szélessége 400 képpont. Milyen széles a szövegmező?

Jóslat ellenőrzése

A szövegmező megtartja saját szélességét (például 100 képpont), és a cella bal felső sarkában áll. A cella mérete nem nyújtja meg: a vezérlő Dock = Fill vagy Anchor = Left, Right beállítása kell. Ez a leggyakoribb kérdés: „miért nem nyúlik a mezőm a rácsban?”

Sorok és oszlopok méretezése

Minden sor és oszlop méretét egy SizeType értékkel adod meg:

SizeTypeJelentéseMikor jó?
AbsoluteFix képpontszám (például 100). Nem változik az ablakkal.Fix szélességű oszlop (ikon, címke), fix magasságú sor.
AutoSizeA legnagyobb tartalomhoz igazodik.Címke oszlop (a leghosszabb címke szélessége), címsor magassága.
PercentA maradék helyen osztozik a többi Percent sorral/oszloppal, a megadott súly szerint.Nyúló oszlop, arányos sorok.
Percent: súly, nem százalékA megadott számok arányok: ha két oszlopnak 100 és 100 az értéke, 50–50% a felosztás; ha 30 és 70, akkor 30–70%. Az összeg nem kell, hogy 100 legyen, de az áttekinthetőség miatt érdemes.
Először a fix, aztán a maradékAz Absolute és AutoSize sorok/oszlopok foglalják el a helyüket, a Percent elemek a maradékon osztoznak. Ezért a „címke + nyúló mező” elrendezés: 1. oszlop AutoSize, 2. oszlop Percent 100.
ÖsszevonásA ColumnSpan és RowSpan több oszlopot vagy sort fed le egy vezérlővel (cím a két oszlop felett, nagy szöveges mező több soron át). A tervezőben a vezérlő tulajdonságai között is megjelenik (ColumnSpan, RowSpan).
Sor és oszlop hozzáadásaKódból: tabla.RowCount++, és hozzá egy RowStyle (tabla.RowStyles.Add(new RowStyle(SizeType.AutoSize))). A tervezőben a Smart Tag menüből.

Jósolj!

Egy rács három oszlopának stílusa: Absolute 100, Percent 30, Percent 70. A rács 1100 képpont széles. Mekkorák az oszlopok?

Jóslat ellenőrzése

Az első oszlop 100 képpont, a maradék 1000 képponton osztozik a két százalékos oszlop: 300 és 700 képpont. Az ablak nagyításakor a fix oszlop marad, a másik kettő a megnövekedett maradékon osztozik 30:70 arányban.

Az űrlap-minta: címke és mező párok

Az egyik leggyakoribb elrendezés a címke–mező űrlap. Egy TableLayoutPanel így oldja meg szépen és átméretezhetően:

  1. Tegyél az ablakra egy TableLayoutPanel-t (Dock = Fill, Padding: 12), 2 oszloppal, annyi sorral, ahány mező kell, plusz egy gombsorral.
  2. Az 1. oszlop: AutoSize (a leghosszabb címke szélessége). A 2. oszlop: Percent 100 (a többi hely).
  3. A sorok: AutoSize (a vezérlők magassága szerint). Az utolsó sor lehet Percent 100, ha egy nagy szövegmező tölti ki a maradékot.
  4. A címkék: Anchor = Left (a cellában függőlegesen középre kerülnek a mezőhöz igazítva), AutoSize = True.
  5. A mezők: Dock = Fill (vagy Anchor = Left, Right), így kitöltik a második oszlopot.
  6. A gombsor: egy FlowLayoutPanel a két oszlopra összevonva (ColumnSpan = 2), amelyben a gombok jobbra igazodnak (FlowDirection = RightToLeft).
Form1.cs (tlpUrlap: a tervezőben létrehozott 2 oszlopos rács)C#
private void SorHozzaad(TableLayoutPanel tabla, string cimke, Control mezo)
{
int sor = tabla.RowCount; // az új sor sorszáma

tabla.RowCount = tabla.RowCount + 1;
tabla.RowStyles.Add(new RowStyle(SizeType.AutoSize));

Label felirat = new Label();
felirat.Text = cimke;
felirat.AutoSize = true;
felirat.Anchor = AnchorStyles.Left;

mezo.Dock = DockStyle.Fill;

tabla.Controls.Add(felirat, 0, sor);
tabla.Controls.Add(mezo, 1, sor);
}

Miért jobb ez, mint az Anchor?

Az űrlap a betűmérettől is független marad: ha a felhasználó nagyobb betűméretet állít be a Windowsban (6.8 lecke), az AutoSize címkeoszlop kitágul, és a mezők a maradékot kapják. A fix Location értékekkel és Anchor-ral készült űrlapon a vezérlők ilyenkor összecsúsznak.

Jósolj!

Az űrlap első oszlopa Percent 50, a második Percent 50 (a címke–mező mintában az AutoSize/Percent 100 helyett). Mit látsz a címkék és a mezők arányában?

Jóslat ellenőrzése

A címkék oszlopa az űrlap felét foglalja el, a címkék kicsik és bal oldalon állnak, mellettük hatalmas üres hely marad, és a mezők csak a jobb felet töltik ki. Az arányos oszlop a tartalomtól függetlenül nyúlik. A címke oszlopnak AutoSize, a mező oszlopnak Percent 100 a helyes beállítás.

FlowLayoutPanel: folyamatos elrendezés

A FlowLayoutPanel a benne lévő vezérlőket egymás után rendezi, mint a szöveg szavait: az elejétől haladva, és ha elfogy a hely, új sorba tör. Nem kell koordinátákat megadnod: a vezérlők sorrendje (a hozzáadás sorrendje) adja a helyüket. Fontos tulajdonságai:

TulajdonságJelentése
FlowDirectionLeftToRight (alap), RightToLeft, TopDown, BottomUp: a folyás iránya.
WrapContentsIgaz (alap): sortörés, ha elfogy a hely. Hamis: a vezérlők egy sorban maradnak (a kilógók levágódnak, AutoScroll mellett görgethetők).
AutoScrollGörgetősáv, ha a tartalom nagyobb.
AutoSize, AutoSizeModeA panel a tartalmához igazodik (GrowAndShrink).
Form1.cs (flpCimkek: FlowLayoutPanel, Dock = Fill)C#
private void CimkeHozzaad(string szoveg)
{
Button cimke = new Button();
cimke.Text = szoveg + " ✕";
cimke.AutoSize = true;
cimke.Margin = new Padding(4);
cimke.Tag = szoveg;
cimke.Click += CimkeTorol_Click;

flpCimkek.Controls.Add(cimke); // a sorban az utolsó helyre kerül
}

private void CimkeTorol_Click(object sender, EventArgs e)
{
Button cimke = (Button)sender;

cimke.Click -= CimkeTorol_Click; // 5.7 lecke: leiratkozás és felszabadítás
flpCimkek.Controls.Remove(cimke);
cimke.Dispose();
}

private void txtUj_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter && txtUj.Text.Trim().Length > 0)
{
e.SuppressKeyPress = true;
CimkeHozzaad(txtUj.Text.Trim());
txtUj.Clear();
}
}

// sortörés kényszerítése egy vezérlő után
flpCimkek.SetFlowBreak(csoportCim, true);

// sorrend módosítása: a vezérlő az elejére kerül
flpCimkek.Controls.SetChildIndex(cimke, 0);
Margin a résA vezérlők közötti távolságot a Margin adja (alapértelmezésben 3 képpont). A FlowLayoutPanel nem ad külön „térköz” tulajdonságot.
SetFlowBreakSortörést kényszerít a megadott vezérlő után. Így csoportokra bonthatod a folyamot (kategóriák címe külön sorban).
SetChildIndexA vezérlő sorrendjét módosítja: a folyamatos elrendezésben a sorrend a hely, így az áthelyezés a vizuális helyet is megváltoztatja.
Gombsor jobbraA FlowDirection = RightToLeft beállítással a gombok jobbról balra követik egymást: az első hozzáadott gomb a jobb szélen áll. Ezért az OK gombot adjuk elsőnek, a Mégse gombot másodiknak.

Jósolj!

A FlowLayoutPanel WrapContents értéke hamis, a panel szélessége 300 képpont, és tíz 80 képpont széles gombot adsz hozzá. Mit látsz?

Jóslat ellenőrzése

Mind a tíz gomb egyetlen sorban áll, és csak az első három-négy látszik, a többi a panel szélén túl kilóg, és levágódik. AutoScroll = true mellett vízszintes görgetősáv jelenik meg. Alapértelmezett WrapContents = true esetén a gombok három-négy darabonként új sorba törnének.

📷 Képernyőkép helye

Mit tegyél ide: egy címkefelhő: a FlowLayoutPanel sorokba tört gombjai, alatta a beviteli mező új címkék hozzáadásához.Javasolt fájlnév: winforms-6-4-flow.webp · Alt szöveg: Sorokba törő címkék FlowLayoutPanelben

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 mező nem nyúlik a rácsban

A TextBox a második oszlopban kicsi marad, bármekkora az ablak.

Ok és javítás

A vezérlő Anchor értéke az alapértelmezett (Top, Left), és a cella nem nyújtja. Állítsd a Dock értékét Fill-re, vagy az Anchor értékét Left, Right (Top, Left, Right) értékre.

2. Két vezérlő ugyanabban a cellában

tabla.Controls.Add(txtA, 1, 0);
tabla.Controls.Add(txtB, 1, 0); // ugyanaz a cella!
Hiba és javítás

A két vezérlő átlapolódik (egymáson látszanak), a második takarja az elsőt. Egy cellába egy vezérlő való. Ha kettő kell, tedd őket egy Panel vagy FlowLayoutPanel tárolóba, és azt a tárolót tedd a cellába.

3. Az oszlop vagy sor szélessége nem a vártnak megfelelő

A Percent oszlopok mellett az AutoSize oszlop túl széles lett.

Ok és javítás

Az AutoSize oszlop a legszélesebb tartalmát követi: egy hosszú, nem tördelt címke túlnyújtja az oszlopot. Állíts be a címkére MaximumSize értéket és AutoSize = True, vagy rövidítsd a címkét. A százalékos oszlopok a maradékon osztoznak, ezért kisebbek lesznek.

4. A FlowLayoutPanel nem tördel

A gombok egy sorban maradnak, és kilógnak.

Ok és javítás

A WrapContents hamis (vagy a panel AutoSize értéke igaz, ezért a panel a tartalmához nő, és nincs mire tördelni). Kapcsold be a WrapContents tulajdonságot, és add meg a panel rögzített szélességét (Dock, Anchor, vagy MaximumSize).

5. Villog és lassú az elemek hozzáadása

for (int i = 0; i < 500; i++)
{
flp.Controls.Add(new Button()); // minden hozzáadás újraszámolja az elrendezést
}
Hiba és javítás

Minden Add újrarendezi a panelt. Fogd közre flp.SuspendLayout(); és flp.ResumeLayout(); hívással: az elrendezés csak egyszer számolódik újra a végén. Sok elemnél (több száz) érdemes más megoldáson gondolkodni (lista, DataGridView), mert minden elem egy külön vezérlő.

6. Az oszlop nem érvényesül, pedig beállítottam

tabla.ColumnCount = 3;
// de a ColumnStyles gyűjtemény csak két elemet tartalmaz
Hiba és javítás

A ColumnCount és a ColumnStyles nem mindig marad szinkronban. Minden oszlophoz tartozzon egy ColumnStyle (a hiányzó elemeket add hozzá, a felesleges stílusokat töröld), és kódból az oszlop hozzáadásakor a stílust is add hozzá.

Mini projekt: regisztrációs űrlap és címkefelhő

Készíts átméretezhető regisztrációs űrlapot, amelyben rács és folyamatos elrendezés együtt dolgozik:

  1. Az ablak: MinimumSize 480 × 460, egy TableLayoutPanel (tlp, Dock = Fill, Padding 12) két oszloppal (AutoSize, Percent 100).
  2. Az első sor egy lblCim címsor (Új felhasználó, nagyobb betű, ColumnSpan = 2).
  3. A következő sorok: Név (txtNev), E-mail (txtEmail), Születési dátum (dtpDatum), Ország (cmbOrszag), mind Dock = Fill, a címkék Anchor = Left.
  4. Egy Érdeklődési kör sor, amelynek mezője egy FlowLayoutPanel (flpKorok, AutoSize) a címkék számára, alatta egy txtUjKor mező: Enter új címkét ad hozzá, a címkére kattintva törlődik (Dispose, leiratkozás).
  5. Az utolsó sor egy FlowLayoutPanel (flpGombok, FlowDirection = RightToLeft, ColumnSpan = 2) a Regisztráció és Mégse gombbal; a jobb szélen álljon a Regisztráció gomb.
  6. A Regisztráció gomb ellenőrzi a kötelező mezőket (név, e-mail @ jellel), és lblUzenet mutatja az eredményt (az érdeklődési körökkel együtt).
  7. Teszteld: átméretezés (a mezők nyúlnak), nagyobb betűméret a Windowsban (az űrlap nem csúszik szét), sok címke hozzáadása (sortörés), címke törlése, a gombsor helye.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A rács a tervezőben készül (Smart Tag → Edit Rows and Columns), a kód a címkefelhő és az ellenőrzés. A kör címkéi Button vezérlők, mert egy Label kattintása kevésbé kézenfekvő, de Label is használható. A Tag tárolja a kör nevét (5.8 lecke).

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
this.MinimumSize = new Size(480, 460);
cmbOrszag.Items.AddRange(new object[] { "Magyarország", "Ausztria", "Szlovákia", "Románia" });
cmbOrszag.SelectedIndex = 0;
}

private void KorHozzaad(string nev)
{
foreach (Control c in flpKorok.Controls)
{
if (c.Tag != null && c.Tag.ToString().ToLower() == nev.ToLower())
{
return; // már szerepel
}
}

Button cimke = new Button();
cimke.Text = nev + " ✕";
cimke.AutoSize = true;
cimke.Margin = new Padding(3);
cimke.Tag = nev;
cimke.Click += Kor_Click;

flpKorok.Controls.Add(cimke);
}

private void Kor_Click(object sender, EventArgs e)
{
Button cimke = (Button)sender;

cimke.Click -= Kor_Click;
flpKorok.Controls.Remove(cimke);
cimke.Dispose();
}

private void txtUjKor_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.SuppressKeyPress = true;

string nev = txtUjKor.Text.Trim();

if (nev.Length > 0)
{
KorHozzaad(nev);
txtUjKor.Clear();
}
}
}

private void btnRegisztracio_Click(object sender, EventArgs e)
{
if (txtNev.Text.Trim().Length == 0)
{
lblUzenet.Text = "A név kötelező.";
txtNev.Focus();
return;
}

if (!txtEmail.Text.Contains("@"))
{
lblUzenet.Text = "Az e-mail címnek tartalmaznia kell a @ jelet.";
txtEmail.Focus();
return;
}

List<string> korok = new List<string>();

foreach (Control c in flpKorok.Controls)
{
korok.Add(c.Tag.ToString());
}

lblUzenet.Text = $"Regisztrálva: {txtNev.Text.Trim()} ({cmbOrszag.Text}), érdeklődés: {string.Join(", ", korok)}";
}

private void btnMegse_Click(object sender, EventArgs e)
{
this.Close();
}
📷 Képernyőkép helye

Mit tegyél ide: a kész űrlap: a rendezett címke–mező párok, alatta a címkék sorokba törve, és a jobb alsó sarokban a gombok.Javasolt fájlnév: winforms-6-4-kesz.webp · Alt szöveg: A regisztrációs űrlap és a címkefelhő

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. Számológép gombrács

Készíts 4 × 5 rácsot egy számológép gombjainak (TableLayoutPanel, minden sor és oszlop Percent, a gombok Dock = Fill), amelynek gombjait kódból hozod létre, és egyetlen közös kezelő a Tag alapján dolgozik. Miért jobb ez a Location számításnál?

Egy lehetséges megoldás

A rács automatikusan egyenlő méretű cellákra osztja a helyet, és az ablak átméretezésekor a gombok is nőnek. Kézi Location és Size számításnál a Resize eseményben kellene mindent újraszámolni.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
string[] jelek = { "7", "8", "9", "÷", "4", "5", "6", "×", "1", "2", "3", "-", "0", ",", "=", "+" };

tlpGombok.ColumnCount = 4;
tlpGombok.RowCount = 4;
tlpGombok.ColumnStyles.Clear();
tlpGombok.RowStyles.Clear();

for (int i = 0; i < 4; i++)
{
tlpGombok.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25));
tlpGombok.RowStyles.Add(new RowStyle(SizeType.Percent, 25));
}

for (int i = 0; i < jelek.Length; i++)
{
Button gomb = new Button();
gomb.Text = jelek[i];
gomb.Dock = DockStyle.Fill;
gomb.Tag = jelek[i];
gomb.Click += Gomb_Click;

tlpGombok.Controls.Add(gomb, i % 4, i / 4); // oszlop: maradék, sor: egész osztás
}
}

private void Gomb_Click(object sender, EventArgs e)
{
txtKijelzo.Text = txtKijelzo.Text + ((Button)sender).Tag;
}

2. Képgaléria méretezővel

Egy FlowLayoutPanel tizenkét színes négyzetet (Panel) mutat. Egy TrackBar (40–160) a négyzetek méretét állítja, és a panel újrarendezi őket. Hogyan oldod meg hatékonyan?

Egy lehetséges megoldás

A Scroll esemény végigmegy a négyzeteken, és a méretet állítja. A SuspendLayout/ResumeLayout miatt a rendezés csak egyszer számolódik.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
Random r = new Random();

for (int i = 0; i < 12; i++)
{
Panel negyzet = new Panel();
negyzet.BackColor = Color.FromArgb(r.Next(60, 230), r.Next(60, 230), r.Next(60, 230));
negyzet.Size = new Size(trbMeret.Value, trbMeret.Value);
flpGaleria.Controls.Add(negyzet);
}
}

private void trbMeret_Scroll(object sender, EventArgs e)
{
flpGaleria.SuspendLayout();

foreach (Control c in flpGaleria.Controls)
{
c.Size = new Size(trbMeret.Value, trbMeret.Value);
}

flpGaleria.ResumeLayout();
}

3. Dinamikus táblázatsorok

Egy TableLayoutPanel három oszlopot (Termék, Mennyiség, Törlés) tartalmaz. Az Új sor gomb új sort ad hozzá egy TextBox, egy NumericUpDown és egy Törlés gombbal; a Törlés gomb az adott sort eltávolítja. Mi a nehézség a sor törlésével?

Egy lehetséges megoldás

A TableLayoutPanel nem tud „sort törölni”: a vezérlőket el kell távolítani, a többi sor vezérlőit pedig egy sorral feljebb kell helyezni, majd a RowCount csökkenthető. Egyszerűbb, ha minden sor egy Panel (vagy FlowLayoutPanel TopDown irányban), így a sor törlése egyetlen vezérlő eltávolítása.

Form1.cs (flpSorok: FlowLayoutPanel, TopDown, WrapContents = false)C#
private void btnUjSor_Click(object sender, EventArgs e)
{
Panel sor = new Panel();
sor.Size = new Size(flpSorok.ClientSize.Width - 25, 32);

TextBox termek = new TextBox { Location = new Point(0, 4), Width = 200 };
NumericUpDown db = new NumericUpDown { Location = new Point(210, 4), Width = 70, Minimum = 1, Value = 1 };
Button torol = new Button { Text = "Törlés", Location = new Point(290, 2), Tag = sor };

torol.Click += SorTorol_Click;

sor.Controls.Add(termek);
sor.Controls.Add(db);
sor.Controls.Add(torol);
flpSorok.Controls.Add(sor);
}

private void SorTorol_Click(object sender, EventArgs e)
{
Button torol = (Button)sender;
Panel sor = (Panel)torol.Tag;

torol.Click -= SorTorol_Click;
flpSorok.Controls.Remove(sor);
sor.Dispose(); // a gyermekeit is felszabadítja
}

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. SizeType értékek

Add meg, melyik SizeType-ot használnád: (a) egy ikon oszlop, amely mindig 32 képpont; (b) a címkék oszlopa, amely a leghosszabb címke szélességéhez igazodik; (c) az űrlap fő része, amely kitölti a maradék helyet.

Megoldás megnyitása

(a) Absolute 32; (b) AutoSize; (c) Percent 100.

2. Vezérlő a cellába kódból

Írd meg a kódot, amely egy Mentés gombot a rács 2. oszlopának 4. sorába (a számozás nullától) teszi, és kitölti a cellát.

Megoldás megnyitása

Az Add(vezérlő, oszlop, sor) sorrendje (x, y). A 2. oszlop indexe 1, a 4. soré 3.

Form1.csC#
Button mentes = new Button();
mentes.Text = "Mentés";
mentes.Dock = DockStyle.Fill;

tlp.Controls.Add(mentes, 1, 3);

Közepes szint

3. Címsor két oszlopon

Egy rács első sorában egy címke a két oszlopot lefedi, és középre igazított. Hogyan állítod be kódból?

Megoldás megnyitása

A SetColumnSpan(címke, 2) hívással. A középre igazításhoz a címke szövege TextAlign = MiddleCenter, és a cellát kitölti a Dock = Fill.

Form1.csC#
Label cim = new Label();
cim.Text = "Új felhasználó";
cim.Dock = DockStyle.Fill;
cim.TextAlign = ContentAlignment.MiddleCenter;
cim.Font = new Font(cim.Font.FontFamily, 14, FontStyle.Bold);

tlp.Controls.Add(cim, 0, 0);
tlp.SetColumnSpan(cim, 2);

4. Sortörés kategória után

Egy FlowLayoutPanelben kategóriák címei (Label) és alattuk elemek (Button) állnak, és minden kategóriacím után új sor kezdődik. Hogyan érted el, hogy a cím külön sorban legyen?

Megoldás megnyitása

A SetFlowBreak(cím, true) a címke után sortörést kényszerít, így az elemek új sorban kezdődnek. Ha a következő cím előtt is új sor kell, az utolsó elem után is kell törés.

Form1.csC#
Label gyumolcs = new Label { Text = "Gyümölcsök", AutoSize = true, Font = new Font(Font, FontStyle.Bold) };
flp.Controls.Add(gyumolcs);
flp.SetFlowBreak(gyumolcs, true); // a cím után új sor

foreach (string nev in new string[] { "Alma", "Körte", "Szilva" })
{
flp.Controls.Add(new Button { Text = nev, AutoSize = true });
}

Kihívás szint

5. Rács sorainak összehasonlítása a sebességben

Mérd meg a Stopwatch segítségével, mennyi ideig tart 300 gomb hozzáadása egy FlowLayoutPanelhez SuspendLayout nélkül és azzal. Mit tapasztalsz?

Megoldás megnyitása

A SuspendLayout nélkül minden Add újrarendez, ezért a futásidő sokszorosa lehet. A pontos arány a géptől függ: futtasd le, és jegyezd fel az értékeket.

Form1.csC#
private long Meres(bool felfuggeszt)
{
System.Diagnostics.Stopwatch ora = System.Diagnostics.Stopwatch.StartNew();

flp.Controls.Clear();

if (felfuggeszt)
{
flp.SuspendLayout();
}

for (int i = 0; i < 300; i++)
{
flp.Controls.Add(new Button { Text = i.ToString(), Width = 50 });
}

if (felfuggeszt)
{
flp.ResumeLayout();
}

ora.Stop();
return ora.ElapsedMilliseconds;
}

private void btnMeres_Click(object sender, EventArgs e)
{
long nelkul = Meres(false);
long vele = Meres(true);

lblEredmeny.Text = $"Nélküle: {nelkul} ms, vele: {vele} ms";
}

6. Melyik tároló való?

Dönts: (a) egy űrlap tíz címke–mező párral; (b) egy gombsor jobbra igazítva; (c) egy képgaléria, amely sorokba tör; (d) a bal oldali navigáció és a fő tartalom, a felhasználó húzásával átméretezhetően. Melyik tárolót használod mindegyikhez?

Megoldás megnyitása

(a) TableLayoutPanel (AutoSize + Percent 100 oszlopok); (b) FlowLayoutPanel RightToLeft irányban; (c) FlowLayoutPanel sortöréssel; (d) SplitContainer (6.3 lecke).

Gyors önellenőrzés

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

1. Mit jelent a Percent a TableLayoutPanelben?

Súlyt: a fix (Absolute, AutoSize) elemek után megmaradó helyen osztozik a többi Percent elemmel, a megadott arányban.

2. Hogyan nyúlik a vezérlő a cellában?

Dock = Fill vagy Anchor = Left, Right beállítással.

3. Mi a helyes mintája a címke–mező űrlapnak?

Két oszlop: az első AutoSize, a második Percent 100; a sorok AutoSize; a mezők Dock = Fill, a címkék Anchor = Left.

4. Hány vezérlő fér egy cellába?

Egy. Több vezérlőhöz tároló kell a cellában (Panel, FlowLayoutPanel).

5. Mire való a SetFlowBreak?

A megadott vezérlő után sortörést kényszerít a FlowLayoutPanelben.

6. Hogyan készítesz jobbra igazított gombsort?

FlowLayoutPanel FlowDirection = RightToLeft irányban: az első hozzáadott gomb a jobb szélen áll.

7. Mikor kell a SuspendLayout?

Sok vezérlő hozzáadásakor vagy módosításakor, hogy az elrendezés csak egyszer számolódjon újra a ResumeLayout hívásánál.

Mit tudsz már?

  • Rácsot építesz a TableLayoutPanel segítségével, és beállítod a sorok és oszlopok méretezését.
  • A címke–mező űrlap mintát megépíted, és gombsort igazítasz jobbra.
  • A FlowLayoutPanel segítségével folyamatos, sortörő elrendezést és dinamikus elemlistát készítesz.
  • Tudod, mikor kell a SuspendLayout, és hogyan szabadítod fel a törölt vezérlőket.
  • Felismered a gyakori hibákat (nem nyúló vezérlő, átlapolt cella, WrapContents).

Következő lecke: TabControl és többlapos felületek

A felület több oldalra osztása füles lapokkal: lapok, eseményeik, és a lapok kódból való kezelése.