Základy pozicování

6. díl - Základy pozicování

Tomáš Jecha, MVP, MCSD       23.02.2012       C#, VB.NET, WPF, .NET       18480 zobrazení

Pozicování je velká přednost technologie WPF. Dovoluje připravovat dynamické rozložení prvků s předvídatelným chováním při změně nejen velikosti okna, ale i elementů uvnitř něj. V tomto díle se věnuji základním principům pozicování.

Ú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.

WPF Window 1

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:

WPF Window 2

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í:

WPF Window 3

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ě:

WPF Window

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>

WPF 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:

image

<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:

image

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.

 

hodnocení článku

7 bodů / 9 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

 

 

 

Nový příspěvek

 

Diskuse: TMP Pozicování

Tak tohohle jsem se již snažil dosáhnout, ale nějak jsem na to nepřišel.

"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)"

Napsal by jste, jak to udělat?

FontSize="{Binding ElementName=label1, Path=Content.Lenght .......

Tady už nevím jak dál.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Nechápu, proč nastavujete velikost písma pomocí bindingu. Právě velikost textu by měla být taková, jakou ji chcete.

V tomhle případě stačí vložit text jako prvek TextBlock a tomu nastavit zalamování - TextWrapping="Wrap". Horizontální zarovnání necháte na stretch (takže vyplní celé okno ve své šířce) a vertikální na top (takže se přichytí na horní okraj okna). Automaticky se tak bude výška uzpůsobovat podle obsahu a velikosti okna.

K tomu, aby jste na spodek tohoto textového pole přichytil další prvky bude zapotřebí nějaký layoutovací prvek, například Grid. Tomu se budu věnovat v dalším díle.

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback