Sdílet prostřednictvím


Optimalizujte svůj XAML kód

Analýza kódu XAML za účelem vytvoření objektů v paměti je časově náročná pro komplexní uživatelské rozhraní. Tady je několik věcí, které můžete udělat, abyste zlepšili dobu analýzy a načtení kódu XAML a efektivity paměti vaší aplikace.

Při spuštění aplikace omezte kód XAML načtený jenom na to, co potřebujete pro počáteční uživatelské rozhraní. Prozkoumejte kód na úvodní stránce (včetně prostředků stránky) a ověřte, že nenačítáte další prvky, které nejsou potřeba hned. Tyto prvky mohou pocházet z různých zdrojů, jako jsou slovníky zdrojů, prvky, které jsou původně sklopené, a prvky nakreslené nad jinými prvky.

Optimalizace XAML pro efektivitu vyžaduje kompromisy; neexistuje vždy jediné řešení pro každou situaci. Tady se podíváme na některé běžné problémy a poskytneme pokyny, které můžete použít k tomu, abyste pro svou aplikaci udělali správné kompromisy.

Minimalizovat počet prvků

I když je platforma XAML schopná zobrazit velký počet prvků, můžete aplikaci rozložit a vykreslit rychleji pomocí nejmenšího počtu prvků k dosažení požadovaných vizuálů.

Volby, které uděláte při rozložení ovládacích prvků uživatelského rozhraní, ovlivní počet prvků uživatelského rozhraní, které se vytvoří při spuštění aplikace. Podrobnější informace o optimalizaci rozložení najdete v tématu Optimalizace rozložení XAML.

Počet elementů je velmi důležitý v šablonách dat, protože každý prvek se pro každou položku dat vytvoří znovu. Informace o snížení počtu prvků v seznamu nebo mřížce najdete v článku Snížení počtu prvků na položku v článku Optimalizace uživatelského rozhraní ListView a GridView.

Tady se podíváme na některé další způsoby, jak snížit počet prvků, které musí vaše aplikace načíst při spuštění.

Odložit vytvoření položky

Pokud kód XAML obsahuje prvky, které se nezobrazují hned, můžete odložit načítání těchto prvků, dokud se nezobrazí. Můžete například zpozdit vytváření neviditelného obsahu, jako je sekundární záložka v uživatelském rozhraní podobném panelu záložek. Nebo můžete ve výchozím nastavení zobrazit položky v zobrazení mřížky, ale poskytnout uživateli možnost zobrazit data v seznamu. Načtení seznamu můžete zpozdit, dokud nebude potřeba.

Pomocí atributu x:Load místo vlastnosti Visibility můžete určit, kdy je prvek zobrazen. Když je viditelnost prvku nastavena na Sbalené, pak je přeskočen během fáze vykreslování, ale stále platíte náklady na instanci objektu v paměti. Pokud místo toho použijete x:Load, architektura nevytvoří instanci objektu, dokud ji nepotřebujete, takže náklady na paměť jsou ještě nižší. Nevýhodou je, že platíte malou režii paměti (přibližně 600 bajtů), když uživatelské rozhraní není načteno.

Poznámka:

Načítání prvků můžete zpozdit pomocí atributu x:Load nebo x:DeferLoadStrategy. Atribut x:Load je k dispozici od windows 10 Creators Update (verze 1703, build sady SDK 15063). Minimální verze cílená projektem sady Visual Studio musí být Windows 10 Creators Update (10.0, build 15063), aby bylo možné použít x:Load. Pokud chcete cílit na starší verze, použijte x:DeferLoadStrategy.

Následující příklady ukazují rozdíl v počtu prvků a využití paměti, pokud se k skrytí prvků uživatelského rozhraní používají různé techniky. Objekt ListView a GridView obsahující stejné položky jsou umístěny v kořenové mřížce stránky. Objekt ListView není viditelný, ale objekt GridView se zobrazí. Xaml v každém z těchto příkladů vytvoří stejné uživatelské rozhraní na obrazovce. Ke kontrole počtu prvků a využití paměti používáme nástroje sady Visual Studio pro profilaci a výkon.

Možnost 1 – neefektivní

Zde se načte ListView, ale není viditelný, protože je šířka 0. ListView a každý z jeho podřízených elementů je vytvořen ve vizuálním stromu a načten do paměti.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE.-->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <ListView x:Name="List1" Width="0">
        <ListViewItem>Item 1</ListViewItem>
        <ListViewItem>Item 2</ListViewItem>
        <ListViewItem>Item 3</ListViewItem>
        <ListViewItem>Item 4</ListViewItem>
        <ListViewItem>Item 5</ListViewItem>
        <ListViewItem>Item 6</ListViewItem>
        <ListViewItem>Item 7</ListViewItem>
        <ListViewItem>Item 8</ListViewItem>
        <ListViewItem>Item 9</ListViewItem>
        <ListViewItem>Item 10</ListViewItem>
    </ListView>

    <GridView x:Name="Grid1">
        <GridViewItem>Item 1</GridViewItem>
        <GridViewItem>Item 2</GridViewItem>
        <GridViewItem>Item 3</GridViewItem>
        <GridViewItem>Item 4</GridViewItem>
        <GridViewItem>Item 5</GridViewItem>
        <GridViewItem>Item 6</GridViewItem>
        <GridViewItem>Item 7</GridViewItem>
        <GridViewItem>Item 8</GridViewItem>
        <GridViewItem>Item 9</GridViewItem>
        <GridViewItem>Item 10</GridViewItem>
    </GridView>
</Grid>

Živý vizuální strom se načteným objektem ListView Celkový počet prvků pro stránku je 89.

Snímek obrazovky s vizuálním stromem zobrazeným jako seznam

ListView a jeho potomci jsou načteny do paměti.

snímek obrazovky s tabulkou Managed Memory Test App 1 tečkou E X E zobrazující ListView a její podřízené položky se načtou do paměti.

Možnost 2 – lepší

Tady je viditelnost ListView nastavena na skryté (druhý XAML je identický s původním). ListView je vytvořen ve vizuálním stromu, ale jeho podřízené prvky nejsou. Načítají se však do paměti, takže použití paměti je stejné jako v předchozím příkladu.

<ListView x:Name="List1" Visibility="Collapsed">

Dynamický vizuální strom se sbaleným Objektem ListView Celkový počet prvků pro stránku je 46.

Snímek obrazovky stromu vizuálních prvků se sbaleným zobrazením seznamu

ListView a jeho potomci jsou načteny do paměti.

Aktualizovaný snímek obrazovky tabulky aplikace Managed Memory Test App 1.exe ukazující, že ListView a jeho podřízené položky jsou načteny do paměti.

Možnost 3 – nejúčinnější

ListView má atribut x:Load nastavený na false (druhý XAML je stejný jako původní). ListView není vytvořen ve vizuálním stromě ani uložen do paměti při spuštění.

<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">

Živý vizuální strom s objektem ListView nebyl načten. Celkový počet prvků stránky je 45.

vizuální strom se zobrazením seznamu není načteno

Objekt ListView a jeho podřízené položky nejsou načteny do paměti.

vizuální strom se zobrazením seznamu

Poznámka:

Počty prvků a použití paměti v těchto příkladech jsou velmi malé a zobrazují se pouze k předvedení konceptu. V těchto příkladech je režie při použití x:Load větší než úspora paměti, takže by aplikace neměla prospěch. Nástroje pro profilaci v aplikaci byste měli použít k určení, jestli bude vaše aplikace těžit z odloženého načítání.

Použití vlastností panelu rozložení

Panely rozložení mají vlastnost Pozadí, takže před panel není potřeba umístit obdélník , aby se obarvil.

neefektivní

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
    <Rectangle Fill="Black"/>
</Grid>

efektivní

<Grid Background="Black"/>

Panely rozložení mají také předdefinované vlastnosti ohraničení, takže nemusíte kolem panelu rozložení umístit prvek Border. Další informace a příklady najdete v tématu Optimalizace rozložení XAML.

Použití obrázků místo vektorových prvků

Pokud použijete stejný vektorový prvek dostatečně dlouho, bude efektivnější místo toho použít prvek Image. Vektorové prvky mohou být dražší, protože procesor musí vytvořit každý jednotlivý prvek samostatně. Soubor obrázku musí být dekódován pouze jednou.

Optimalizace prostředků a slovníků prostředků

Slovníky prostředků obvykle používáte k ukládání zdrojů na poněkud globální úrovni, na které chcete odkazovat na více místech ve své aplikaci. Například styly, štětce, šablony atd.

Obecně jsme optimalizovali ResourceDictionary tak, aby nevytvářel instance prostředků, pokud o ně není požádáno. Měli byste se vyhnout situacím, kdy by se prostředky zbytečně instanciovaly.

Prostředky s x:Name

Pomocí atributu x:Key odkazujte na své prostředky. Jakýkoli prostředek s atributem x:Name neprospěje z optimalizace platformy; místo toho je vytvořen jako instance, jakmile je vytvořen ResourceDictionary. K tomu dochází, protože x:Name platformě říká, že vaše aplikace potřebuje přístup na úrovni pole k tomuto prostředku, takže platforma musí vytvořit něco, na co bude možné se odkazovat.

ResourceDictionary v uživatelském ovládacím prvku

ResourceDictionary definovaný uvnitř UserControl má nevýhodu. Platforma vytvoří kopii takového ResourceDictionary pro každou instanci UserControl. Pokud máte UserControl, který se používá hodně, přesuňte ResourceDictionary z UserControl a umístěte ho na úroveň stránky.

Rozsah prostředků a slovníku prostředků

Pokud stránka odkazuje na uživatelský ovládací prvek nebo prostředek definovaný v jiném souboru, architektura tento soubor analyzuje také.

Protože InitialPage.xaml používá jeden prostředek z ExampleResourceDictionary.xaml, musí být při spuštění analyzován celý ExampleResourceDictionary.xaml.

InitialPage.xaml.

<Page x:Class="ExampleNamespace.InitialPage" ...>
    <Page.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="ExampleResourceDictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>

    <Grid>
        <TextBox Foreground="{StaticResource TextBrush}"/>
    </Grid>
</Page>

ExampleResourceDictionary.xaml.

<ResourceDictionary>
    <SolidColorBrush x:Key="TextBrush" Color="#FF3F42CC"/>

    <!--This ResourceDictionary contains many other resources that
        are used in the app, but are not needed during startup.-->
</ResourceDictionary>

Pokud prostředek používáte na mnoha stránkách v celé aplikaci, je vhodné ho uložit do App.xaml a vyhnout se duplicitám. Ale App.xaml se analyzuje při spuštění aplikace, takže všechny prostředky, které se používají pouze na jedné stránce (pokud tato stránka není počáteční stránkou), by se měly vložit do místních prostředků stránky. Tento příklad ukazuje App.xaml obsahující prostředky, které jsou využívány jenom jednou stránkou (což není počáteční stránka). To zbytečně zvyšuje dobu spuštění aplikace.

app.xaml

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Application ...>
     <Application.Resources>
        <SolidColorBrush x:Key="DefaultAppTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="InitialPageTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="SecondPageTextBrush" Color="#FF3F42CC"/>
        <SolidColorBrush x:Key="ThirdPageTextBrush" Color="#FF3F42CC"/>
    </Application.Resources>
</Application>

InitialPage.xaml.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.InitialPage" ...>
    <StackPanel>
        <TextBox Foreground="{StaticResource InitialPageTextBrush}"/>
    </StackPanel>
</Page>

SecondPage.xaml.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.SecondPage" ...>
    <StackPanel>
        <Button Content="Submit" Foreground="{StaticResource SecondPageTextBrush}"/>
    </StackPanel>
</Page>

Pokud chcete tento příklad zefektivnit, přesuňte SecondPageTextBrush do SecondPage.xaml a přesuňte ThirdPageTextBrush do thirdPage.xaml. InitialPageTextBrush mohou zůstat v App.xaml, protože prostředky aplikace se musí analyzovat při každém spuštění aplikace.

Sloučete více štětců, které vypadají stejně, do jednoho zdroje.

Platforma XAML se snaží ukládat běžně používané objekty do mezipaměti, aby je bylo možné co nejčastěji používat. XAML ale nedokáže snadno zjistit, jestli je štětec deklarovaný v jedné části kódu stejný jako štětec deklarovaný v jiné. Příklad zde používá SolidColorBrush k demonstraci, ale případ je pravděpodobnější a důležitější u GradientBrush. Zkontrolujte také štětce, které používají předdefinované barvy; Například "Orange" a "#FFFFA500" mají stejnou barvu.

neefektivní.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page ... >
    <StackPanel>
        <TextBlock>
            <TextBlock.Foreground>
                <SolidColorBrush Color="#FFFFA500"/>
            </TextBlock.Foreground>
        </TextBlock>
        <Button Content="Submit">
            <Button.Foreground>
                <SolidColorBrush Color="#FFFFA500"/>
            </Button.Foreground>
        </Button>
    </StackPanel>
</Page>

Pokud chcete duplikaci opravit, definujte štětec jako prostředek. Pokud ovládací prvky na jiných stránkách používají stejný štětec, přesuňte ho do App.xaml.

Efektivní.

<Page ... >
    <Page.Resources>
        <SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
    </Page.Resources>

    <StackPanel>
        <TextBlock Foreground="{StaticResource BrandBrush}" />
        <Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
    </StackPanel>
</Page>

Minimalizace překreslení

Překreslení nastane, když je nakreslený více než jeden objekt ve stejných pixelech obrazovky. Všimněte si, že mezi těmito pokyny a přáním minimalizovat počet prvků je někdy kompromis.

Jako vizuální diagnostický nástroj použijte DebugSettings.IsOverdrawHeatMapEnabled. Můžete najít nakreslené objekty, o kterých jste nevěděli, že jsou ve scéně.

Průhledné nebo skryté prvky

Pokud prvek není viditelný, protože je průhledný nebo skrytý za jinými prvky a nepřispívají k rozložení, odstraňte ho. Pokud prvek není viditelný v počátečním vizuálním stavu, ale je viditelný v jiných stavech vizuálu, použijte x:Load k řízení jeho stavu nebo nastavte Viditelnost na Sbalené na samotném prvku a změňte hodnotu na Visible v příslušných stavech. K tomuto heuristickému pravidlu budou existovat výjimky: obecně platí, že hodnota, kterou vlastnost má ve většině vizuálních stavů, je nejlepší nastavit místně na prvku.

Složené prvky

K vytvoření efektu použijte složený prvek místo vrstvení více prvků. V tomto příkladu je výsledkem dvoubarevný tvar, kde je horní polovina černá (z pozadí mřížky ) a dolní polovina je šedá (z poloprůhledné bílé obdélník smíchaný s alfasložkou přes černé pozadí mřížky ). Zde je vyplněno 150% pixelů potřebných k dosažení výsledku.

neefektivní.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Black">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Rectangle Grid.Row="1" Fill="White" Opacity=".5"/>
</Grid>

Efektivní.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Rectangle Fill="Black"/>
    <Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>

Rozvrhové panely

Panel rozložení může mít dva účely: vybarvení oblasti a uspořádání podřízených prvků. Pokud prvek dále vzadu v pořadí ve vrstvě již zbarvuje oblast, pak rozvržení panelu před ním nemusí tuto oblast malovat: místo toho se může soustředit jen na rozvržení svých podřízených objektů. Tady je příklad.

neefektivní.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Background="Blue"/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

Efektivní.

<GridView Background="Blue">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

Pokud musí být mřížka testovatelná, nastavte na ní hodnotu pozadí průhlednost.

Hranice

K nakreslení ohraničení kolem objektu použijte prvek Border. V tomto příkladu se mřížky používá jako TextBox. Ale všechny pixely ve středové buňce jsou překresleny.

neefektivní.

<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Blue" Width="300" Height="45">
    <Grid.RowDefinitions>
        <RowDefinition Height="5"/>
        <RowDefinition/>
        <RowDefinition Height="5"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="5"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="5"/>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Row="1" Grid.Column="1"></TextBox>
</Grid>

Efektivní.

<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
    <TextBox/>
</Border>

Okraje

Mějte na paměti okraje. Dva sousední prvky se překrývají (pravděpodobně omylem), pokud se záporné okraje rozšíří do vykreslovacího ohraničení jiného a způsobí překreslení.

Ukládání statického obsahu do mezipaměti

Dalším zdrojem překreslení je obrazec vytvořený z mnoha překrývajících se prvků. Pokud nastavíte CacheMode na BitmapCache na UIElement, který obsahuje složený obrazec, pak platforma vykresluje prvek na rastrový obrázek jednou a pak použije tento rastrový obrázek každý rámeček místo překreslení.

neefektivní.

<Canvas Background="White">
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>

Vennův diagram se třemi plnými kruhy

Výše uvedený obrázek je výsledkem, ale tady je mapa překreslených oblastí. Tmavší červená označuje vyšší množství překreslení.

Vennův diagram znázorňující překrývající se oblasti

Efektivní.

<Canvas Background="White" CacheMode="BitmapCache">
    <Ellipse Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
    <Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>

Všimněte si použití CacheMode. Tuto techniku nepoužívejte, pokud se některý z dílčích obrazců animuje, protože mezipaměť rastrového obrázku se pravděpodobně bude muset generovat znovu každý snímek, což by bylo kontraproduktivní.

Použijte XBF2

XBF2 je binární reprezentace kódu XAML, která zabraňuje všem nákladům na analýzu textu za běhu. Také optimalizuje binární soubor pro načítání a vytváření stromů a umožňuje "rychlou cestu" u typů XAML ke snížení nákladů na haldu a tvorbu objektů, například VSM, ResourceDictionary, styly atd. Je zcela namapovaný do paměti, takže pro načtení a čtení stránky XAML neexistuje žádná paměťová stopa. Kromě toho snižuje nároky na disky uložených stránek XAML v appxu. XBF2 je kompaktnější reprezentace a může snížit nároky na disky srovnávacích souborů XAML/XBF1 až o 50%. Například integrovaná aplikace Fotky viděla přibližně 60% snížení po převodu na XBF2 z přibližně 1mb prostředků XBF1 na přibližně 400 kb prostředků XBF2. Viděli jsme také, že aplikace mají výhody od 15 do 20% v procesoru a 10 až 15% v haldě Win32.

Vestavěné ovládací prvky a slovníky XAML, které rámec poskytuje, jsou již plně s podporou XBF2. Pro vlastní aplikaci se ujistěte, že soubor projektu deklaruje TargetPlatformVersion 8.2 nebo novější.

Pokud chcete zkontrolovat, jestli máte XBF2, otevřete aplikaci v binárním editoru; 12. a 13. bajty jsou 00 02, pokud máte XBF2.