Grafikus C# – Windows Forms tanfolyam › 6.3 lecke

Windows Forms · 6.3 lecke · tárolók

Panel, GroupBox és SplitContainer

Egy nagy űrlapon húsz vezérlő rendezetlenül kuszaság. A tárolóvezérlők (Panel, GroupBox, SplitContainer) csoportosítják a vezérlőket: a tartalom együtt mozog, együtt kapcsolható ki, együtt igazítható, és a felhasználó számára is átlátható. A GroupBox kitüntetett szerepet kap a RadioButton-ok csoportosításában, a SplitContainer pedig húzható elválasztót ad két rész között. Ebben a leckében megtanulod mindhármat, a lapozó („oldalas”) felület alapjait, és a leggyakoribb tárolóhibákat.

  • kb. 100 perc
  • kezdő–közepes szint
  • tárolóvezérlők
Diáklány C# programot készít laptopon

A lecke végére

  • érted a szülő–gyermek kapcsolatot (Parent, Controls), és tudod, hogy a gyermek helye a szülőhöz viszonyított;
  • használod a Panel vezérlőt (AutoScroll, lapozás a Visible és BringToFront segítségével);
  • a GroupBox segítségével csoportosítod a RadioButton vezérlőket, és egy csoport tartalmát egyszerre engedélyezed vagy letiltod;
  • a SplitContainer segítségével húzható elválasztót készítesz (SplitterDistance, Panel1Collapsed, FixedPanel);
  • beágyazott tárolókkal háromoszlopos elrendezést építesz;
  • felismered a gyakori hibákat (közös RadioButton csoport, SplitterDistance kivétel, rossz szülő).

Mielőtt elkezded

Ehhez a leckéhez a 6.2 lecke (Anchor, Dock), az 1.4 lecke (RadioButton) és az 5.8 lecke (vezérlők bejárása) szükséges. Új projektben próbáld ki a példákat.

6.2 Anchor és Dock →

Tárolók: szülő és gyermek

A tárolóvezérlő (container) más vezérlőket tartalmaz. A tartalmazott vezérlők a tároló Controls gyűjteményében vannak, és a tároló a szülőjük (Parent). Ennek három következménye van:

1. A hely a szülőhöz képest

A gyermek Location értéke a szülő bal felső sarkához viszonyít, nem az ablakhoz. Ha a panelt elmozdítod, a benne lévő vezérlők vele mozognak, a Location értékük pedig nem változik.

2. Közös állapot

Ha a tároló Enabled értéke hamis, minden gyermeke is letiltott. Ha a Visible hamis, a gyermekek is eltűnnek. Így egyetlen beállítással kezelhető egy egész csoport.

3. Levágás

A gyermek csak a szülő területén belül látszik. Ami túllóg a panel szélén, levágódik (hacsak az AutoScroll nem ad görgetősávot).

4. Anchor és Dock a szülőre vonatkozik

A 6.2 lecke szabálya: a gyermek horgonya és dokkolása a közvetlen szülőre hat, ezért a tárolók egymásba ágyazásával rétegesen építhető az elrendezés.

Form1.csC#
// vezérlő áthelyezése egyik tárolóból a másikba: a Parent átállításával, vagy az Add hívásával
gomb.Parent = pnlJobb; // kikerül a régi szülőből, az újba kerül
pnlJobb.Controls.Add(gomb); // ugyanezt teszi

// a szülő lekérdezése és a koordináták átszámolása
Control szulo = gomb.Parent;
Point kepernyoHely = gomb.Parent.PointToScreen(gomb.Location); // a gomb helye a képernyőn

// egy csoport engedélyezése, letiltása egyetlen beállítással
grpBeallitasok.Enabled = chkEngedelyez.Checked;

Jósolj!

Egy gomb Location értéke a panelben (20, 10), a panel helye az ablakban (100, 50). Hol van a gomb bal felső sarka az ablak kliensterületéhez képest?

Jóslat ellenőrzése

A (120, 60) pontnál (a két érték összege): a panel bal felső sarka (100, 50), és a gomb ettől (20, 10) távolságra van. A gomb.Location értéke továbbra is (20, 10), mert a szülőhöz viszonyított. A képernyő-koordinátát a PointToScreen adja.

Panel: a láthatatlan tároló

A Panel a legegyszerűbb tároló: nincs felirata, és alapból keret sincs, csak csoportosít. Fontosabb tulajdonságai:

TulajdonságMit tesz?
BorderStyleNone (alap), FixedSingle, Fixed3D: keret a panel körül.
BackColorA panel háttérszíne (vizuális csoportosítás, színes sáv).
AutoScrollHa a tartalom nagyobb a panelnél, görgetősáv jelenik meg.
PaddingBelső margó (a dokkolt gyermekek ennyivel beljebb kerülnek).
Dock, AnchorA panel elrendezése az ablakban (6.2 lecke).

Lapozó felület: oldalak panelekkel

Gyakori minta: több panel van egymás tetején (mindegyik Dock = Fill), és mindig csak egy látszik. Ez a varázsló (wizard) és a beállítóablak alapja:

Form1.cs (pnlOldal1, pnlOldal2, pnlOldal3: Dock = Fill)C#
private Panel[] oldalak;
private int aktualis = 0;

private void Form1_Load(object sender, EventArgs e)
{
oldalak = new Panel[] { pnlOldal1, pnlOldal2, pnlOldal3 };
Mutat(0);
}

private void Mutat(int index)
{
aktualis = index;

for (int i = 0; i < oldalak.Length; i++)
{
oldalak[i].Visible = i == index; // csak az aktuális látszik
}

btnVissza.Enabled = index > 0;
btnTovabb.Text = index == oldalak.Length - 1 ? "Befejezés" : "Tovább";
lblLepes.Text = $"{index + 1}. lépés / {oldalak.Length}";
}

private void btnTovabb_Click(object sender, EventArgs e)
{
if (aktualis < oldalak.Length - 1)
{
Mutat(aktualis + 1);
}
else
{
MessageBox.Show("A varázsló kész.");
}
}

private void btnVissza_Click(object sender, EventArgs e)
{
if (aktualis > 0)
{
Mutat(aktualis - 1);
}
}
Visible vagy BringToFront?A Visible a nem aktuális oldalt rejtetté teszi (a gyermekei sem látszanak, és nem kapnak fókuszt). A BringToFront csak előre hozza, a mögötte lévők láthatatlanok, de aktívak maradnak (fókuszba kerülhetnek Tab billentyűvel). A lapozáshoz a Visible a biztonságos.
Tervezés közbenA tervezőben az egymáson lévő panelek szerkesztése nehézkes. Hasznos a Document Outline ablak, vagy a panelek ideiglenes elrendezése egymás mellé. Egyszerűbb megoldás a TabControl (6.5 lecke), amely a lapozást beépítve tudja.
AutoScrollHa a panel tartalma nagyobb, mint a panel, bekapcsolt AutoScroll mellett görgetősáv jelenik meg. A ScrollControlIntoView(vezérlő) metódus egy konkrét vezérlőt görget láthatóvá.
Dock Fill a gyermekekreHa a panel belsejében egyetlen vezérlőnek kell kitöltenie a helyet (egy lista, egy kép), állítsd annak Dock értékét Fill-re.

Jósolj!

A varázsló második oldalát a Visible = false helyett csak a SendToBack() metódussal rejted el a harmadik mögé. Mit tapasztalsz a Tab billentyű lenyomásakor?

Jóslat ellenőrzése

A takart panel vezérlői látható nélkül is megkaphatják a fókuszt a Tab billentyűvel (a felhasználó „láthatatlan” mezőbe gépel), mert a panel továbbra is látható, csak más panel fedi. A Visible = false a panelt és minden gyermekét kiveszi a fókuszsorrendből.

GroupBox: keretes csoport felirattal

A GroupBox egy feliratos keretes tároló. A Text tulajdonsága a keret címe. A Panel-hez képest a lényeg: vizuálisan jelzi, hogy a benne lévő vezérlők összetartoznak (például „Szállítási mód”). Legfontosabb szerepe a választógomboknál van:

A RadioButton csoportját a tároló adja

Az azonos tárolóban lévő RadioButton vezérlők kölcsönösen kizárják egymást: egyszerre csak egy lehet bejelölve. Ha két független választást szeretnél (például méret és szín), külön tárolóba (két GroupBox-ba) kell tenned őket. Az ablakon közvetlenül lévő összes választógomb egyetlen csoportot alkot.

Form1.cs (grpMeret: S/M/L; grpSzin: piros/kék/zöld)C#
// a csoport kiválasztott gombjának megkeresése: a GroupBox Controls gyűjteményének bejárása
private string Valasztott(GroupBox csoport)
{
foreach (Control c in csoport.Controls)
{
RadioButton rb = c as RadioButton;

if (rb != null && rb.Checked)
{
return rb.Text;
}
}

return "(nincs)";
}

private void btnRendel_Click(object sender, EventArgs e)
{
lblEredmeny.Text = "Méret: " + Valasztott(grpMeret) + ", szín: " + Valasztott(grpSzin);
}

// az egész csoport ki-be kapcsolása
private void chkEgyedi_CheckedChanged(object sender, EventArgs e)
{
grpSzin.Enabled = chkEgyedi.Checked;
}
Controls bejárásaA csoport gyermekeit a Controls gyűjtemény adja. Az as átalakítással a RadioButton típusúakat szűrjük ki, az esetleges más vezérlőket (címke) kihagyjuk (5.2 lecke).
Tag a választáshozHa a választógomb mögött érték van (méret: 1, 2, 3), tedd a Tag-be, és ne a szövegből olvasd vissza (5.8 lecke).
Alapértelmezett választásEgy csoportban legalább egy gombot állíts Checked = true értékre a tervezőben. Így sosem üres a csoport, és nem kell az „egyik sincs kiválasztva” esettel foglalkozni.
Enabled a csoportonA GroupBox.Enabled = false a benne lévő összes vezérlőt letiltja (szürkére festi). Ha egy vezérlőnek aktívnak kellene maradnia (például a csoportot kapcsoló CheckBox), tedd a csoporton kívülre, a csoport fölé.

Jósolj!

Egy ablakon közvetlenül (tároló nélkül) hat RadioButton van: háromnak a szövege Kicsi/Közepes/Nagy, háromnak Piros/Kék/Zöld. Hányat jelölhet be a felhasználó egyszerre?

Jóslat ellenőrzése

Egyet: mind a hat ugyanabban a tárolóban (az ablakon) van, ezért egyetlen csoportot alkotnak. A méret kiválasztása a szín kiválasztását törli. A megoldás: a két hármast két GroupBox (vagy Panel) tartalmazza.

SplitContainer: húzható elválasztó

A SplitContainer egy tároló két panellel (Panel1 és Panel2) és közöttük egy húzható elválasztóval (splitter). A felhasználó az elválasztót húzva méretezheti át a két részt. Tipikus használat: fájlfa és tartalom, lista és részletek.

TulajdonságJelentés
OrientationVertical (alap): bal és jobb panel, függőleges elválasztó; Horizontal: felső és alsó panel.
SplitterDistanceAz elválasztó helye: az első panel mérete képpontban.
SplitterWidthAz elválasztó vastagsága.
FixedPanelMelyik panel mérete marad állandó az ablak átméretezésekor (None, Panel1, Panel2). A másik panel nyúlik.
Panel1MinSize, Panel2MinSizeA panelek minimális mérete (az elválasztó nem húzható ennél kisebbre).
IsSplitterFixedIgaz esetén a felhasználó nem húzhatja az elválasztót.
Panel1Collapsed, Panel2CollapsedIgaz esetén az adott panel összecsukódik (eltűnik), a másik kitölti a helyet.
Form1.cs (split: SplitContainer, Dock = Fill)C#
private void Form1_Load(object sender, EventArgs e)
{
split.Panel1MinSize = 120; // a bal panel legalább 120 széles
split.Panel2MinSize = 200;
split.FixedPanel = FixedPanel.Panel1; // ablak nagyításakor a bal panel marad, a jobb nő
split.SplitterDistance = 200;

// a panelek tartalma: Dock = Fill
lstFajlok.Dock = DockStyle.Fill; // split.Panel1 gyermeke
rtbTartalom.Dock = DockStyle.Fill; // split.Panel2 gyermeke
}

// az oldalsó panel ki-be kapcsolása egy gombbal
private void btnOldal_Click(object sender, EventArgs e)
{
split.Panel1Collapsed = !split.Panel1Collapsed;
}

// az elválasztó mozgatásának követése
private void split_SplitterMoved(object sender, SplitterEventArgs e)
{
lblInfo.Text = "Bal panel szélessége: " + split.SplitterDistance;
}

Beágyazott elválasztók: három oszlop

Háromrészes elrendezéshez a SplitContainer-t egymásba ágyazzuk: a külső Panel2-be teszünk egy másik SplitContainer-t (Dock = Fill). Így két elválasztó lesz, külön húzható:

Form1.cs (splitKulso, splitBelso: a splitKulso.Panel2-ben)C#
splitKulso.Dock = DockStyle.Fill;
splitKulso.Panel1MinSize = 120; // 1. oszlop: mappák

splitBelso.Dock = DockStyle.Fill; // a külső Panel2-ben
splitBelso.Panel1MinSize = 160; // 2. oszlop: lista
splitBelso.Panel2MinSize = 200; // 3. oszlop: előnézet
Dock Fill a gyermekekreA SplitContainer panelei tárolók: a bennük lévő vezérlőket (ListBox, RichTextBox) szokás Dock = Fill értékre állítani, hogy kitöltsék a panelt.
Mentés a Settings-beA SplitterDistance értékét érdemes a FormClosed eseményben menteni, és a Load-ban visszaállítani (5.6 lecke), így a felhasználó beállítása megmarad.
Panel1CollapsedAz összecsukott panel nem foglal helyet, és tartalma nem látszik. A Panel1Collapsed = false visszaállítja az eredeti elválasztó-helyet.
Mi a különbség a TableLayoutPanel-től?A SplitContainer a felhasználó által húzható elválasztót ad. A TableLayoutPanel (6.4 lecke) a program által megadott, százalékos arányú rácsot.

Jósolj!

A Panel1MinSize értéke 150, és a Form1_Load eseményben split.SplitterDistance = 100; sort írsz. Mi történik?

Jóslat ellenőrzése

A program InvalidOperationException kivételt dob: A SplitterDistance értéke a Panel1MinSize és a (szélesség - Panel2MinSize) között legyen. A 100 kisebb a 150 minimumnál. Hasonló hibát kapsz, ha a SplitterDistance túl nagy (a második panelnek már nem marad a minimális hely), vagy ha az érték az ablak még be nem állt méretéhez képest érvénytelen. A javítás: előbb a minimum, utána a távolság, és az érték a megengedett tartományon belül legyen.

📷 Képernyőkép helye

Mit tegyél ide: egy háromoszlopos ablak (mappák, fájllista, előnézet) két húzható elválasztóval.Javasolt fájlnév: winforms-6-3-split.webp · Alt szöveg: Háromoszlopos elrendezés SplitContainer vezérlőkkel

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. Két csoport választógombjai zavarják egymást

A méret kiválasztása törli a színválasztást.

Ok és javítás

Mindkét hármas ugyanabban a tárolóban (az ablakon) van. Tedd őket külön GroupBox-ba. A már kirakott gombokat húzással a GroupBox-ba mozgathatod (a Document Outline ablakban ellenőrizheted a szülőt).

2. A vezérlő nem a panelben van

A panel mozgatásakor az egyik gomb a helyén marad.

Ok és javítás

A gomb véletlenül az ablakra került, nem a panelre (a tervezőben a panel fölé húztad, de nem „ejtetted bele”). A Document Outline ablakban látszik a hierarchia. A javítás: húzd a gombot a panelbe, vagy kódból: gomb.Parent = panel;. Az áthelyezés után a Location értéke a panelhez viszonyítva értelmezendő.

3. InvalidOperationException a SplitterDistance beállításakor

split.SplitterDistance = 100;   // de a Panel1MinSize 150
Hiba és javítás

Az érték kisebb a Panel1MinSize értékénél. Ellenőrizd a megengedett tartományt: Math.Max(split.Panel1MinSize, Math.Min(ertek, split.Width - split.Panel2MinSize - split.SplitterWidth)). Mentett érték visszaállításánál mindig így védekezz.

4. A letiltott csoportban a kapcsoló is letiltódik

Egy jelölőnégyzet a GroupBox-ban kapcsolja a csoportot, de kikapcsolás után nem lehet újra bekapcsolni.

Ok és javítás

A GroupBox.Enabled = false a benne lévő jelölőnégyzetet is letiltja. A kapcsoló legyen a csoporton kívül (fölötte). Egy trükk: a jelölőnégyzet a GroupBox felirata mellett kerüljön a csoport fölé, de ne legyen a gyermeke.

5. A tartalom hiányzik a panelből

Egy Visible = false panel után a helye üres marad, és a mellette lévő vezérlők nem foglalják el.

Ok és javítás

A sima Anchor/Dock = None vezérlők nem rendeződnek át egy rejtett panel miatt. Dokkolt panelnél (Dock = Top/Left) a Visible = false felszabadítja a helyet a többi dokkolt vezérlőnek, ezért az átrendeződő elrendezéshez dokkolást használj, vagy SplitContainer PanelXCollapsed értéket.

6. A lapozó panelek egymás alá kerültek

Az oldalak nem fedik egymást, hanem egymás alatt jelennek meg.

Ok és javítás

A panelek Dock értéke nem Fill, vagy a Location értékük eltér. A lapozáshoz minden oldal ugyanazt a helyet foglalja: Dock = Fill egy közös tárolóban, és a kód csak egy oldalt tesz láthatóvá.

Mini projekt: beállítóablak oldalakkal

Készíts beállítóablakot, amelyben a bal oldali lista kiválasztja, melyik oldal látszik a jobb oldalon:

  1. Az ablak: SplitContainer (split, Dock = Fill, Panel1MinSize: 120, FixedPanel: Panel1). A bal panelben egy lstOldalak (ListBox, Dock = Fill) a Profil, Megjelenés, Értesítések elemekkel.
  2. A jobb panelben három, egymáson lévő Panel (pnlProfil, pnlMegjelenes, pnlErtesites, mind Dock = Fill). A Profil oldalon: név és e-mail mező. A Megjelenés oldalon két GroupBox: Téma (Világos, Sötét, Kék) és Betűméret (Kicsi, Közepes, Nagy) választógombokkal. Az Értesítések oldalon egy chkErtesites kapcsoló és egy grpErtesites csoport (e-mail, felugró ablak jelölőnégyzet), amely csak bekapcsolt kapcsolónál engedélyezett.
  3. A lista kiválasztása (SelectedIndexChanged) csak a megfelelő oldalt mutatja (Visible).
  4. Egy btnOldalsav gomb a bal panelt összecsukja (Panel1Collapsed), és újra megnyitja.
  5. Az Alkalmaz gomb az lblOsszegzes címkében kiírja a kiválasztott értékeket (téma, betűméret, értesítések), a választógombok bejárásával (Valasztott metódus).
  6. Az elválasztó helye (SplitterDistance) a Settings-be mentődik, és induláskor visszaállítódik (a tartományvédelemmel).
  7. Teszteld: az oldalak váltása, a két választógomb-csoport függetlensége, a csoport letiltása, az oldalsáv összecsukása, az elválasztó húzása és az újraindítás utáni hely.
Megoldás megnyitása (próbáld ki előbb önállóan!)

A beállításhoz hozz létre egy SplitterTavolsag nevű int típusú User hatókörű beállítást (5.6 lecke). Az oldalak tömbben vannak, hogy a lista sorszáma közvetlenül az oldal sorszáma legyen.

Form1.csC#
private Panel[] oldalak;

private void Form1_Load(object sender, EventArgs e)
{
oldalak = new Panel[] { pnlProfil, pnlMegjelenes, pnlErtesites };
lstOldalak.Items.AddRange(new object[] { "Profil", "Megjelenés", "Értesítések" });

split.Panel1MinSize = 120;
split.Panel2MinSize = 200;
split.FixedPanel = FixedPanel.Panel1;

int mentett = Properties.Settings.Default.SplitterTavolsag;

if (mentett > 0)
{
int max = split.Width - split.Panel2MinSize - split.SplitterWidth;
split.SplitterDistance = Math.Max(split.Panel1MinSize, Math.Min(mentett, max));
}

grpErtesites.Enabled = chkErtesites.Checked;
lstOldalak.SelectedIndex = 0;
}

private void lstOldalak_SelectedIndexChanged(object sender, EventArgs e)
{
int index = lstOldalak.SelectedIndex;

if (index < 0)
{
return;
}

for (int i = 0; i < oldalak.Length; i++)
{
oldalak[i].Visible = i == index;
}
}

private void chkErtesites_CheckedChanged(object sender, EventArgs e)
{
grpErtesites.Enabled = chkErtesites.Checked;
}

private void btnOldalsav_Click(object sender, EventArgs e)
{
split.Panel1Collapsed = !split.Panel1Collapsed;
}

private string Valasztott(GroupBox csoport)
{
foreach (Control c in csoport.Controls)
{
RadioButton rb = c as RadioButton;

if (rb != null && rb.Checked)
{
return rb.Text;
}
}

return "(nincs)";
}

private void btnAlkalmaz_Click(object sender, EventArgs e)
{
string ertesites = chkErtesites.Checked ? "be" : "ki";

lblOsszegzes.Text = $"Téma: {Valasztott(grpTema)} | Betűméret: {Valasztott(grpBetu)} | Értesítések: {ertesites}";
}

private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
if (!split.Panel1Collapsed)
{
Properties.Settings.Default.SplitterTavolsag = split.SplitterDistance;
Properties.Settings.Default.Save();
}
}
📷 Képernyőkép helye

Mit tegyél ide: a kész beállítóablak: bal oldalon az oldalak listája, jobbra a Megjelenés oldal a két választógomb-csoporttal.Javasolt fájlnév: winforms-6-3-kesz.webp · Alt szöveg: A beállítóablak oldalakkal

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. Rendelés két választócsoporttal

Egy pizzarendelő űrlapon a méret (26 cm, 32 cm, 42 cm) és a szósz (paradicsom, tejfölös) választógombokkal választható, és egy Rendelek gomb kiírja az összesítést. Hogyan kerülöd el a csoportok keveredését, és hogyan adsz ármódosítást a mérethez?

Egy lehetséges megoldás

Két GroupBox (grpMeret, grpSzosz) tartalmazza a két csoportot. A méret ára a választógomb Tag értékében van (2000, 2800, 3600), így nem kell a szövegből kiolvasni.

Form1.csC#
private RadioButton Kivalasztott(GroupBox csoport)
{
foreach (Control c in csoport.Controls)
{
RadioButton rb = c as RadioButton;

if (rb != null && rb.Checked)
{
return rb;
}
}

return null;
}

private void btnRendel_Click(object sender, EventArgs e)
{
RadioButton meret = Kivalasztott(grpMeret);
RadioButton szosz = Kivalasztott(grpSzosz);
int ar;

if (meret == null || szosz == null || !int.TryParse(meret.Tag.ToString(), out ar))
{
lblOsszeg.Text = "Válassz méretet és szószt!";
return;
}

lblOsszeg.Text = $"{meret.Text}, {szosz.Text}: {ar} Ft";
}

2. Háromlépéses varázsló

Készíts háromoldalas regisztrációs varázslót (Adatok, Jelszó, Összegzés), Vissza és Tovább gombbal. A Tovább gomb csak akkor aktív, ha az aktuális oldal kötelező mezői ki vannak töltve. Hol ellenőrzöd?

Egy lehetséges megoldás

Az ellenőrzés egy Ervenyes() metódus: az aktuális oldal szerint dönt. A mezők TextChanged eseménye újraszámolja a gomb állapotát (5.5 lecke), és a lapozáskor is meghívjuk.

Form1.csC#
private bool Ervenyes()
{
if (aktualis == 0)
{
return txtNev.Text.Trim().Length > 0 && txtEmail.Text.Contains("@");
}

if (aktualis == 1)
{
return txtJelszo.Text.Length >= 8 && txtJelszo.Text == txtJelszo2.Text;
}

return true;
}

private void GombFrissit()
{
btnTovabb.Enabled = Ervenyes();
}

private void Mezo_TextChanged(object sender, EventArgs e)
{
GombFrissit();
}

// a Mutat(index) metódus végén: GombFrissit();

3. Háromoszlopos elrendezés

Építs háromoszlopos felületet két egymásba ágyazott SplitContainer-rel: mappák, fájlok, tartalom. A mappák oszlop összecsukható egy gombbal. Melyik panelhez rendeled a FixedPanel értéket?

Egy lehetséges megoldás

A külső elválasztó FixedPanel = Panel1 (a mappák szélessége állandó), a belső elválasztón FixedPanel = Panel1 (a fájllista szélessége állandó, az előnézet nyúlik). Az összecsukáshoz a külső Panel1Collapsed kapcsolható.

Form1.csC#
private void Form1_Load(object sender, EventArgs e)
{
splitKulso.FixedPanel = FixedPanel.Panel1;
splitKulso.Panel1MinSize = 120;
splitKulso.SplitterDistance = 160;

splitBelso.FixedPanel = FixedPanel.Panel1;
splitBelso.Panel1MinSize = 160;
splitBelso.Panel2MinSize = 200;
splitBelso.SplitterDistance = 220;
}

private void btnMappak_Click(object sender, EventArgs e)
{
splitKulso.Panel1Collapsed = !splitKulso.Panel1Collapsed;
}

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. Panel és GroupBox

Mi a különbség a Panel és a GroupBox között, és melyiket választod egy „Szállítási mód” választócsoporthoz?

Megoldás megnyitása

A GroupBox felirattal és kerettel jelzi a csoportot, a Panel csak csoportosít, és görgethető. A „Szállítási mód” csoporthoz a GroupBox a jobb: a felirat megmondja a csoport tartalmát.

2. Vezérlő áthelyezése

Egy Áthelyez gomb a btnPelda gombot a pnlBal panelből a pnlJobb panelbe teszi, majd vissza. Hogyan, és mi történik a gomb helyével?

Megoldás megnyitása

A Parent átállításával. A gomb Location értéke megmarad, de az új szülőhöz viszonyítva értelmeződik, ezért a gomb máshol látszik. Ha ugyanott akarod, számold át: PointToScreen és PointToClient.

Form1.csC#
private void btnAthelyez_Click(object sender, EventArgs e)
{
Point kepernyo = btnPelda.Parent.PointToScreen(btnPelda.Location);

btnPelda.Parent = btnPelda.Parent == pnlBal ? pnlJobb : pnlBal;

btnPelda.Location = btnPelda.Parent.PointToClient(kepernyo); // ugyanazon a képernyőhelyen marad
}

Közepes szint

3. Görgethető panel

Egy pnlLista panelben 30 jelölőnégyzet van kódból létrehozva. Mi kell ahhoz, hogy a panel görgethető legyen, és hogyan ugrasz a 25. elemre?

Megoldás megnyitása

Az AutoScroll = true görgetősávot ad, ha a tartalom nagyobb a panelnél. A ScrollControlIntoView a megadott vezérlőt látható területre görgeti.

Form1.csC#
private CheckBox[] elemek = new CheckBox[30];

private void Form1_Load(object sender, EventArgs e)
{
pnlLista.AutoScroll = true;

for (int i = 0; i < elemek.Length; i++)
{
CheckBox cb = new CheckBox();
cb.Text = "Elem " + (i + 1);
cb.Location = new Point(10, 10 + i * 28);
elemek[i] = cb;
pnlLista.Controls.Add(cb);
}
}

private void btnUgras_Click(object sender, EventArgs e)
{
pnlLista.ScrollControlIntoView(elemek[24]); // a 25. elem
}

4. Az elválasztó helyének megjegyzése

Hogyan mented és állítod vissza a SplitterDistance értéket úgy, hogy hibás (túl nagy vagy túl kicsi) mentett érték se okozzon kivételt?

Megoldás megnyitása

A visszaállításnál az értéket a megengedett tartományra szorítjuk (Math.Max és Math.Min), a mentés a FormClosed eseményben történik (5.6 lecke).

Form1.csC#
private void Visszaallit(SplitContainer sc, int mentett)
{
int min = sc.Panel1MinSize;
int max = sc.Width - sc.Panel2MinSize - sc.SplitterWidth;

if (mentett > 0 && max >= min)
{
sc.SplitterDistance = Math.Max(min, Math.Min(mentett, max));
}
}

Kihívás szint

5. Csoport rekurzív letiltása kivétellel

Írj Tilt(Control szulo, bool engedelyezve, Control kivetel) metódust, amely egy tároló minden gyermekét (a beágyazottakat is) letiltja vagy engedélyezi, kivéve egy megadott vezérlőt. Mire jó?

Megoldás megnyitása

Szerkesztő és megtekintő mód váltásánál: a csoport vezérlői le vannak tiltva, de a Szerkesztés gomb aktív marad. A szülő Enabled = false a gyermekeket is letiltaná, ezért itt a gyermekeken egyenként dolgozunk, és a szülő maga aktív marad.

Form1.csC#
private void Tilt(Control szulo, bool engedelyezve, Control kivetel)
{
foreach (Control c in szulo.Controls)
{
if (c == kivetel)
{
continue;
}

if (c.Controls.Count > 0)
{
Tilt(c, engedelyezve, kivetel); // a tároló maga aktív marad, a gyermekeit kezeljük
}
else
{
c.Enabled = engedelyezve;
}
}
}

6. Függőleges és vízszintes osztás váltása

Egy gomb a SplitContainer elrendezését vízszintesről függőlegesre és vissza váltja. Mire kell vigyázni a SplitterDistance és a minimális méretek értékénél?

Megoldás megnyitása

Az Orientation váltáskor az elválasztó helyét az új irányú mérethez kell igazítani, különben a régi érték érvénytelen lehet. Biztonságos megoldás: a minimumot átmenetileg nullázni, váltani, és az elválasztót a méret felére állítani.

Form1.csC#
private void btnIrany_Click(object sender, EventArgs e)
{
split.Panel1MinSize = 25;
split.Panel2MinSize = 25;

split.Orientation = split.Orientation == Orientation.Vertical ? Orientation.Horizontal : Orientation.Vertical;

int osszes = split.Orientation == Orientation.Vertical ? split.Width : split.Height;
split.SplitterDistance = osszes / 2; // az új irányban a felére
}

Gyors önellenőrzés

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

1. Mihez viszonyítja a gyermek a Location értékét?

A szülő bal felső sarkához, nem az ablakhoz.

2. Mi határozza meg egy RadioButton csoportját?

A tároló: az azonos tárolóban lévő választógombok kizárják egymást. Külön csoporthoz külön GroupBox vagy Panel kell.

3. Mit tesz a GroupBox.Enabled = false?

A benne lévő összes vezérlőt letiltja.

4. Mi a SplitContainer két panelje?

A Panel1 és a Panel2, közöttük húzható elválasztóval (SplitterDistance).

5. Mire való a FixedPanel?

Megadja, melyik panel mérete marad állandó az ablak átméretezésekor, a másik nyúlik.

6. Miért dobhat kivételt a SplitterDistance?

Ha az érték kisebb a Panel1MinSize értékénél, vagy a második panelnek nem marad a minimális hely.

7. Mi a különbség a Visible = false és a SendToBack között panelek lapozásánál?

A Visible = false a panelt és gyermekeit kiveszi a fókuszsorrendből, a SendToBack csak eltakarja őket (a Tab billentyű oda még eljuthat).

Mit tudsz már?

  • Érted a szülő–gyermek kapcsolatot és a tárolók közös viselkedését.
  • A Panel segítségével lapozó felületet építesz.
  • A GroupBox segítségével független választócsoportokat és letiltható blokkokat készítesz.
  • A SplitContainer segítségével húzható, összecsukható, beágyazható elválasztót használsz.
  • Felismered a gyakori tárolóhibákat.

Következő lecke: TableLayoutPanel és FlowLayoutPanel

Az elrendezés automatizálása: rács százalékos arányokkal és sorokba, oszlopokba törő folyamatos elrendezés.