Při vývoji WPF aplikací pro Windows Phone či Windows Store se celkem snadno setkávám s OutOfMemoryException. Pokud se snažím zobrazit hodně dat a tuto výjimku je schopné odchytit Visual Studio, je příčina zcela určitě ve velké paměťové náročnosti ovládacích prvků uživatelského rozhraní. Projevuje se to zejména na zařízeních s malou pamětí a s procesorem architektury ARM. V tomto článku ukážu, jak se s tím dá vypořádat.
Vše bude jednou textura
Každý ovládací prvek na platformě Windows Phone nesmí být širší nebo vyšší než 2048 pixelů. Víte, proč tomu tak je? Protože ovládací prvek je ve finále textura a je jedno, jestli se jedná o Image nebo o TextBlock. Textura zabírá poměrně velké množství paměti, protože to je bitmapa. Je to řádově víc, než je velikost obrázku uloženého jako soubor na disku. Jako soubor je totiž obrázek komprimován. Může se jednat o metody jednoduché jako PNG, složité jako například JPEG a velmi komplexní jako JPEG XR. Obrázek se komprimuje hlavně proto, aby byl jeho přenos ze serveru na klienta co možná nejrychlejší. Na úrovni vykreslování na displej je ale všechno bitmapa, protože se dá pracovat jen s touto reprezentací.
V paměti jsou textury všech ovládacích prvků okna
Pojmem okno myslím v tomto článku rodičovský element deklarovaný jazykem XAML. Většinou se jedná o PhoneApplicationPage či LayoutAwarePage. Jazyk XAML se stejně překládá do jazyka C# a nakonec vznikne něco, co je prakticky totožné s tím, co generuje Visual Studio do code behindu pokud navrhujete Windows Forms aplikaci. Ostatně i v případě XAML aplikace se tyto kódy se nachází v adresáři obj/Debug a můžete si je kdykoliv prohlédnout.
Podstatné je, že se v paměti drží všechno, co okno může zobrazit. Slovem může myslím cokoliv, co není vidět, ale uživatel se na to může posunout. Jsou to všechny položky ovládacích prvků jako ListBox, Pivot, nebo GridView. Kdykoliv nastavuji binding na vlastnosti ItemsSource, zamýšlím se nad tím, jestli počet prvků bude řádově v desítkách. Pokud jich může být víc, není myslitelné vytvářet ovládací prvky pro všechny položky. A to je právě to, k čemu je vynikající DataTemplate a proč je tak snadné se dostat do paměťového stresu.
Řešení může být poměrně snadné, ale vždy to záleží na konkrétním případu. Někdy stačí jen nastavovat vlastnost Visibility na hodnotu Collapsed. Proto by správně nadpis této části měl znít: V paměti jsou textury všech ovládacích prvků okna, které mají nastavenou vlastnost Visibiliy na hodnotu Visible.
Pryč s ovládacími prvky
V některých případech však okolnosti vyžadují implementaci vlastního ovládacího prvku. Není třeba se ničeho obávat, většinou to je mnohem rychlejší a nakonec i lepší, než se snažit ohnout nějaký jiný ovládací prvek, aby vypadal tak, jak je zrovna potřeba. V naprosté většině případů stačí vyjít ze třídy UserControl. Přímé vykreslování přes třídu Graphics (což je obal GDI+) jsem potřeboval, jen když jsem si chtěl udělat plynulou animaci ve Windows Forms. Vlastní ovládací prvek tedy stačí udělat tak, aby obsahoval co možná nejmenší počet svých vlastních ovládacích prvků. Aby se zkrátka uživatel nepohyboval po ovládacích prvcích, ale datových kontextech ovládacích prvků.
Obrázek nahoře znázorňuje prvky v paměti při použití běžné techniky. Pro každý DataContext se vytvoří ovládací prvek podle DataTemplate.
Tento obrázek vyjadřuje úsporu paměti oproti minulému případu. Již se nevytváří ovládací prvek pro každý ViewModel, ale jsou jen statické ovládací prvky, kterým se mění jejich DataContext.