Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Das Analysieren von XAML-Markup zum Erstellen von Objekten im Arbeitsspeicher ist zeitaufwändig für eine komplexe Benutzeroberfläche. Hier sind einige Dinge, die Sie tun können, um die Analyse- und Ladezeit Ihres XAML-Markups und die Speichereffizienz Ihrer App zu verbessern.
Beschränken Sie beim Starten der App das XAML-Markup, das nur auf das geladen wird, was Sie für die anfängliche Benutzeroberfläche benötigen. Überprüfen Sie das Markup auf der ersten Seite (einschließlich Seitenressourcen), und vergewissern Sie sich, dass Sie keine zusätzlichen Elemente laden, die nicht sofort benötigt werden. Diese Elemente können aus einer Vielzahl von Quellen stammen, z. B. Ressourcenwörterbücher, Elemente, die anfänglich eingeklappt sind, und Elemente, die auf andere Elemente gezeichnet werden.
Die Optimierung Ihres XAML-Codes für Effizienz erfordert Kompromisse; es gibt nicht immer eine einzige Lösung für jede Situation. Hier befassen wir uns mit einigen häufig auftretenden Problemen und stellen Richtlinien bereit, mit denen Sie die richtigen Kompromisse für Ihre App erzielen können.
Minimieren der Elementanzahl
Obwohl die XAML-Plattform in der Lage ist, eine große Anzahl von Elementen anzuzeigen, können Sie das Layout ihrer App beschleunigen und schneller rendern, indem Sie die kleinste Anzahl von Elementen verwenden, um die gewünschten visuellen Elemente zu erzielen.
Die Entscheidungen, die Sie bei der Anordnung der UI-Steuerelemente treffen, haben Auswirkungen auf die Anzahl der UI-Elemente, die beim Start der App erstellt werden. Ausführlichere Informationen zum Optimieren des Layouts finden Sie unter Optimieren des XAML-Layouts.
Die Elementanzahl ist in Datenvorlagen äußerst wichtig, da jedes Element für jedes Datenelement erneut erstellt wird. Informationen zur Reduzierung der Elemente pro Eintrag in einer Liste oder einem Raster finden Sie in dem Artikel ListView und GridView UI-Optimierung unter Reduzierung der Elemente pro Eintrag.
Hier sehen wir uns einige andere Möglichkeiten an, wie Sie die Anzahl der Elemente verringern können, die Ihre App beim Start laden muss.
Aufschieben der Elementerstellung
Wenn Ihr XAML-Markup Elemente enthält, die nicht sofort angezeigt werden, können Sie das Laden dieser Elemente zurückstellen, bis sie angezeigt werden. Sie können z. B. die Erstellung von nicht sichtbaren Inhalten wie einer sekundären Registerkarte in einer registerkartenähnlichen Benutzeroberfläche verzögern. Alternativ können Sie Elemente in einer Rasteransicht standardmäßig anzeigen, aber dem Benutzer eine Option zum Anzeigen der Daten in einer Liste bereitstellen. Sie können das Laden der Liste verzögern, bis sie benötigt wird.
Verwenden Sie das x:Load-Attribut anstelle der eigenschaft Visibility, um zu steuern, wann ein Element angezeigt wird. Wenn die Sichtbarkeit eines Elements auf Collapsedfestgelegt ist, wird es während des Renderdurchlaufs übersprungen, aber Sie zahlen weiterhin die Kosten der Objektinstanz im Arbeitsspeicher. Wenn Sie stattdessen "x:Load" verwenden, erstellt das Framework die Objektinstanz erst, wenn sie benötigt wird, sodass die Speicherkosten noch niedriger sind. Der Nachteil ist, dass Sie einen kleinen Arbeitsspeicheraufwand (ca. 600 Bytes) bezahlen, wenn die Benutzeroberfläche nicht geladen wird.
Hinweis
Sie können das Laden von Elementen mithilfe des x:Load oder des x:DeferLoadStrategy Attributs verzögern. Das x:Load-Attribut ist ab Windows 10 Creators Update (Version 1703, SDK Build 15063) verfügbar. Die für Ihr Visual Studio-Projekt erforderliche Mindestversion muss Windows 10 Creators Update (10.0, Build 15063) sein, um x:Load zu verwenden. Verwenden Sie für frühere Versionen x:DeferLoadStrategy.
Die folgenden Beispiele zeigen den Unterschied bei der Elementanzahl und der Speichernutzung, wenn verschiedene Techniken zum Ausblenden von UI-Elementen verwendet werden. Eine ListView und eine GridView mit identischen Elementen werden im Hauptgitter einer Seite platziert. Die ListView ist nicht sichtbar, aber die GridView wird angezeigt. Der XAML-Code in jedem dieser Beispiele erzeugt dieselbe Benutzeroberfläche auf dem Bildschirm. Wir verwenden die -Tools von Visual Studio für Profiling und Leistungsanalyse, um die Elementanzahl und die Speicherauslastung zu überprüfen.
Option 1 – Ineffizient
Hier wird die ListView geladen, ist aber nicht sichtbar, da die Breite 0 ist. Die ListView und jedes seiner Kindelemente wird im visuellen Baum erstellt und in den Speicher geladen.
<!-- 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>
Live-Visueller Baum mit geladener ListView. Die Gesamtanzahl der Elemente für die Seite beträgt 89.
ListView und die untergeordneten Elemente werden in den Arbeitsspeicher geladen.
Option 2 - Besser
Hier wird die Sichtbarkeit von ListView auf reduziert festgelegt (der andere XAML ist identisch mit dem Original). Die ListView wird im visuellen Baum erstellt, jedoch nicht die untergeordneten Elemente. Sie werden jedoch in den Arbeitsspeicher geladen, sodass die Speichernutzung mit dem vorherigen Beispiel identisch ist.
<ListView x:Name="List1" Visibility="Collapsed">
Live-Visualisierung mit eingeklappter ListView. Die Gesamtanzahl der Elemente für die Seite beträgt 46.
ListView und die untergeordneten Elemente werden in den Arbeitsspeicher geladen.
Option 3 – effizienteste
Hier ist die ListView mit dem x:Load-Attribut auf False festgelegt (das andere XAML ist identisch mit dem Original). Die ListView wird nicht im visuellen Baum erstellt oder beim Startvorgang in den Arbeitsspeicher geladen.
<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">
Live-Visualisierungsbaum mit nicht geladenem ListView. Die Gesamtanzahl der Elemente für die Seite beträgt 45.
ListView und die untergeordneten Elemente werden nicht in den Arbeitsspeicher geladen.
Hinweis
Die Elementanzahl und die Speichernutzung in diesen Beispielen sind sehr klein und werden nur gezeigt, um das Konzept zu veranschaulichen. In diesen Beispielen ist der Mehraufwand für die Verwendung von "x:Load" größer als die Speichereinsparungen, sodass die App nicht profitieren würde. Sie sollten die Profilerstellungstools in Ihrer App verwenden, um zu ermitteln, ob Ihre App von verzögertem Laden profitiert.
Verwenden von Eigenschaften des Layout-Panels
Layout-Panels verfügen über eine Hintergrundeigenschaft, sodass kein Rechteck vor einem Panel platziert werden muss, um es zu färben.
ineffiziente
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
<Rectangle Fill="Black"/>
</Grid>
effiziente
<Grid Background="Black"/>
Layoutpanels verfügen auch über integrierte Rahmeneigenschaften, daher müssen Sie kein Border-Element um ein Layoutpanel einfügen. Weitere Informationen und Beispiele finden Sie unter Optimieren des XAML-Layouts.
Verwenden von Bildern anstelle von vektorbasierten Elementen
Wenn Sie dasselbe vektorbasierte Element ausreichend wiederverwenden, wird es effizienter, stattdessen ein Image-Element zu verwenden. Vektorbasierte Elemente können teurer sein, da die CPU jedes einzelne Element separat erstellen muss. Die Bilddatei muss nur einmal decodiert werden.
Optimieren von Ressourcen und Ressourcenwörterbüchern
In der Regel verwenden Sie Ressourcenwörterbücher zum Speichern von Ressourcen auf einer etwas globalen Ebene, auf die Sie an mehreren Stellen in Ihrer App verweisen möchten. Beispielsweise Stile, Pinsel, Vorlagen usw.
Im Allgemeinen haben wir ResourceDictionary- optimiert, um Ressourcen nicht instanziieren zu können, es sei denn, sie werden gefragt. Es gibt jedoch Situationen, die Sie vermeiden sollten, damit Ressourcen nicht unnötig instanziiert werden.
Ressourcen mit x:Name
Verwenden Sie das x:Key-Attribut, um auf Ihre Ressourcen zu verweisen. Jede Ressource mit dem x:Name-Attribut profitiert nicht von der Plattformoptimierung; Stattdessen wird sie instanziiert, sobald das ResourceDictionary erstellt wird. Dies geschieht, weil x:Name der Plattform mitteilt, dass Ihre App Feldzugriff auf diese Ressource benötigt, und die Plattform daher etwas erzeugen muss, um darauf verweisen zu können.
ResourceDictionary in einem Benutzersteuerelement
Ein in einem UserControl- definiertes ResourceDictionary führt zu einer Strafe. Die Plattform erstellt eine Kopie eines solchen ResourceDictionary für jede Instanz von UserControl. Wenn Sie ein häufig verwendetes UserControl haben, verschieben Sie das ResourceDictionary aus dem UserControl auf Seitenebene.
Ressourcen- und Ressourcenwörterbuch-Bereich
Wenn eine Seite auf ein Benutzersteuerelement oder eine Ressource verweist, die in einer anderen Datei definiert ist, analysiert das Framework diese Datei ebenfalls.
Da InitialPage.xaml- eine Ressource aus ExampleResourceDictionary.xaml-verwendet, muss die gesamte ExampleResourceDictionary.xaml- beim Start analysiert werden.
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>
Wenn Sie eine Ressource auf vielen Seiten in der gesamten App verwenden, empfiehlt es sich, sie in App.xaml- zu speichern und Duplizierungen zu vermeiden. App.xaml- wird jedoch beim Start der Anwendung analysiert, sodass alle Ressourcen, die nur auf einer Seite verwendet werden (es sei denn, diese Seite ist die erste Seite), in die lokalen Ressourcen der Seite eingefügt werden sollten. Dieses Beispiel zeigt App.xaml- mit Ressourcen, die nur von einer Seite verwendet werden (dies ist nicht die erste Seite). Dadurch wird die Startzeit der App unnötig erhöht.
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>
Um dieses Beispiel effizienter zu gestalten, verschieben Sie SecondPageTextBrush in SecondPage.xaml-, und verschieben Sie ThirdPageTextBrush in ThirdPage.xaml-.
InitialPageTextBrush können in App.xaml verbleiben, da Anwendungsressourcen auf jeden Fall beim App-Start analysiert werden müssen.
Konsolidieren Sie mehrere Pinsel, die gleich aussehen, in einer Ressource
Die XAML-Plattform versucht, häufig verwendete Objekte zwischenzuspeichern, damit sie so oft wie möglich wiederverwendet werden können. XAML kann jedoch nicht einfach erkennen, ob ein in einem Teil von Markup deklarierter Pinsel mit einem in einem anderen deklarierten Pinsel identisch ist. In diesem Beispiel wird SolidColorBrush verwendet, um dies zu demonstrieren, aber bei GradientBrushist der Fall wahrscheinlicher und wichtiger. Überprüfen Sie auch, ob Pinsel vordefinierte Farben verwenden; beispielsweise sind "Orange" und "#FFFFA500" die gleiche Farbe.
ineffizient.
<!-- 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>
Um die Duplizierung zu beheben, definieren Sie den Pinsel als Ressource. Wenn Steuerelemente auf anderen Seiten denselben Pinsel verwenden, verschieben Sie ihn in App.xaml.
Effizient.
<Page ... >
<Page.Resources>
<SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
</Page.Resources>
<StackPanel>
<TextBlock Foreground="{StaticResource BrandBrush}" />
<Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
</StackPanel>
</Page>
Minimieren der Überziehung
Überzeichnung erfolgt, wenn mehrere Objekte in denselben Bildschirmpixeln gezeichnet werden. Beachten Sie, dass es manchmal einen Kompromiss zwischen dieser Anleitung und dem Wunsch gibt, die Anzahl der Elemente zu minimieren.
Verwenden Sie DebugSettings.IsOverdrawHeatMapEnabled als visuelle Diagnose. Möglicherweise finden Sie Objekte, die gezeichnet werden, von denen Sie nicht wussten, dass sie sich in der Szene befanden.
Transparente oder ausgeblendete Elemente
Wenn ein Element nicht sichtbar ist, da es transparent oder hinter anderen Elementen verborgen ist und nicht zum Layout beiträgt, löschen Sie es. Wenn das Element im anfänglichen visuellen Zustand nicht sichtbar ist, aber in anderen visuellen Zuständen sichtbar ist, verwenden Sie "x:Load", um seinen Status zu steuern oder legen Sie die Sichtbarkeit des Elements auf Collapsed fest und ändern Sie den Wert in den entsprechenden Zuständen auf Visible. Es gibt Ausnahmen von dieser Heuristik: Im Allgemeinen wird der Wert, den eine Eigenschaft in den meisten visuellen Zuständen hat, am besten lokal für das Element festgelegt.
Zusammengesetzte Elemente
Verwenden Sie ein zusammengesetztes Element, anstatt mehrere Elemente zu überschichten, um einen Effekt zu erzeugen. In diesem Beispiel ist das Ergebnis eine zweifarbige Form, bei der die obere Hälfte schwarz ist (aus dem Hintergrund des Grid), und die untere Hälfte grau (vom halbtransparenten weißen Rechteck, das über das Schwarz des Hintergrunds des Gridalphagemischt ist). Hier werden 150% der zum Erreichen des Ergebnisses erforderlichen Pixel gefüllt.
ineffizient.
<!-- 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>
Effizient.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Black"/>
<Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>
Layout-Panels
Ein Layoutpanel kann zwei Zwecke haben: zum Färben eines Bereichs und zum Layout untergeordneter Elemente. Wenn ein Element in der Z-Reihenfolge weiter hinten einen Bereich bereits einfärbt, muss ein Layoutpanel weiter vorn diesen Bereich nicht zeichnen: Es kann stattdessen darauf fokussieren, seine untergeordneten Elemente zu gestalten. Hier ist ein Beispiel.
ineffizient.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Effizient.
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Wenn das Grid- treffertestbar sein muss, legen Sie dafür einen transparenten Hintergrundwert fest.
Grenzen
Verwenden Sie ein Element vom Typ Border, um einen Rahmen um ein Objekt zu zeichnen. In diesem Beispiel wird ein Grid- als Schieberahmen um ein TextBox-verwendet. Aber alle Pixel in der mittleren Zelle werden überschrieben.
ineffizient.
<!-- 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>
Effizient.
<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
<TextBox/>
</Border>
Ränder
Achten Sie auf Ränder. Zwei benachbarte Elemente überlappen sich (möglicherweise versehentlich), wenn negative Ränder in die Rendergrenzen eines anderen reichen und eine Überzeichnung verursachen.
Zwischenspeichern statischer Inhalte
Eine andere Quelle der Überzeichnung ist eine Form aus vielen überlappenden Elementen. Wenn Sie CacheMode- auf BitmapCache- auf dem UIElement- festlegen, das das zusammengesetzte Formelement enthält, rendert die Plattform das Element einmal als Bitmap und verwendet dann diese Bitmap für jeden Frame, anstatt es jedes Mal neu zu zeichnen.
ineffizient.
<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>
Das obige Bild ist das Ergebnis, aber hier ist eine Karte der übergezeichneten Regionen. Dunkleres Rot weist auf eine höhere Überziehung hin.
Effizient.
<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>
Beachten Sie die Verwendung von CacheMode-. Verwenden Sie diese Technik nicht, wenn eine der Unterformen animiert wird, da der Bitmapcache vermutlich jeden Frame neu generiert werden muss, was den Zweck zunichte macht.
Verwenden Sie XBF2
XBF2 ist eine binäre Darstellung des XAML-Markups, das alle Kosten für die Textanalyse zur Laufzeit vermeidet. Es optimiert Ihre Binärdatei für das Laden und die Baumerstellung und ermöglicht einen "Fast-Path" für XAML-Typen, um die Kosten für Heap- und Objekterstellung zu verbessern, wie z. B. VSM, ResourceDictionary, Styles und so weiter. Es ist vollständig speicherabbild, sodass kein Heap-Speicher erforderlich ist, um eine XAML-Seite zu laden und zu lesen. Darüber hinaus wird der Datenträgerbedarf gespeicherter XAML-Seiten in einem Appx reduziert. XBF2 ist eine kompaktere Darstellung und kann den Datenträgerbedarf von vergleichenden XAML/XBF1-Dateien um bis zu 50%reduzieren. So sah die integrierte Fotos-App etwa 60% Reduzierung nach der Konvertierung in XBF2 von ca. 1 MB XBF1-Ressourcen auf ca. 400 KB XBF2-Ressourcen. Wir haben auch gesehen, dass Apps überall von 15 bis 20% in CPU und 10 bis 15% im Win32-Heap profitieren.
Integrierte XAML-Steuerelemente und Wörterbücher, die vom Framework bereitgestellt werden, sind bereits vollständig XBF2-fähig. Stellen Sie für Ihre eigene App sicher, dass Ihre Projektdatei TargetPlatformVersion 8.2 oder höher deklariert.
Um zu überprüfen, ob Sie XBF2 haben, öffnen Sie Ihre App in einem Binär-Editor. die 12. und 13. Bytes sind 00 02, wenn Sie XBF2 haben.