Úvod
Určitě jste se setkali už s řadou typů pozicování. Ať už to bylo Windows Forms nebo HTML+CSS. V každé z těchto technologií je potřeba uvažovat trochu jinak a to není výjimkou ani u Windows Presentation Foundation. U jmenovaných technologií nejdříve trochu rozeberu jejich nedostatky, které oproti WPF vidím.
Windows Forms slouží k tvorbě uživatelského rozhraní – konkrétně formulářů. Výhodou je, že pozicování je zde velmi jednoduché a předvídatelné. Komponenty mají pevnou pozici (za předpokladu, že nepoužijete nějaký z layoutovacích panelů) a jediné, co jim můžete nastavovat je způsob přichycení k okrajům formuláře nebo dokování. Jako hlavní nevýhodu vidím absenci pozicování na základě dynamické velikosti prvků. Například zvětšení textu nezvětší tlačítko, na kterém je umístěný. To je také důvod, proč si aplikace ve WinForms neumí poradit se změnou DPI (viz minulý díl). Další nevýhodou je podle mého názoru možnost navrhovat prostředí pouze vizuálním návrhářem. Tohle může být subjektivní, ale návrh komplexního pozicování je z vizuálního návrháře často velmi obtížné a v některých případech dokonce nemožné. Můžete například pracovat s komponentou, která zatím nemá vzhled nebo chcete nastavit chování v různých stavech a vizuálně tak není možné výsledek vizualizovat pro všechny stavy.
HTML+CSS je technologie z pekel. Postupným vývojem přetvořena z jednoduchého jazyka pro formátování dokumentů na nástroj plný nelogických pravidel, výjimek a chybějících prvků – a to především pro pozicování. Ano, nemám ji rád. Možná se mnou nebudete souhlasit, ale k dosažení byť jednoduchých věcí je potřeba v této technologii naprosto nesmyslných hacků a berliček. Nemohou za to samotné HTML a CSS, které jsou řadu let znásilňované k zobrazování komplikovaných uživatelských prostředí, ačkoliv to není jejich původní smysl. A navíc musí pracovat v prohlížečích s bohatou fantazií, co se výkladu standardů týče. Abych to shrnul, tato technologie je bez potřebných zkušeností velmi nepředvídatelná, nepříliš dobře udržovatelná a nenabízí snadnou formu pro přehledné pozicování.
Dynamické pozicování
Hlavním rozdílem WPF proti jiným pozicovacím technologiím je fakt, že rozměry jsou většinou dynamické a přitom je výsledek při dostatečných znalostech dobře předvídatelný i ve složitějších konstrukcích. Ve většině běžných aplikací totiž zjistíte, že vlastně s pevnými rozměry pracovat nemusíte a ani nechcete a na formuláři je většina prvků pozicována dynamicky.
Příkladem může být velmi jednoduchá aplikace (viz obrázek) s nadpisem, seznamem a jedním tlačítkem.
Pokud by taková aplikace měla veškeré pozicování nastavená pevně, byl by problém i při menších změnách. Na dalším obrázku je vidět ta samá aplikace, pouze text popisku je delší a tlačítko má větší písmo:
Jak vidíte, díky pevnému pozicování bude potřeba změnit velikosti elementů, aby text nepřetékal z aplikace ven a tlačítko se zobrazilo celé. Klasicky je toto problém při lokalizaci, kde nemůžeme dopředu počítat s délkou textu, protože se bude po lokalizaci do jiných jazyků měnit. A samozřejmě i při běžných úpravách v aplikaci je jednodušší pracovat s co nejméně pevných rozměrů a pozic.
Podívejme se, jak by se aplikace měla zachovat správně při dynamickém pozicování:
Správného chování jsem dosáhnul tím, že výška textu popisku není pevná (mění se podle délky textu, aby se vždy vešel), není definována výška seznamu (vyplňuje místo mezi popiskem a spodní části pro tlačítko) a není pevně definována ani výška spodní části, kde se nachází tlačítko (opět dynamická velikost podle velikosti a množství textu na tlačítku). Postupně budu vysvětlovat, jak tohoto dynamického chování dosáhnout.
Úvod do dynamického pozicování WPF
U vizuálních komponent si můžete vybrat, jak se bude prvek chovat při změně velikosti rodičovského elementu (například maximalizace okna) a především při změně velikosti svého obsahu (vložení delšího textu do nadpisu, jiná velikost písma a podobně).
Nyní jednotlivé základní způsoby chování proberu a následně ukážu na příkladech.
Velikost dle obsahu prvku
Elementy mají vlastnosti Width (šířka) a Height (výška). Nastavit můžete 2 druhy hodnot:
- Číselnou hodnotu v device independent pixels (viz minulý díl) – určuje pevnou šířku nebo výšku.
- Hodnotu “Auto” – velikost se nastaví automaticky podle velikosti obsahu elementu nebo nastavení zarovnání (na “Auto” je defaultně nastavená šířka i výška, pokud vlastnost nezmění na jinou hodnotu)
Velikost a umístění dle rodičovského elementu
Dále nastavujeme, jak se bude prvek chovat vzhledem k rodičovskému elementu. Zde jsou hlavními vlastnostmi HorizontalAlignment (horizontální zarovnání) a VerticalAlignment (vertikální zarovnání). Můžeme jim nastavit jednu z těchto hodnot:
- Left (jen u horizontálního zarovnání) – prvek je zarovnán k levému okraji rodičovského elementu
- Right (jen u horizontálního zarovnání) – prvek je zarovnán k pravému okraji rodičovského elementu
- Top (jen u vertikálního zarovnání) – prvek je zarovnán k hornímu okraji rodičovského elementu
- Bottom (jen u vertikálního zarovnání) – prvek je zarovnán ke spodnímu okraji rodičovského elementu
- Center – prvek je zarovnán na střed
- Stretch – prvek bude roztažen na celou šířku/výšku rodičovského elementu (defaultní hodnota)
Kombinace chování
Pravá síla pozicování WPF je v kombinování nastavení těchto vlastností s několika základními pozicovacími prvky. Různé stavy a kombinace vysvětlím na několika příkladech.
Na testování si založte nový projekt typu WPF Application.
Příklad 1 – defaultní hodnoty
Jako první mějme kód, kde je uvnitř okna aplikace panel Grid, uvnitř kterého je jedno tlačítko Button:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="250" Width="300" Background="{StaticResource {x:Static SystemColors.ControlLightBrushKey}}">
<Grid>
<Button Content="Trochu delší text v tlačítku" />
</Grid>
</Window>
Výsledek bude vypadat následovně:
Poznámka: Při zakládání nového okna si můžete všimnout, že návrhář Visual Studia vkládá do nových WPF oken element Grid. Je to z toho důvodu, že okno aplikace (třída Window) může v sobě nést pouze jeden element. Proto je v základní šabloně Visual Studia pro nové WPF okno umístěn uvnitř prvek Grid, který již v sobě dokáže nést libovolné množství dalších elementů. Není však podmínka Grid používat. Funkcím Gridu se věnuji v dalším dílu seriálu. Ale v tuto chvíli nám bude sloužit jako primitivní panel bez vhledu, do kterého můžeme vkládat neomezené množství dalších elementů.
Po spuštění je tlačítko umístěné přes celý formulář. Může za to již zmiňované nastavení zarovnání HorizontalAlignment a VerticalAlignment. Ty jsou defaultně nastavené na Stretch, což znamená roztažení na velikost rodičovského prvku. Takže element Grid se roztáhne na velikost celého rodičovského okna a to samé udělá i tlačítko Button, které se roztáhne na velikost prvku Grid a tím i na velikost celého okna.
Důležité je zmínit, jak se zachovala šířka a výška elementu. Ta je nastavena na defaultní hodnotu Auto, což v případě použití zarovnání Stretch znamená, že se šířka a výška podřídí a prvek bude roztažen na rozměry rodičovského prvku.
Příklad 2 – změna zarovnání tlačítka v rodičovském elementu
Pokud chceme, aby bylo tlačítko zobrazeno v automatické velikosti a neroztahovalo se na celý formulář, změňme zarovnání na jednu z jiných hodnot. Na tomto příkladu zarovnávám vertikálně dolů a horizontálně na střed.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="250" Width="300" Background="{StaticResource {x:Static SystemColors.ControlLightBrushKey}}">
<Grid>
<Button Content="Trochu delší text v tlačítku" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
</Grid>
</Window>
Přehled chování
V této tabulce srovnávám jednotlivé možnosti nastavení a způsob, jak se budou ovlivňovat:
Nastavení zarovnání
|
Nastavení rozměrů
|
Výsledek
|
HorizontalAlignment="Stretch"
(default)
|
Width="Auto"
(default)
|
Element se roztáhne na šířku přes celého rodiče.
|
HorizontalAlignment="Left"
|
Width="Auto"
(default)
|
Element bude přichycen k levému okraji rodiče, šířka se mění automaticky podle obsahu.
|
HorizontalAlignment="Center"
|
Width="Auto"
(default)
|
Element na horizontálním středu elementu rodiče, šířka se mění automaticky podle obsahu.
|
HorizontalAlignment="Right"
|
Width="Auto"
(default)
|
Element bude přichycen k pravému okraji rodiče, šířka se mění automaticky podle obsahu.
|
HorizontalAlignment="Stretch"
(default)
|
Width="100"
|
Nelogický stav (nastaveno roztažení na šířku a zároveň pevná šířka).
|
HorizontalAlignment="Left"
|
Width="100"
|
Element bude přichycen k levému okraji rodiče, šířka je pevně 100.
|
HorizontalAlignment="Center"
|
Width="100"
|
Element na horizontálním středu elementu rodiče, šířka je pevně 100.
|
HorizontalAlignment="Right"
|
Width="100"
|
Element bude přichycen k pravému okraji rodiče, šířka je pevně 100.
|
VerticalAlignment="Stretch"
(default)
|
Height="Auto"
(default)
|
Element se roztáhne na výšku přes celého rodiče.
|
VerticalAlignment="Top"
|
Height="Auto"
(default)
|
Element bude přichycen k hornímu okraji rodiče, výška se mění automaticky podle obsahu.
|
VerticalAlignment="Center"
|
Height="Auto"
(default)
|
Element na vertikálním středu elementu rodiče, výška se mění automaticky podle obsahu.
|
VerticalAlignment="Bottom"
|
Height="Auto"
(default)
|
Element bude přichycen k spodnímu okraji rodiče, výška se mění automaticky podle obsahu.
|
VerticalAlignment="Stretch"
(default)
|
Height="100”
|
Nelogický stav (nastaveno roztažení na výšku a zároveň pevná výška).
|
VerticalAlignment="Top"
|
Height="100”
|
Element bude přichycen k hornímu okraji rodiče, výška je pevně 100.
|
VerticalAlignment="Center"
|
Height="100”
|
Element na vertikálním středu elementu rodiče, výška je pevně 100.
|
VerticalAlignment="Bottom"
|
Height="100”
|
Element bude přichycen k spodnímu okraji rodiče, výška je pevně 100.
|
Doporučuji si toto chování vyzkoušet, protože bude pro pochopení dalších kapitol důležité.
Margin a Padding
U elementů lze nastavovat dvojici vlastností Margin a Padding. Ty definují vnější okraje a vnitřní výplň. Většinou jsou tyto hodnoty defaultně nastaveny na “0,0,0,0”.
Obě tyto vlastnosti jsou typu Thickness (tloušťka). Ten dokáže nést 4 hodnoty – levá, horní, pravá a spodní tloušťka. Při nastavování v XAML kódu můžeme zapsat hodnotu 3mi způsoby:
- “0,1,2,3” – nastavujeme postupně všechny vlastnosti - levou tloušťku na 0, horní 1, pravou 2 a spodní na 3
- “0,1” – nastavujeme levou a pravou tloušťku na 0 a horní a spodní na 1
- “0” – nastavujeme všechny tloušťky na 0
Poznámka: Pořadí stran si lze zapamatovat snadno - začíná se u hodnoty pro levou tloušťku a pokračuje se po směru hodinových ručiček.
Margin – vnější okraje
Margin určuje vnější okraje elementu. Tedy kolik má mít prvek kolem sebe místa. To lze dobře demonstrovat na případu, kdy nechceme, aby bylo tlačítko nalepené přímo na hraně formuláře. Na obrázku je vlevo varianta bez okrajů a vpravo s okraji Margin:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="150" Width="300" Background="{StaticResource {x:Static SystemColors.ControlLightBrushKey}}">
<Grid>
<Button Content="Tlačítko" Margin="8" VerticalAlignment="Bottom" />
</Grid>
</Window>
Vlastnost Margin je narozdíl od Padding definována na úrovní abstraktní třídy FrameworkElement je proto dostupná téměř pro všechny ovládací prvky.
Padding – vnitřní výplň
Padding znamená v překladu “výplň”. Reprezentuje tloušťku výplně mezi okrajem elementu a jeho obsahem. Na následujícím obrázku je vidět, jak vypadá padding nastavovaný pro tlačítka postupně s hodnotou 0 až 8. Vidíme, jak se prostor mezi okrajem tlačítka je obsahem postupně rozšiřuje:
Vlastnost Padding je definována v třídě Control, ze které nedědí všechny ovládací a proto není u některých prvků dostupná. Více jsem o architektuře psal v předchozím článku.
Poznámka: Velmi často se také hodí nastavování Margin nebo Padding na rodičovské elementy. V našem případě lze nastavit komponentě Grid vlastnost Margin a tím odsadit celý obsah formuláře.
Závěr
Pochopení problematiky pozicování je pro práci s WPF naprosto stěžejní a dokáže ulehčit znatelné množství práce. Toto je ale jen úvod, na který budu navazovat v dalších dílech.