Device independent pixels

5. díl - Device independent pixels

Tomáš Jecha, MVP, MCSD       16.02.2012       C#, VB.NET, WPF, .NET       15827 zobrazení

Co je to DPI a proč by vás to mělo zajímat? Jak se aplikují automatické transformace při změně této hodnoty? Článek je prvním pohledem tohoto seriálu do transformací a pozičního systému WPF.

Technologie WPF je plně vektorová. Týká se to jak návrhu uživatelského prostředí, tak samotného vykreslování. To přináší celou řadu výhod a možností, které budu v tomto seriálu postupně rozebírat.

Tento díl se věnuje troše nezbytné teorie o postupu, který zajišťuje WPF aplikacím nezávislost na nastavení hodnoty DPI operačního systému.

Windows Forms a práce s pixely

Pokud pracujete s technologií Windows Forms využíváte k pozicování pixely. Pixel je nejmenší fyzický obrazový bod. V rozlišení 800×600 máte k dispozici 800 pixelů na šířku a 600 na výšku. A tlačítko se šířkou 100 je tak fyzicky široké 100 pixelů.

Pokud máte monitor 17” s rozlišením 1920×1080 bude tlačítko zákonitě vypadat menší, než na rozlišení 1024×768. Ačkoliv je jejich rozměr v pixelech identický. Rozdíl se ještě víc prohlubuje například na mobilních telefonech, které mají hustotu pixelů ještě výrazně větší a naše tlačítko by tam vypadalo velmi malé.

Dots per inch - DPI

Proto existuje údaj dots per inch DPI. Ten určuje, hustotu pixelů. Přesněji kolik pixelů se vejde do délky jednoho palce na našem zobrazovacím zařízení. Čím vyšší rozlišení nebo čím menší monitor, tím menší pixely jsou a tím je zákonitě větší jejich hustota a tím roste i samotný údaj DPI.

V ideálním světě má každý monitor a každé zobrazovací zařízení podle svého rozlišení a rozměrů své specifické DPI, které je nastavené v operačním systému a podle toho aplikace zobrazují uživatelské rozhraní v odpovídají velikosti – například monitor s poloviční velikostí bude zobrazovat uživatelské prostředí v pixelech v dvojnásobných rozměrech, aby mělo fyzickou velikost stejnou jako na monitoru dvakrát větším.

Nežijeme však v ideálním světě a velká část aplikací si s jiným DPI neumí správně poradit. Například právě Windows Forms s DPI pracovat neumí (pracujeme přímo s pixely) a proto bychom museli podle tohoto údaje ručně měnit velikost a pozice všech ovládacích prvků. Z toho důvodu má operační systém tuto hodnotu nastavenou na výchozí, což je 96 DPI (96 pixelů na palec) a neodpovídá tak na většině zobrazovacích zařízení skutečnosti. Změnit ale samozřejmě lze z ovládacích panelů. Já osobně používám 120 DPI (125% proti 96 DPI), protože mám 15.6” notebook s FullHD rozlišením 1920×1080, kde bylo při 96 DPI vše velmi titěrné. Změnu jsem si vykoupil občasným setkáním s aplikacemi, která nastavení nerespektují nebo se zobrazují špatně. Obvykle se to projeví přetékáním objektů mimo formulář a za jiné prvky a podobně.

Protože skutečné DPI většinou nastavené v systému není, ztrácí toto označení smysl a například ve Windows 7 se volba pro změnu zjednodušeně jmenuje “Zmenšit nebo zvětšit text a další položky”, kde na oko nenastavujeme přímo DPI, ale velikost v procentech. Máme na výběr 100% (=96DPI), 125% (=120DPI) a 150% (=144DPI). Poměrově tak lze nastavit velikost ovládacích prvků proti zažitému defaultním DPI.

Nastavení DPI

Device independent pixels

WPF problémy s různě nastaveným DPI řeší velmi elegantně. Používá totiž jako defaultní jednotku k určování pozice a rozměrů takzvané device independent pixels. Jedná se o abstrakci nad skutečnými pixely a spočívá v automatické změně měřítka v závislosti na nastavení DPI na počítači, kde aplikaci spouštíme. Pracuje se tu s předpokladem, že za výchozího 96 DPI je poměr 1 device independent pixel = 1 fyzický pixel. Jinými slovy, pokud aplikaci budete spouštět pouze na počítačích, které mají výchozí nastavení DPI, objekty nastavené na šířku 100 budou mít šířku 100 pixelů. Pokud aplikaci spustíte na počítači s 144 DPI, velikost všech objektů bude poměrově zvětšena o 50% a prvek se šířkou 100 bude mít fyzicky šířku 150 pixelů.

Na následujícím obrázku je tlačítko ze stejné aplikace spuštěné na počítači s 96 DPI (vlevo) a s 192 DPI (vpravo).

Scale

Jak WPF zajistí změnu velikosti?

Transformace obecně

Jak už jsem psal, WPF je plně vektorová technologie. Změnu měřítka lze proto snadno provést pomocí transformací. Těch existuje hned několik. Za zmínku stojí například RotationTransform pro otáčení objektu. Pro změnu měřítka je tu pak ScaleTransform.

Transformace lze nastavit na libovolný objekt a WPF před jeho vykreslením spočítá finální transformační matici, kterou využije. Například pokud budete mít tlačítko zvětšené na čtyřnásobek umístěné v panelu, který vše naopak zmenšuje na čtvrtinu, výsledná transformace se vyruší. Důležitý je fakt, že se prvek vykresluje až s finální transformací která zahrnuje kombinaci všech aplikovaných transformací (například tlačítko na formuláři přijímá transformace celého okna, panelu ve kterém leží i jeho vlastní).

Jako příklad uvádím tento jednoduchý XAML kód. Nastavuji vloženému tlačítku do vlastnosti LayoutTransform instanci třídy ScaleTransform zvětšující tlačítko na 4-násobek původní velikosti:

<Button Width="60" Height="30" Content="VbNet.cz">
    <Button.LayoutTransform>
        <ScaleTransform ScaleX="4" ScaleY="4" />
    </Button.LayoutTransform>
</Button>        

Poznámka: Transformaci lze nastavit krom do vlastností LayoutTransform také do RenderTransform. Ta transformuje pouze vzhled objektu při vykreslování, ale layout nechává nedotčený (při pozicování se počítá s originálními rozměry a pozicí elementu). Této tématice se budu věnovat v jednom z dalších dílů.

Poznámka: Vysvětlení zápisu “…<Button><Button.LayoutTransform>…” naleznete v odstavci “Nastavování vlastností” v článku o XAMLu.

Transformace pro DPI

Změna velikosti prvků podle DPI se provádí automaticky na úrovni kompozice vykreslování a je tak aplikována na veškerý obsah formuláře. Provádí se poměrově proti výchozímu 96 DPI. Například:

  • Pokud je DPI 96, transformace je 1. Tedy žádná změna velikosti.
  • Pokud je DPI 120, transformace je 1,25. Vše se tedy bude vykreslovat o 25% větší.
  • Pokud je DPI 144, transformace je 1,5. Vše se vykresluje o 50% větší.

Opět i zde platí způsob výpočtu finální transformace. Mějme pro příklad formulář, kde je tlačítko zmenšené na 80%. V systému je nastavené DPI 120 a transformace na úrovni kompozice je tak 125%. Vykreslovací jádro vynásobí 80% a 125% a výsledkem je 100% (0,8×1,25=1). Takže objekt v rozměru dejme tomu 60 se vykreslí s finálním rozměrem 60 pixelů.

Možné problémy

Pokud budete psát aplikaci pracující pod různými DPI, můžete se nejčastěji setkat s problémy s bitmapovými obrázky. Pokud jej zvětšíte, zhorší se kvalita a na druhou stranu, pokud jej nezvětšíte, bude rozložení prvků jiné (okolo obrázku bude místo / nevyplní celou požadovanou plochu a podobně). Další problém může nastat při jejich pozicování a možném efektu “rozpíjení”, kdy obrázek má díky nějaké transformaci pozici mezi dvěma fyzickými pixely a výsledek může působit rozmazaně.

Všechny tyto uvedené problém jsou důsledkem špatného použití technologie a většinu z nich je možné poměrně snadno ve WPF řešit. Ale tomu se budu věnovat v samostatném dílu.

Drobná ukázka na závěr

Na závěr přikládám krátký kód, který přidá do formuláře sadu 20ti tlačítek, přičemž každé nastaví jinou transformaci změny měřítka – ScaleTransformation. Po spuštění jsou všechny tlačítka plně funkční.

Scale transformace

Pro vygenerování vložte do hlavní třídy okna tento kód (není potřeba zásah do XAML kódu):

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            // vytvořit panel na umístění tlačítek
            var stackPanel = new StackPanel();
            this.Content = stackPanel;

            // vytvořit tlačítka
            for (int i = 0; i < 20; i++)
            {
                double scale = ((double)i) / 15.0 + 0.5;
                stackPanel.Children.Add(new Button()
                {
                    Width = 120,
                    Height = 30,
                    Content = "VbNet.cz",
                    LayoutTransform = new ScaleTransform(scale,scale)                    
                });
            }
        }
    }

Závěr

Díku vektorovému přístupu je možné připravovat aplikace, které se dynamicky zvětšují a zmenšují podle rozlišení nebo správně reagují na změny hodnoty DPI systému. To otevírá celou řadu nových typů aplikací, které lze s Windows Presentation Foundation realizovat.

Další díl budu věnovat úvodu do vestavěných komponent a pozicování, které je ve WPF vyřešeno naprosto skvěle. Konečně tak nakousnu praktickou stránku věci. Pokud jste tak ještě neučinili, doporučuji se na další díl připravit dřívějším článkem o jazyce XAML.

 

hodnocení článku

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

 

Všechny díly tohoto seriálu

 

Mohlo by vás také zajímat

Jednoduchý scheduler v .NETu

Asi to znáte – máte nějaký složitější systém na zpracování velkého objemu dat a čas od času potřebujete vykovat nějakou automatizovanou údržbu – typicky smazat všechny položky starší než několika dní. Možností, jak toho dosáhnout, je hodně. Snažil jsem se vymyslet něco jednoduchého a efektivního.

ASP MVC - from zero to hero (2), světlo na konci tunelu

Code First initializers a migrace - kompletní přehled

 

 

Nový příspěvek

 

Diskuse: Základy pozicování

Dobrý den,

chtěl bych se zeptat zda je ve WPF nějaká proměná či událost která hlídá jestli je tlačítko myši stisknuté?

Tvořím aplikaci, kterou bych chtěl nastylovat i s horní lištou (tlačítka "konec", "minimalizovat",..).

V případě, že v XAMLu nastavím

ResizeMode="NoResize" 
WindowStyle="None"

odstraní se jakékoli okraje a není možné aplikaci přesunout na jinou pozici na obrazovce.

Pokud někoho napadne jednoduší řešení, něž hlídat jestli je stisknuté tl. myši a jestli se kurzor hýbe, budu rád.

Mimochodem skvělý článek. :)

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

Stačí do události MouseLeftButtonDown nad celým oknem (nebo komponentou, která má sloužit místo, za který můžete okno uchopit) vložit velmi jednoduchý kód:

private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
	DragMove();
}
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Funguje to. Moc děkuji.

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

Diskuse: Základy pozicování

Na mobilních zařízeních (nejen telefonech) se systémem Windows Mobile je rozlišení pevně dáno a měnit se nedá, stejně tak DPI. Takže například na zařízeních s rozlišením 240x320 bude vždy tlačítko přesně tak velké, jak bylo nastaveno v návrháři. Mobilní rozlišení jsou rovněž standardizována, takže se nemůže stát, že se vyskytne nějaké nestandardní.

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

Jedna věc je DPI udávané systémem. Díky transformacím je tu ale naštěstí možnost si takové věci velmi snadno vyřešit přesně tak, jak je v aplikaci potřeba, i když je DPI pevné.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

No já jsem tím chtěl říct to, že tento problém u aplikací pro mobilní zařízení není.

Rozdíl se ještě víc prohlubuje například na mobilních telefonech, které mají hustotu pixelů ještě výrazně větší a naše tlačítko by tam vypadalo velmi malé.

U mobilních aplikací se narozdíl od desktopových uživatelské rozhraní navrhuje vždy pro konkrétní DPI a rozlišení.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Vždyť to, co píšete je úplně mimo obsah článku. Vy jste vytrhl jednu větu z kontextu, ve které vysvětluji, že na měnších dispejích je větší hustota pixelů (což je předmluva k popisu DPI) a začněte do toho motat něco kolem vývoje pro mobilní zařízení.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

které mají hustotu pixelů ještě výrazně větší a naše tlačítko by tam vypadalo velmi malé

Ne nevypadalo by velmi malé, protože by vždy bylo přesně takové, jaké bylo nakresleno v návrháři během vývoje.

Takže buď taháte problém, který je u desktopových aplikací do mobilních aplikací, kde není, nebo máte problém s chápáním psaného textu.

Článek nekomentuji proto, abych rozpoutal konflikt, ale aby si (potenciální) mobilní vývojáři nemysleli, že to samé musí řešit i u mobilních aplikací.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Jaký problém? Jaký návrhář? Vzpamatujte se a přečtěte si znovu, co jsem v odstavci napsal. Tlačítko o šířce 100 pixelů vypadá menší na displejích s větší hustotou pixelů, což jsou například displeje mobilních telefonů. Nic o vývoji, ani návrháři. Je to předmluva k DPI, což nemá s vývojem nic společného.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

A dost. Rozumná debata možná není. Vaše podobné urážlivé příspěvky budu mazat, porušují pravidla a stojí mě zbytečný čas. Pokud chcete nesmyslně slovíčkařit o věcech, které jsem ani nenapsal, dělejte to na svém blogu, na svém webu, kde na to máte svaté právo.

nahlásit spamnahlásit spam 2 / 8 odpovědětodpovědět

Pán Linhart, jedna vec je, že mobilné aplikácie sa navrhujú na konkrétne rozlíšenie a sú mu plne prispôsobené. Tu ale ide skôr o to, že teraz príklad, ak by sa Vám podarilo spustiť niektorú verziu Windowsu pre počítače na mobilnom telefóne, nebude predsa všetko menšíe? Alebo pôjdete na stránku s Silverlight aplikáciou, a dáte si naraz zobraziť celú stránku, tiež to bude menšie, ako na počítači, nie?

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

Proč by se měla spouštět desktopová aplikace na mobilním zařízení?! Kromě toho, že to nejde, tak je nesmysl i portovat ho z desktopové verze, což jsem zmiňoval ve svém článku. Pokud to skutečně bylo myšleno jako příklad, tak je to mimořádně hloupý a nevhodný příklad.

Na Silverlight ve Windows Mobile zapomeňte, není tam podporován.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ja som sa pokúsil vyjadriť, ako vnímam názor p. Jechu. Nejde tu o kompatibilitu desktopu a mobilov, skôr tu ide o podstatu DIP. Zaujímalo by ma, ako bude fungovať DIP na Windows 8 na tabletoch a desktopových PC ?

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

Stejně jako v jakémkoliv jiném Windows, XP nebo novějším. Není to záležitost systému, ale WPF.

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

Pýtal som sa, či to bude správne zmenšovať grafiku, zvlášť text. Či to bude robiť problémy, aké p. Jecha popisoval v komentároch, alebo sa zachová ako na desktope, a zobrazí všetko správne.

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

Vy vůbec nechápete o čem je řeč, jinak byste nemohl mít podobné dotazy.

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

DPI je údaj který poskytuje systém.

To, jak se k této hodnotě zahová aplikace je čiště na použité technologie a aplikace.

A zrovna WPF provede popisovanou transformaci velikosti zcela automaticky.

A například WinForms s touto hodnotou neudělá nic. Ale vzhledem k tomu, že využívá komponenty ze systému, které změnu velikosti respektují, setká se spolu aplikace, co změnu DPI ignoruje s komponentou, která změnu reflektuje a mohou nastat popsané problémy.

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

Diskuse: Základy pozicování

Je možné nejako donútiť, aby tlačítko vyplnilo napr. 15% šírky okna a 8% jeho výšky, nech už je okno akokoľvek veľké? A potom podobne aj text tlačítka, aby sa zachoval konštantný pomer medzi veľkosťou tlačítka a textu na ňom.

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

Možné to je. Lze to řešit kombinací komponent ViewBox a Grid. Budu se tomu věnovat va dalších dílech.

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

Diskuse: Základy pozicování

Skvělý článek a jedna z věcí, proč jsem se rozhodl naučim WPF :-) Díky!!

nahlásit spamnahlásit spam 1 / 1 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