Partager via


Optimiser votre balisage XAML

L’analyse du balisage XAML pour construire des objets en mémoire prend beaucoup de temps pour une interface utilisateur complexe. Voici quelques opérations que vous pouvez effectuer pour améliorer l’analyse et le temps de chargement de votre balisage XAML et l’efficacité de la mémoire de votre application.

Au démarrage de l’application, limitez le balisage XAML chargé uniquement à ce dont vous avez besoin pour votre interface utilisateur initiale. Examinez le balisage dans votre page initiale (y compris les ressources de page) et vérifiez que vous ne chargez pas d’éléments supplémentaires qui ne sont pas nécessaires immédiatement. Ces éléments peuvent provenir de diverses sources, telles que les dictionnaires de ressources, les éléments initialement masqués et les éléments superposés à d'autres éléments.

L’optimisation de votre code XAML pour améliorer l’efficacité nécessite de faire des compromis ; il n’y a pas toujours une solution unique pour chaque situation. Ici, nous examinons certains problèmes courants et fournissons des instructions que vous pouvez utiliser pour faire les bons compromis pour votre application.

Réduire le nombre d’éléments

Bien que la plateforme XAML soit capable d’afficher un grand nombre d’éléments, vous pouvez rendre votre application étendue et afficher plus rapidement en utilisant le plus petit nombre d’éléments pour atteindre les visuels souhaités.

Les choix que vous effectuez dans la façon dont vous disposez vos contrôles d’interface utilisateur affectent le nombre d’éléments d’interface utilisateur créés au démarrage de votre application. Pour plus d’informations sur l’optimisation de la disposition, consultez Optimiser votre disposition XAML.

Le nombre d’éléments est extrêmement important dans les modèles de données, car chaque élément est créé à nouveau pour chaque élément de données. Pour plus d’informations sur la réduction du nombre d’éléments dans une liste ou une grille, consultez Réduction d'éléments par élément dans l'article Optimisation de l'interface utilisateur ListView et GridView.

Ici, nous examinons d’autres façons de réduire le nombre d’éléments que votre application doit charger au démarrage.

Différer la création d’un élément

Si votre balisage XAML contient des éléments que vous n’affichez pas immédiatement, vous pouvez différer le chargement de ces éléments jusqu’à ce qu’ils soient affichés. Par exemple, vous pouvez retarder la création de contenu non visible, tel qu’un onglet secondaire dans une interface utilisateur de type tabulation. Vous pouvez également afficher les éléments d’un affichage grille par défaut, mais fournir une option permettant à l’utilisateur d’afficher les données dans une liste à la place. Vous pouvez retarder le chargement de la liste jusqu’à ce qu’elle soit nécessaire.

Utilisez l’attribut x :Load au lieu de la propriété Visibility pour contrôler lorsqu’un élément est affiché. Lorsque la visibilité d’un élément est définie sur réduit, elle est ignorée pendant la passe de rendu, mais vous payez toujours les coûts d’instance d’objet en mémoire. Lorsque vous utilisez x :Load à la place, l’infrastructure ne crée pas l’instance d’objet tant qu’elle n’est pas nécessaire, les coûts de mémoire sont donc encore inférieurs. L’inconvénient est que vous payez une petite surcharge de mémoire (environ 600 octets) lorsque l’interface utilisateur n’est pas chargée.

Remarque

Vous pouvez retarder le chargement d’éléments à l’aide de l’attribut x :Load ou x :DeferLoadStrategy. L’attribut x :Load est disponible à partir de Windows 10 Creators Update (version 1703, sdk build 15063). La version minimale ciblée par votre projet Visual Studio doit être Windows 10 Creators Update (10.0, build 15063) afin d’utiliser x :Load. Pour cibler les versions antérieures, utilisez x :DeferLoadStrategy.

Les exemples suivants montrent la différence dans le nombre d’éléments et l’utilisation de la mémoire lorsque différentes techniques sont utilisées pour masquer les éléments d’interface utilisateur. Un ListView et un GridView contenant des éléments identiques sont placés dans la grille racine d’une page. ListView n’est pas visible, mais le GridView est affiché. Le code XAML de chacun de ces exemples produit la même interface utilisateur à l’écran. Nous utilisons les outils de Visual Studio pour le profilage et les performances afin de vérifier le nombre d’éléments et l’utilisation de la mémoire.

Option 1 - Inefficace

Ici, listView est chargé, mais n’est pas visible, car sa largeur est 0. ListView et chacun de ses éléments enfants sont créés dans l’arborescence visuelle et chargés en mémoire.

<!-- 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>

Arborescence visuelle dynamique avec ListView chargé. Le nombre total d’éléments pour la page est de 89.

Capture d’écran de l’arborescence visuelle avec affichage liste.

ListView et ses enfants sont chargés en mémoire.

Capture d’écran de l’application de test de mémoire managée 1.EXE montrant que ListView et ses enfants sont chargés en mémoire.

Option 2 - Mieux

Ici, la visibilité de ListView est définie sur réduite (l’autre XAML est identique à l’original). ListView est créé dans l’arborescence visuelle, mais ses éléments enfants ne le sont pas. Toutefois, ils sont chargés en mémoire, de sorte que l’utilisation de la mémoire est identique à l’exemple précédent.

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

Arborescence visuelle dynamique avec l’objet ListView réduit. Le nombre total d’éléments pour la page est de 46.

Capture d’écran de l’arborescence visuelle avec vue de liste réduite.

ListView et ses enfants sont chargés en mémoire.

Capture d’écran mise à jour du tableau de l'application Managed Memory Test App 1 point E X E, montrant que ListView et ses enfants sont chargés en mémoire.

Option 3 - Plus efficace

Ici, l'attribut x:Load du ListView est défini sur False (l'autre XAML est identique à l'original). ListView n’est pas créé dans l’arborescence visuelle ou chargé en mémoire au démarrage.

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

Arborescence visuelle dynamique avec listView non chargé. Le nombre total d’éléments pour la page est de 45.

Arborescence visuelle avec la liste d'affichage non chargée

ListView et ses enfants ne sont pas chargés en mémoire.

arborescence visuelle avec vue en liste

Remarque

Les nombres d’éléments et l’utilisation de la mémoire dans ces exemples sont très petits et ne sont montrés que pour illustrer le concept. Dans ces exemples, la surcharge liée à l’utilisation de x :Load est supérieure à l’économie de mémoire, de sorte que l’application ne bénéficierait pas. Vous devez utiliser les outils de profilage sur votre application pour déterminer si votre application bénéficie du chargement différé.

Utiliser les propriétés du panneau de disposition

Les panneaux de disposition ont une propriété Background. Il n’est donc pas nécessaire de placer un rectangle devant un panneau pour le colorer.

inefficace

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

efficace

<Grid Background="Black"/>

Les panneaux de disposition ont également des propriétés de bordure intégrées. Vous n’avez donc pas besoin de placer un élément Border autour d’un panneau de disposition. Pour plus d’informations et d’exemples, consultez Optimiser votre disposition XAML.

Utiliser des images à la place d’éléments vectoriels

Si vous réutilisez le même élément vectoriel suffisamment de fois, il devient plus efficace d’utiliser un élément Image à la place. Les éléments vectoriels peuvent être plus coûteux, car l’UC doit créer chaque élément individuel séparément. Le fichier image doit être décodé une seule fois.

Optimiser les ressources et les dictionnaires de ressources

En règle générale, vous utilisez dictionnaires de ressources pour stocker, à un niveau quelque peu global, les ressources que vous souhaitez référencer à plusieurs emplacements dans votre application. Par exemple, les styles, les pinceaux, les modèles, et ainsi de suite.

En général, nous avons optimisé ResourceDictionary pour ne pas instancier des ressources, sauf si elles sont demandées. Mais il existe des situations que vous devez éviter afin que les ressources ne soient pas instanciées inutilement.

Ressources avec x :Name

Utilisez l’attribut x :Key pour référencer vos ressources. Toute ressource avec l’attribut x :Name ne bénéficie pas de l’optimisation de la plateforme ; Au lieu de cela, elle est instanciée dès la création de ResourceDictionary. Cela se produit parce que x :Name indique à la plateforme que votre application a besoin d’un accès de champ à cette ressource, de sorte que la plateforme doit créer quelque chose pour créer une référence à.

ResourceDictionary dans un UserControl

Un ResourceDictionary défini à l’intérieur d’un UserControl entraîne une pénalité. La plateforme crée une copie d'un tel ResourceDictionary pour chaque instance de UserControl. Si vous avez un UserControl qui est utilisé beaucoup, déplacez le ResourceDictionary hors de UserControl et placez-le au niveau de la page.

Étendue des ressources et du ResourceDictionary

Si une page référence un contrôle utilisateur ou une ressource définie dans un autre fichier, l’infrastructure analyse également ce fichier.

Ici, étant donné que InitialPage.xaml utilise une ressource de ExampleResourceDictionary.xaml, l’ensemble de ExampleResourceDictionary.xaml doit être analysé au démarrage.

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>

Si vous utilisez une ressource sur de nombreuses pages dans votre application, la stocker dans app.xaml est une bonne pratique et évite la duplication. Toutefois, App.xaml est analysé au démarrage de l’application afin que toutes les ressources utilisées dans une seule page (sauf si cette page est la page initiale) doivent être placées dans les ressources locales de la page. Cet exemple montre app.xaml contenant des ressources utilisées par une seule page (qui n’est pas la page initiale). Cela augmente inutilement le temps de démarrage de l’application.

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>

Pour rendre cet exemple plus efficace, déplacez SecondPageTextBrush dans SecondPage.xaml et déplacez ThirdPageTextBrush dans ThirdPage.xaml. InitialPageTextBrush peut rester dans App.xaml, car les ressources de l'application doivent être analysées au démarrage de celle-ci dans tous les cas.

Fusionner plusieurs pinceaux similaires en une seule ressource.

La plateforme XAML tente de mettre en cache les objets couramment utilisés afin qu’ils puissent être réutilisés le plus souvent possible. Mais XAML ne peut pas facilement indiquer si un pinceau déclaré dans un élément de balisage est identique à un pinceau déclaré dans un autre. L’exemple ici utilise SolidColorBrush pour illustrer, mais le cas est plus probable et plus important avec GradientBrush. Vérifiez également les pinceaux qui utilisent des couleurs prédéfinies ; par exemple, "Orange" et "#FFFFA500" sont de même couleur.

inefficace.

<!-- 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>

Pour corriger la duplication, définissez le pinceau en tant que ressource. Si les contrôles d’autres pages utilisent le même pinceau, déplacez-le vers App.xaml.

efficace.

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

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

Réduire la surcharge

La surimpression se produit lorsque plusieurs objets sont dessinés dans les mêmes pixels de l'écran. Notez qu’il existe parfois un compromis entre ces conseils et le désir de réduire le nombre d’éléments.

Utilisez DebugSettings.IsOverdrawHeatMapEnabled comme diagnostic visuel. Vous pouvez trouver des objets dessinés que vous ne connaissiez pas étaient dans la scène.

Éléments transparents ou masqués

Si un élément n’est pas visible, car il est transparent ou masqué derrière d’autres éléments, et qu’il ne contribue pas à la disposition, puis supprimez-le. Si l’élément n’est pas visible dans l’état visuel initial, mais qu’il est visible dans d’autres états visuels, utilisez x :Load pour contrôler son état ou définir Visibility sur collapsed sur l’élément lui-même et modifiez la valeur en Visible dans les états appropriés. Il y aura des exceptions à cette heuristique : en général, la valeur qu’une propriété a dans la majorité des états visuels est la meilleure définie localement sur l’élément.

Éléments composites

Utilisez un élément composite au lieu de calquer plusieurs éléments pour créer un effet. Dans cet exemple, le résultat est une forme aux deux tons où la moitié supérieure est noire (provenant de l'arrière-plan de la grille ) et la moitié inférieure est grise (du rectangle blanc semi-transparent , mélangé par alpha sur l’arrière-plan noir de la grille ). Ici, 150% des pixels nécessaires pour obtenir le résultat sont remplis.

inefficace.

<!-- 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>

efficace.

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

Panneaux de mise en page

Un panneau de disposition peut avoir deux objectifs : pour colorer une zone et pour mettre en place des éléments enfants. Si un élément plus loin dans l’ordre z colorie déjà une zone, un panneau de disposition devant n’a pas besoin de peindre cette zone : au lieu de cela, il peut simplement se concentrer sur la disposition de ses enfants. Voici un exemple.

inefficace.

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

efficace.

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

Si la grille doit être testable, définissez une valeur d’arrière-plan transparente sur celle-ci.

Frontières

Utilisez un élément Border pour dessiner une bordure autour d’un objet. Dans cet exemple, une grille est utilisée comme bordure de fortune autour d’unezone de texte . Mais tous les pixels de la cellule centrale sont surchargés.

inefficace.

<!-- 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>

efficace.

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

Marges

Tenez compte des marges. Deux éléments voisins se chevauchent accidentellement si les marges négatives s’étendent dans les limites de la zone de rendu d’un autre élément et provoquent un redessin.

Mettre en cache du contenu statique

Une autre source d'excès de dessin est une forme constituée de nombreux éléments qui se superposent. Si vous définissez CacheMode sur BitmapCache sur l'UIElement qui contient la forme composite, la plateforme restitue l’élément à une image bitmap une seule fois, puis utilise cette image bitmap au lieu de la superposition.

inefficace.

<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>

diagramme Venn avec trois cercles solides

L’image ci-dessus est le résultat, mais voici une carte des régions retirées. Le rouge plus foncé indique des montants plus élevés de découvert bancaire.

diagramme de venn qui montre des zones qui se chevauchent

efficace.

<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>

Notez l’utilisation de CacheMode. N’utilisez pas cette technique si l’un des sous-éléments s’anime, car le cache bitmap devra probablement être régénéré à chaque image, ce qui annule l'objectif de l’utilisation de cette technique.

Utiliser XBF2

XBF2 est une représentation binaire du balisage XAML qui évite tous les coûts d’analyse de texte au moment de l’exécution. Il optimise également votre binaire pour améliorer le chargement et la construction d'arborescences, et permet d'accélérer les chemins pour les types XAML afin de réduire les coûts de création de tas et d'objets, par exemple pour VSM, ResourceDictionary, Styles, etc. Il est entièrement mappé en mémoire, de sorte qu'il n'y ait aucune empreinte de mémoire pour le chargement et la lecture d'une page XAML. En outre, il réduit l’empreinte disque des pages XAML stockées dans un appx. XBF2 est une représentation plus compacte et permet de réduire l’empreinte disque des fichiers XAML/XBF1 comparés jusqu’à 50%. Par exemple, l’application Photos intégrée a vu environ 60% réduction après la conversion en XBF2 passant d’environ 1 mo de ressources XBF1 à ~400 Ko de ressources XBF2. Nous avons également constaté que les applications bénéficient d'une amélioration de 15 à 20% au niveau du processeur et de 10 à 15% dans le tas Win32.

Les contrôles et dictionnaires intégrés XAML que l’infrastructure fournit sont déjà entièrement compatibles avec XBF2. Pour votre propre application, vérifiez que votre fichier projet déclare TargetPlatformVersion 8.2 ou version ultérieure.

Pour vérifier si vous avez XBF2, ouvrez votre application dans un éditeur binaire ; les 12e et 13e octets sont 00 02 si vous avez XBF2.