Často se nám stává, že na rozsáhlejším formuláři se nám to hemží mnoha skupinami komponent, které prakticky vzato dělají to samé, akorát s jinými hodnotami. Občas potřebujeme chování takové skupiny změnit, ale to znamená, že musíme tyto změny provést na více místech, podle toho, kolik skupin používáme. Dnes se mě jeden člověk ptal, jestli se nedá víc komponent seskupit k sobě, a tak jsem se rozhodl, že o tom napíšu článek. Není to vůbec složité.
Přidání User Control
User Controls jsou komponenty, které si poskládáme sami z komponent již existujících. Visual Studio nám samozřejmě umožňuje vizuálně navrhnout vzhled celé komponenty, stejně jako navrhujeme i formuláře. Vytvořte si tedy nový projekt Windows Application, v Solution Exploreru klikněte pravým tlačítkem na název projektu a vyberte v nabídce Add položku User Control. Pojmenujte ji třeba FolderPicker.
Naše komponenta bude složena z textového pole, tlačítka a obrázku. Do textového pole buď sami zapíšeme nějakou cestu, nebo klikneme na tlačítko Procházet a cestu vybereme z dialogu. Pokud zadaná cesta existuje, ukáže se obrázek zelené fajfky, jinak bude schovaný. Přidejte tedy na formulář komponenty TextBox, Button a PictureBox. Vhodně je uspořádejte a do PictureBoxu nahrajte obrázek zelené fajfky (nejprve na obrázek klikněte pravým tlačítkem a vyberte Uložit obrázek jako...).
Hotová komponenta by měla vypadat asi takto:
Pokročilá nastavení velikosti komponenty
Protože uživatelé mohou měnit velikost této komponenty, musíme nastavit ještě pár vlastností. Výška komponenty musí zůstat stále stejná. Nebudeme ji zvětšovat ani zmenšovat. Každá komponenta má k tomuto účelu vlastnosti MaximumSize a MinimumSize. Vlastnost MinimumSize nastavte na 200; 40 a vlastnost MaximumSize nastavte na 1000; 40. Tím omezíme minimální a maximální velikost komponenty - první číslo je šířka a druhé výška. Obě tyto vlastnosti jsou datového typu System.Drawing.Size, který má vlastnosti Width a Height. Těm se přiřazují právě tato dvě čísla.
Komponentu tedy můžeme nyní roztahovat jen do šířky. Zde je ale další problém - při změně velikosti se komponenty nechovají tak, jak by měly. Zůstanou na místě a neposunují se spolu s komponentou. Na většinu takových situací nám stačí vlastnost Anchor, což v překladu znamená kotva. Má čtyři nastavení, která můžeme dohromady kombinovat. Pokud ji budete chtít nastavit, ukáže se speciální ovládací prvek, v němž můžete nastavit čtyři směry. Vyberte tedy nejprve obrázek a nastavte mu kotvu na směry vpravo a nahoru. Pokud je nastavená kotva, při změně velikosti bude komponenta od okrajů na zvolených stranách udržovat stejnou vzdálenost. Pokud tedy celou komponentu rozšíříme, obrázek bude udržovat dstále stejnou vzdálenost od pravého kraje komponenty. Bude se tedy posouvat vpravo či vlevo a bude mát od pravého kraje komponenty stále stejnou vzdálenost. To samé nastavení dejte i tlačítku.
Zbývá nám textové pole. Tam zapněte kotvu vlevo, vpravo a nahoře. Protože textové pole musí udržovat stále stejnou vzdálenost od pravého i od levého okraje komponenty, při roztahování mu nezbyde než se roztahovat spolu s komponentou. Pokud tedy teď zkusíte komponentě měnit velikost, bude se chovat správně.
Aplikační logika komponenty
Ještě před psaním kódu nastavte obrázku vlastnost Visible na False, aby ze začátku nebyl vidět. Do kódu přidáme nejprve proceduru CheckPathExists, která zobrazí či schová obrázek podle toho, jestli zadaná cesta existuje. Přepněte se tedy do režimu kódu a vložte sem tyto řádky:
Public Sub CheckPathExists()
PictureBox1.Visible = IO.Directory.Exists(TextBox1.Text)
End Sub
Tuto kontrolu musíme zajistit, pokud opustíme textové pole. Je zbytečné kontrolovat cestu po každém znaku (pokud by byla cesta v síti, asi by to i dost zpomalovalo), stačí tedy provést kontrolu, jakmile kurzor opustí textové pole a přeskočí na jinou komponentu. V rozbalovacích seznamech nahoře tedy vyberte hodnoty TextBox1 a LostFocus, vytvoří se nám procedura události LostFocus, která nastane, když kurzor opustí textové pole (např. po kliknutí jinam nebo po stisku tabulátoru).
Private Sub TextBox1_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.LostFocus
CheckPathExists()
End Sub
Nyní ještě zbývá naprogramovat tlačítko Procházet. Přidejte do naší komponenty ještě komponentu FolderBrowserDialog, což je dialog pro výběr cesty. Poklepejte na tlačítko Procházet a zapište do něj tento kód:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If FolderBrowserDialog1.ShowDialog = DialogResult.OK Then
TextBox1.Text = FolderBrowserDialog1.SelectedPath
CheckPathExists()
End If
End Sub
Tato komponenta je odvozena od formuláře, má tedy metodu ShowDialog, která okno zobrazí a počká, dokud jej uživatel neuzavře. Další kód se provádí až potom, když je dialog pryč. Tato metoda vrátí OK, pokud uživatel složku vybral a potvrdil (mohl totiž také kliknout na Storno a to pak nebudeme dělat nic). Pokud tedy složku opravdu vybral, nastavíme její cestu do textového pole a zkontrolujeme, jestli existuje. To by byla ta jednodušší část. Pokud se přepnete na formulář, v soupravě nástrojů se naše komponenta objeví. Můžete si ji na formulář přidat a vyzkoušet.
Vlastnosti komponenty
Pokud jste komponentu zkusili někde použít, jaké asi bylo vaše překvapení, když jste zjistili, že se z formuláře na vnitřní komponenty jen tak nedostanete. Naše vlastní komponenty má totiž jen ty vlastnosti, které má třída System.Windows.Forms.Control, což je jakási obecná komponenta, od které jsou odvozeny všechny ostatní (říká se tomu dědičnost). My bychom určitě potřebovali vlastnosti SelectedPath a IsValid. První vlastnost vrátí cestu, která je v komponentě zadaná, a druhá vrátí True, pokud je cesta správná, jinak vrátí False.
Tyto vlastnosti musíme v komponentě nadeklarovat. Používá se k tomu speciální konstrukce Property. Ta má dvě části - Get a Set. Get je vlastně funkce, která vrátí hodnotu této vlastnosti, a Set je procedura, která nastaví předanou hodnotu do této vlastnosti. Názorně je to vidět na příkladu vlastnosti SelectedPath:
Public Property SelectedPath() As String
Get
Return TextBox1.Text
End Get
Set(ByVal value As String)
TextBox1.Text = value
CheckPathExists()
End Set
End Property
Mezi řádky Get a End Get máme vlastní kód funkce Get - je to jediný řádek, který způsobí, že funkce Get vrátí text zapsaný v komponentě TextBox1. Mezi řádky Set(value As String) a End Set musíme hodnotu value předanou jako parametr do komponenty nastavit. Přiřadíme ji tedy jako text do komponenty TextBox1 a ještě zkontrolujeme správnost cesty a podle toho možná ukážeme fajfku.
Tato deklarace samozřejmě patří do souboru s naší komponentou, ne do formuláře. Musí být uvnitř třídy FolderPicker, což je třída naší komponenty. Z jiného místa totiž nemůžeme přistupovat ke komponentě TextBox1.
Tento kód se navenek projeví tak, že ve formuláři v komponentě uvidíme vlastnost SelectedPath stejně, jako všechny ostatní vlastnosti. Můžeme jí přiřazovat hodnoty nebo je z ní číst tak, jak to děláme s jinými vlastnostmi. Uvnitř má však každá vlastnost svoji Get a Set (speciální vlastnosti jen jednu z nich) a ty obsahují opět nějaký kód.
Druhá vlastnost IsValid vrací True nebo False podle toho, jestli je zadaná cesta správná. Do této vlastnosti ale nemůžeme přiřazovat, jedná se tedy o speciální typ vlastnost, která má jen funkci Get. Tato vlastnost je ReadOnly (jen ke čtení) a to také musíme zapsat do její deklarace:
Public ReadOnly Property IsValid() As Boolean
Get
Return PictureBox1.Visible
End Get
End Property
Je to velmi jednoduché - vrátíme hodnotu vlastnosti Visible komponenty PictureBox1. Pokud je totiž obrázek fajfky vidět (Visible = True), cesta je správná a musíme vrátit True. Pokud fajfka vidět není, vrátíme False.
A to je prakticky vše - naučili jsme se vytvářet vlastní komponentu a deklarovat jí vlastnosti, které potřebujeme. Nezapomeňte na klíčové slovo ReadOnly před vlastnostmi jen pro čtení. Všechny deklarace vlastností musí obsahovat na začátku klíčové slovo Public, aby byly vidět zvenku. Jinak je můžeme používat opět pouze z nějaké komponenty.