Optimalizace načítání XAML pro WinUI a Windows App SDK

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čítání kódu XAML a efektivitu paměti aplikace WinUI.

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 můžou pocházet z různých zdrojů, jako jsou slovníky zdrojů, prvky, které jsou původně sbalené, 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 udělali správné kompromisy pro vaši aplikaci WinUI.

Minimalizovat počet prvků

I když platforma XAML dokáže zobrazit velký počet prvků, můžete aplikaci rozložit a vykreslit rychleji pomocí nejmenšího počtu prvků potřebných 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 naleznete v sekci Snížení počtu prvků na položku v článku Optimalizace výkonu ListView a GridView pro WinUI.

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, například sekundární karty v uživatelském rozhraní podobném tabulátoru. 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.

K řízení, kdy se zobrazí prvek, použijte atribut x:Load místo vlastnosti Visibility . Když je viditelnost elementu nastavená na Skryté, přeskočí se během vykreslovacího průchodu, 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 dochází k malé režii paměti (přibližně 600 bajtů), když uživatelské rozhraní není načteno.

Poznámka:

V sadě Windows App SDK představuje x:Load doporučený způsob odloženého načítání pro obsah XAML, který není potřeba okamžitě.

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. Pomocí nástrojů pro profilaci a výkon zkontrolujte počet prvků a využití paměti ve vaší aplikaci.

Možnost 1 – neefektivní

Zde se načte ListView, ale není viditelný, protože jeho Width je 0. Konstrukce ListView a jeho podřízených prvků probíhá ve vizuálním stromu, kde jsou následně načteny 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 vizuálního stromu se zobrazením seznamu.

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

Snímek obrazovky aplikace Managed Memory Test App 1 .EXE ukazující ListView a její součásti, které se načítají do paměti.

Možnost 2 – lepší

Tady je vlastnost ListView Visibility nastavená na Collapsed (druhá hodnota XAML je shodná s původním kódem). ListView je vytvořen ve stromu vizuálních prvků, ale jeho podřízené prvky nejsou. Stále se ale načítají 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 se stromem vizuálu a sbaleným zobrazením seznamu

ListView a jeho podřízené prvky jsou načteny do paměti.

Aktualizovaný snímek obrazovky zobrazující tabulku aplikace Managed Memory Test App 1 dot E X E ukazuje, že ListView a její 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 identický s původním atributem). ListView není vytvořen ve vizuálním stromu nebo načten do paměti při startu.

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

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

Vizuální strom s nenačítaným zobrazením seznamu

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. V aplikaci byste měli použít nástroje pro profilaci, abyste zjistili, jestli bude odložené načítání pomáhat.

Použití vlastností panelu rozložení

Panely rozložení mají vlastnost Pozadí, takže před panel není nutné vložit Obdélník jen kvůli jeho obarvení.

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 kolem panelu rozložení nemusíte zadávat prvek Ohraničení . 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 dostatek času, 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ů se obvykle používají k ukládání prostředků na poněkud globální úrovni, které chcete použít na více místech v aplikaci. Například styly, štětce, šablony atd.

ResourceDictionary je obecně optimalizovaný tak, aby nedocházelo k inicializaci prostředků, dokud nejsou vyžádány. Vyvarujte se však situacím, ve kterých by mohly být prostředky zbytečně vytvořeny.

Prostředky s x:Name

K odkazům na prostředky použijte atribut x:Key . Jakýkoli prostředek s atributem x:Name nebude využívat optimalizace platformy; místo toho se instancuje, jakmile je vytvořen ResourceDictionary. K tomu dochází, protože x:Name platformě říká, že vaše aplikace potřebuje přístup k tomuto prostředku, takže platforma musí vytvořit něco, co bude obsahovat odkaz na něj.

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 zdroje a ResourceDictionary

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ý Soubor 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 v App.xaml a vyhnout se duplicitám. App.xaml se ale analyzuje při spuštění aplikace, takže všechny prostředky, které se používají jenom na jedné stránce, pokud se nejedná o počáteční stránku, 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 používány pouze jednou stránkou, která není počáteční stránkou. To zbytečně zvyšuje dobu spuštění.

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 se SecondPageTextBrush do souboru SecondPage.xaml a přesuňte ThirdPageTextBrush se do souboru ThirdPage.xaml. InitialPageTextBrush může zůstat v App.xaml , protože prostředky aplikace musí být analyzovány při spuštění aplikace v každém případě.

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

Platforma XAML se snaží ukládat do mezipaměti běžně používané objekty, 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 předvedení, 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>

Pro opravu duplikace 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í diagnostiku 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ý u samotného prvku a změňte hodnotu na Visible v příslušných stavech. Budou existovat výjimky z této heuristiky: obecně je nejlepší nastavit hodnotu, kterou má vlastnost ve většině vizuálních stavů, místně na elementu.

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ý obrazec, kde horní polovina je černá z pozadí mřížky a dolní polovina je šedá díky poloprůhlednému bílému obdélníku, který se alfa-slučuje s černým pozadím 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>

Rozvržení panelů

Panel rozložení může mít dva účely: barvu oblasti a rozložení podřízených prvků. Pokud prvek dále v z-úrovni již obarvuje oblast, panel rozložení před ním nemusí tuto oblast vykreslovat; místo toho se může soustředit na rozložení svých potomků. 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 reaktivní na testování zásahu, nastavte hodnotu pozadí na ní Transparent.

Hranice

Pomocí prvku Ohraničení můžete nakreslit ohraničení kolem objektu. V tomto příkladu se mřížka používá jako šimfové ohraničení kolem textového pole. 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 mohou náhodně překrývat, pokud záporné okraje zasahují do hranice vykreslování jiného prvku a způsobí nadměrné kreslení.

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 vykreslí prvek jednou do rastrového obrázku a použije tento rastrový obrázek v každém snímku místo opakovaného vykreslování.

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 je některý z dílčích obrazců animován, protože by se mezipaměť rastrových obrázků musela pravděpodobně regenerovat každý snímek, což by zhatilo její účel.

Použití zkompilovaného výstupu XAML

Sada Windows App SDK zkompiluje XAML do binární reprezentace v rámci sestavení, což zabraňuje nákladům na analýzu textu za běhu. Kompilovaný formát také optimalizuje načítání a vytváření stromu pro běžné typy XAML, jako jsou vizuální stavy, slovníky prostředků a styly.

Předdefinované ovládací prvky WinUI a slovníky už tento kanál využívají. Pro vlastní aplikaci WinUI ponechte normální kroky kompilace XAML povolené, aby byl vygenerovaný výstup kompilace značek dostupný za běhu.