Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Att parsa XAML-markering för att konstruera objekt i minnet är tidskrävande för ett komplext användargränssnitt. Här är några saker du kan göra för att förbättra parsnings- och inläsningstiden för XAML-markering och minneseffektiviteten i din WinUI-app.
Vid appstart begränsar du XAML-markering som läses in till endast det du behöver för det ursprungliga användargränssnittet. Granska markeringen på den första sidan, inklusive sidresurser, och bekräfta att du inte läser in extra element som inte behövs direkt. Dessa element kan komma från en mängd olika källor, till exempel resursordlistor, element som ursprungligen är dolda och element som ritas över andra element.
För att optimera din XAML för effektivitet krävs kompromisser. Det finns inte alltid en enda lösning för varje situation. Här tittar vi på några vanliga problem och ger riktlinjer som du kan använda för att göra rätt kompromisser för din WinUI-app.
Minimera antal element
Även om XAML-plattformen kan visa ett stort antal element kan du få appen att lägga ut och rendera snabbare genom att använda det minsta antalet element som behövs för att uppnå de visuella objekt du vill ha.
De val du gör i hur du lägger ut användargränssnittskontrollerna påverkar antalet gränssnittselement som skapas när din app startar. Mer detaljerad information om hur du optimerar layouten finns i Optimera din XAML-layout.
Elementantal är mycket viktigt i datamallar eftersom varje element skapas igen för varje dataobjekt. Information om hur du minskar antalet element i en lista eller ett rutnät finns i Artikeln Om minskning av element per objekt i artikeln Optimera ListView och GridView-prestanda för WinUI .
Här tittar vi på några andra sätt att minska antalet element som appen måste läsa in vid start.
Skjuta upp skapande av objekt
Om XAML-markering innehåller element som du inte visar direkt kan du skjuta upp inläsningen av dessa element tills de visas. Du kan till exempel fördröja skapandet av icke-synligt innehåll, till exempel en sekundär flik i ett flikliknande användargränssnitt. Eller så kan du visa objekt i en rutnätsvy som standard, men ange ett alternativ för användaren att visa data i en lista i stället. Du kan fördröja inläsningen av listan tills den behövs.
Använd attributet x:Load i stället för egenskapen Synlighet för att styra när ett element visas. När ett elements synlighet är inställd på Komprimerad hoppas det över under återgivningspasset, men du betalar fortfarande kostnaderna för objektinstansen i minnet. När du använder x:Load i stället skapar ramverket inte objektinstansen förrän den behövs, så minneskostnaderna är ännu lägre. Nackdelen är att du betalar en liten minneskostnad (cirka 600 byte) när användargränssnittet inte läses in.
Anmärkning
I Windows App SDK är x:Load det rekommenderade mönster för uppskjuten inläsning för XAML-innehåll som inte behövs omedelbart.
I följande exempel visas skillnaden i antal element och minnesanvändning när olika tekniker används för att dölja gränssnittselement. En ListView och en GridView som innehåller identiska objekt placeras i en sidas rotrutnät. ListView visas inte, men GridView visas. XAML i vart och ett av dessa exempel genererar samma användargränssnitt på skärmen. Använd verktyg för profilering och prestanda för att kontrollera antalet element och minnesanvändning i din app.
Alternativ 1 – Ineffektiv
Här läses ListView in, men visas inte eftersom det Width är 0. ListView och vart och ett av dess underordnade element skapas i det visuella trädet och läses in i minnet.
<!-- 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>
Visuellt träd i realtid med ListView inläst. Totalt antal element för sidan är 89.
ListView och dess underordnade filer läses in i minnet.
Alternativ 2 – Bättre
Här är ListView's Visibility inställt på Collapsed (den andra XAML är identisk med originalet). ListView skapas i det visuella trädet, men dess underordnade element skapas inte. De läses dock fortfarande in i minnet, så minnesanvändningen är identisk med föregående exempel.
<ListView x:Name="List1" Visibility="Collapsed">
Levande visuellt träd med ListView komprimerat. Totalt antal element för sidan är 46.
ListView och dess underordnade filer läses in i minnet.
Alternativ 3 – Mest effektivt
Här har ListView attributet x:Load inställt på False (den andra XAML är identisk med originalet). ListView skapas inte i det visuella trädet eller läses in i minnet vid start.
<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">
Levande visuellt träd med ListView inte inläst. Totalt antal element för sidan är 45.
ListView och dess underordnade filer läses inte in i minnet.
Anmärkning
Elementantalet och minnesanvändningen i dessa exempel är mycket små och visas bara för att demonstrera konceptet. I de här exemplen är kostnaden för att använda x:Load större än minnesbesparingarna, så appen skulle inte gynnas. Du bör använda profileringsverktyg i din app för att avgöra om uppskjuten inläsning hjälper.
Använd egenskaper för layoutpanel
Layoutpaneler har en bakgrundsegenskap, så du behöver inte placera en rektangel framför en panel bara för att färgsätta den.
Ineffektiva
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
<Rectangle Fill="Black"/>
</Grid>
Effektiv
<Grid Background="Black"/>
Layoutpaneler har också inbyggda kantlinjeegenskaper, så du behöver inte placera ett kantlinjeelement runt en layoutpanel. Mer information och exempel finns i Optimera XAML-layouten .
Använda bilder i stället för vektorbaserade element
Om du återanvänder samma vektorbaserade element tillräckligt många gånger blir det mer effektivt att använda ett bildelement i stället. Vektorbaserade element kan vara dyrare eftersom processorn måste skapa varje enskilt element separat. Bildfilen måste bara avkodas en gång.
Optimera resurser och resursordlistor
Du använder vanligtvis resursordlistor för att lagra resurser på en något global nivå som du vill referera till på flera platser i din app. Till exempel format, penslar, mallar och så vidare.
I allmänhet optimeras ResourceDictionary för att undvika instansiering av resurser tills de tillfrågas. Men det finns situationer som du bör undvika så att resurserna inte instansieras i onödan.
Resurser med x:Name
Använd x:Key-attributet för att referera till dina resurser. Alla resurser med attributet x:Name drar inte nytta av plattformsoptimeringen. i stället instansieras den så snart ResourceDictionary har skapats. Detta beror på att x:Name talar om för plattformen att din app behöver fältåtkomst till den här resursen, så plattformen måste skapa något för att hålla en referens till den.
ResourceDictionary i en användarstyrning
En ResourceDictionary som definieras inuti en UserControl medför en straffavgift. Plattformen skapar en kopia av en sådan ResourceDictionary för varje instans av UserControl. Om du har en UserControl som används mycket flyttar du ResourceDictionary från UserControl och placerar den på sidnivå.
Resurs- och resursordbokomfång
Om en sida refererar till en användarkontroll eller en resurs som definierats i en annan fil parsar ramverket även filen.
Eftersom InitialPage.xaml använder en resurs från ExampleResourceDictionary.xaml måste hela ExampleResourceDictionary.xaml parsas vid start.
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>
Om du använder en resurs på många sidor i appen är det bra att lagra den i App.xaml och undvika duplicering. Men App.xaml parsas vid appstart, så alla resurser som endast används på en sida, såvida inte den sidan är den första sidan, bör placeras i sidans lokala resurser. I det här exemplet visas App.xaml som innehåller resurser som endast används av en sida som inte är den första sidan. Detta ökar starttiden i onödan.
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>
Om du vill göra det här exemplet mer effektivt går du SecondPageTextBrush till SecondPage.xaml och flyttar ThirdPageTextBrush till ThirdPage.xaml.
InitialPageTextBrush kan finnas kvar i App.xaml eftersom programresurser måste parsas vid appstart i alla fall.
Konsolidera flera penslar som ser likadana ut i en resurs
XAML-plattformen försöker cachelagra objekt som används ofta, så att de kan återanvändas så mycket som möjligt. Men XAML kan inte enkelt avgöra om en pensel som deklareras i en markering är densamma som en pensel som deklareras i en annan. Exemplet här använder SolidColorBrush för att demonstrera, men fallet är mer troligt och viktigare med GradientBrush. Kontrollera även för penslar som använder fördefinierade färger. till exempel "Orange" och "#FFFFA500" har samma färg.
Ineffektiva
<!-- 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>
Om du vill åtgärda dupliceringen definierar du penseln som en resurs. Om kontroller på andra sidor använder samma pensel flyttar du den till App.xaml.
Effektiv
<Page ... >
<Page.Resources>
<SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
</Page.Resources>
<StackPanel>
<TextBlock Foreground="{StaticResource BrandBrush}" />
<Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
</StackPanel>
</Page>
Minimera övertrassering
Överlagring sker när flera objekt ritas i samma skärmpixelområden. Observera att det ibland finns en kompromiss mellan den här vägledningen och viljan att minimera antalet element.
Använd DebugSettings.IsOverdrawHeatMapEnabled som en visuell diagnostik. Du kanske hittar objekt som ritas som du inte visste fanns i scenen.
Transparenta eller dolda element
Om ett element inte är synligt eftersom det är transparent eller dolt bakom andra element och inte bidrar till layouten tar du bort det. Om elementet inte är synligt i det inledande visuella tillståndet men är synligt i andra visuella tillstånd använder du x:Load för att styra dess tillstånd eller ange Synlighet till Komprimerad för själva elementet och ändra värdet till Synligt i lämpliga tillstånd. Det kommer att finnas undantag till denna heuristiska: i allmänhet är värdet som en egenskap har i de flesta visuella tillstånd bäst inställt lokalt på elementet.
Sammansatta element
Använd ett sammansatt element i stället för att lägga flera element i lager för att skapa en effekt. I det här exemplet är resultatet en tvåtonad form där den övre halvan är svart från rutnätets bakgrund och den nedre halvan är grå från den halvtransparenta vita rektangeln alfablandad över rutnätets svarta bakgrund. Här fylls 150% av de pixlar som krävs för att uppnå resultatet.
Ineffektiva
<!-- 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>
Effektiv
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Black"/>
<Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>
Layoutpaneler
En layoutpanel kan ha två syften: att färgsätta ett område och att organisera underordnade element. Om ett element längre tillbaka i z-ordningen redan täcker ett område behöver layoutpanelen framför inte måla det området. I stället kan den fokusera på att lägga ut sina barn. Här följer ett exempel.
Ineffektiva
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Effektiv
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Om rutnätet måste vara träfftestbart anger du ett bakgrundsvärde Transparent för det.
Gränser
Använd ett kantlinjeelement för att rita en kantlinje runt ett objekt. I det här exemplet används ett rutnät som en provisorisk kantlinje runt en textruta. Men alla bildpunkter i mittenrutan är överritade.
Ineffektiva
<!-- 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>
Effektiv
<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
<TextBox/>
</Border>
Marginaler
Var medveten om marginaler. Två närliggande element överlappar, eventuellt av misstag, om negativa marginaler sträcker sig till ett annat elements återgivningsgräns och orsakar övertrassering.
Cachelagrat statiskt innehåll
En annan källa till övertrassering är en form som består av många överlappande element. Om du ställer in CacheMode på BitmapCache på UIElement som innehåller den sammansatta formen, renderar plattformen elementet till en bitmapp en gång och använder bitmappen varje bildruta i stället för övertrassering.
Ineffektiva
<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>
Bilden ovan är resultatet, men här är en karta över de överlagrade regionerna. Mörkare röd anger högre mängder övertrassering.
Effektiv
<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>
Observera användningen av CacheMode. Använd inte den här tekniken om någon av underformerna animeras eftersom bitmappscachen sannolikt måste återskapas varje bildruta, vilket undergräver syftet.
Använda kompilerade XAML-utdata
Windows App SDK kompilerar XAML till en binär representation som en del av bygget, vilket undviker textparsingkostnader vid körning. Det kompilerade formatet optimerar även inläsning och trädskapande för vanliga XAML-typer som visuella tillstånd, resursordlistor och formatmallar.
Inbyggda WinUI-kontroller och ordlistor drar redan nytta av den här pipelinen. För din egen WinUI-app behåller du de normala XAML-kompileringsstegen aktiverade så att de genererade kompileringsutdata för markering är tillgängliga vid körning.
Relaterade artiklar
Windows developer