Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
Comprendre le comportement intrinsèque des objets WPF vous aidera à faire les compromis appropriés entre les fonctionnalités et les performances.
Le fait de ne pas supprimer les gestionnaires d’événements sur les objets permet de conserver les objets actifs
Le délégué qu’un objet passe à son événement est une référence à cet objet. Par conséquent, les gestionnaires d’événements peuvent conserver les objets vivants plus longtemps que prévu. Quand vous nettoyez un objet inscrit pour détecter l’événement d’un objet, il est essentiel de supprimer ce délégué avant de libérer l’objet. La conservation des objets inutiles augmente l’utilisation de la mémoire de l’application. Cela est particulièrement vrai lorsque l’objet est la racine d’une arborescence logique ou d’une arborescence visuelle.
WPF introduit un modèle de détecteur d’événements faibles qui peut être utile quand les relations de durée de vie de l’objet entre la source et l’écouteur sont difficiles à suivre. Certains événements WPF existants utilisent ce modèle. Si vous implémentez des objets avec des événements personnalisés, ce modèle peut vous être utilisé. Pour plus d’informations, consultez Modèles d’événements faibles.
Il existe plusieurs outils, tels que le CLR Profiler et le Working Set Viewer, qui fournissent des informations sur l’utilisation de la mémoire d’un processus spécifié. Le profileur CLR inclut un nombre de vues très utiles du profil d’allocation, notamment un histogramme des types alloués, des graphiques d’allocation et d’appels, une chronologie montrant les opérations de garbage collection de diverses générations et l’état obtenu du tas managé après ces collections, ainsi qu’une arborescence des appels présentant les allocations par méthode et les chargements d’assembly. Pour plus d’informations, consultez Performance.
Propriétés et objets de dépendance
En règle générale, l’accès à une propriété de dépendance d’un DependencyObject n’est pas plus lent que l’accès à une propriété CLR. Bien qu’il y ait une petite surcharge de performances pour définir une valeur de propriété, l’obtention d’une valeur est aussi rapide que l’obtention de la valeur à partir d’une propriété CLR. La compensation de la petite surcharge des performances est le fait que les propriétés de dépendance prennent en charge des fonctionnalités robustes, telles que la liaison de données, l’animation, l’héritage et le style. Pour plus d’informations, consultez Vue d’ensemble des propriétés de dépendance.
Optimisations de DependencyProperty
Vous devez définir attentivement les propriétés de dépendance dans votre application. Si votre DependencyProperty n’affecte que les options de métadonnées de type de rendu, plutôt que d’autres options de métadonnées telles que AffectsMeasure, vous devez l’indiquer comme tel en remplaçant ses métadonnées. Pour plus d’informations sur la substitution ou l’obtention de métadonnées de propriété, consultez Métadonnées de propriété de dépendance.
Il peut être plus efficace que le gestionnaire de modifications de propriété invalide manuellement les étapes de mesure, d'organisation et de rendu, si toutes les modifications de propriété n'affectent pas réellement ces étapes. Par exemple, vous pouvez décider de rendre un arrière-plan uniquement lorsque une valeur est supérieure à une limite définie. Dans ce cas, votre gestionnaire de modifications de propriété invalide uniquement le rendu lorsque la valeur dépasse la limite définie.
Rendre une propriété de dépendance héritable n’est pas gratuit
Par défaut, les propriétés de dépendance inscrites ne sont pas héritées. Toutefois, vous pouvez explicitement rendre une propriété héritable. Bien qu'il s'agisse d'une fonctionnalité utile, convertir une propriété pour qu'elle soit héritée affecte les performances en augmentant le temps nécessaire à l'invalidation des propriétés.
Utiliser RegisterClassHandler avec soin
Lors de l’appel de RegisterClassHandler vous permet d’enregistrer l’état de votre instance, il est important de savoir que le gestionnaire est appelé sur chaque instance, ce qui peut entraîner des problèmes de performances. Utilisez uniquement RegisterClassHandler lorsque votre application nécessite d’enregistrer l’état de votre instance.
Définir la valeur par défaut d’un DependencyProperty pendant l’inscription
Lors de la création d’un DependencyProperty qui nécessite une valeur par défaut, définissez la valeur à l’aide des métadonnées par défaut passées en tant que paramètre à la méthode Register du DependencyProperty. Utilisez cette technique plutôt que de définir la valeur de propriété dans un constructeur ou sur chaque instance d’un élément.
Définir la valeur PropertyMetadata à l’aide de Register
Lors de la création d’un DependencyProperty, vous avez la possibilité de définir l'PropertyMetadata à l’aide des méthodes Register ou OverrideMetadata. Bien que votre objet puisse avoir un constructeur statique pour appeler OverrideMetadata, ce n’est pas la solution optimale et aura un impact sur les performances. Pour des performances optimales, réglez la PropertyMetadata sur Registerpendant l’appel.
Objets congelables
Un Freezable est un type spécial d’objet qui a deux états : décongelé et figé. Améliorer les performances de votre application et réduire l'empreinte mémoire passe par le gel des objets autant que possible. Pour plus d’informations, consultez Vue d’ensemble des objets gelables.
Chaque Freezable a un événement Changed qui est déclenché chaque fois qu’il change. Toutefois, les notifications de modification sont coûteuses en termes de performances d’application.
Prenons l’exemple suivant dans lequel chaque Rectangle utilise le même objet Brush :
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush
Par défaut, WPF fournit un gestionnaire d’événements pour l’événement SolidColorBrush de l’objet Changed afin d’invalider la propriété Rectangle de l’objet Fill. Dans ce cas, chaque fois que l'SolidColorBrush doit déclencher l'événement Changed, il est nécessaire d’appeler la fonction de rappel pour chaque Rectangle: l’accumulation de ces appels de fonction de rappel entraîne une pénalité de performance significative. En outre, il est très gourmand en performances pour ajouter et supprimer des gestionnaires à ce stade, car l’application doit parcourir toute la liste pour ce faire. Si votre scénario d’application ne modifie jamais le SolidColorBrush, vous payez inutilement le coût de la maintenance des gestionnaires d’événements Changed.
Le gel d’une Freezable peut améliorer ses performances, car il n’a plus besoin de dépenser des ressources pour maintenir les notifications de modification. Le tableau ci-dessous montre la taille d’un SolidColorBrush simple lorsque sa propriété IsFrozen est réglée à true, par rapport à quand elle ne l’est pas. Cela suppose l’application d’un pinceau à la propriété Fill de dix objets Rectangle.
| État | Taille |
|---|---|
| Figé SolidColorBrush | 212 octets |
| Non figé SolidColorBrush | 972 octets |
L’exemple de code suivant illustre ce concept :
Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);
for (int i = 0; i < 10; i++)
{
// Create a Rectangle using a non-frozed Brush.
Rectangle rectangleNonFrozen = new Rectangle();
rectangleNonFrozen.Fill = nonFrozenBrush;
// Create a Rectangle using a frozed Brush.
Rectangle rectangleFrozen = new Rectangle();
rectangleFrozen.Fill = frozenBrush;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)
For i As Integer = 0 To 9
' Create a Rectangle using a non-frozed Brush.
Dim rectangleNonFrozen As New Rectangle()
rectangleNonFrozen.Fill = nonFrozenBrush
' Create a Rectangle using a frozed Brush.
Dim rectangleFrozen As New Rectangle()
rectangleFrozen.Fill = frozenBrush
Next i
Les gestionnaires de changement sur des Freezables non figés peuvent conserver les objets actifs
Le délégué qu'un objet transmet à l'événement Freezable d'un objet Changed est en réalité une référence à cet objet. Par conséquent, les gestionnaires d’événements Changed peuvent conserver les objets actifs plus longtemps que prévu. Quand vous nettoyez un objet inscrit pour détecter l’événement Freezable d’un objet Changed, il est essentiel de supprimer ce délégué avant de libérer l’objet.
WPF gère également les événements Changed en interne. Par exemple, toutes les propriétés de dépendance qui prennent Freezable en tant que valeur écoutent automatiquement les événements Changed. La propriété Fill, qui prend un Brush, illustre ce concept.
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush
Lors de l'affectation de myBrush à myRectangle.Fill, un délégué pointant vers l'objet Rectangle sera ajouté à l'événement SolidColorBrush de l'objet Changed. Cela signifie que le code qui suit ne rend réellement pas myRect éligible au ramasse-miettes :
myRectangle = null;
myRectangle = Nothing
Dans ce cas, myBrush conserve encore myRectangle actif et le rappelle quand il déclenche son événement Changed. Notez que l’affectation de myBrush à la propriété Fill d’une nouvelle Rectangle ajoute simplement un autre gestionnaire d’événements à myBrush.
La méthode recommandée pour nettoyer ces types d’objets consiste à supprimer la Brush de la propriété Fill, ce qui supprime à son tour le gestionnaire d’événements Changed.
myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing
Virtualisation de l’interface utilisateur
WPF fournit également une variante de l’élément StackPanel qui « virtualise » automatiquement le contenu enfant lié aux données. Dans ce contexte, le mot virtualiser fait référence à une technique par laquelle un sous-ensemble d’objets est généré à partir d’un plus grand nombre d’éléments de données en fonction des éléments visibles à l’écran. Il est intensif, tant en termes de mémoire que de processeur, pour générer un grand nombre d’éléments d’interface utilisateur lorsque seuls quelques-uns peuvent être sur l’écran à un moment donné. VirtualizingStackPanel (par le biais des fonctionnalités fournies par VirtualizingPanel) calcule les éléments visibles et fonctionne avec le ItemContainerGenerator à partir d’un ItemsControl (par exemple, ListBox ou ListView) pour créer uniquement des éléments pour les éléments visibles.
En tant qu’optimisation des performances, les objets visuels pour ces éléments sont générés ou conservés en vie uniquement s’ils sont visibles à l’écran. Lorsqu’ils ne se trouvent plus dans la zone visible du contrôle, les objets visuels peuvent être supprimés. Cela ne doit pas être confondu avec la virtualisation des données, où les objets de données ne sont pas tous présents dans la collection locale, plutôt diffusés en continu selon les besoins.
Le tableau ci-dessous montre le temps écoulé d’ajout et de rendu de 5 000 éléments TextBlock à un StackPanel et un VirtualizingStackPanel. Dans ce scénario, les mesures représentent le temps entre l’attachement d’une chaîne de texte à la propriété ItemsSource d’un objet ItemsControl à l’heure où les éléments du panneau affichent la chaîne de texte.
| Panneau pour les hôtes | temps de rendu (ms) |
|---|---|
| StackPanel | 3210 |
| VirtualizingStackPanel | 46 |
Voir aussi
.NET Desktop feedback