Arborescences dans WPF

Dans de nombreuses technologies, les éléments et les composants sont organisés en une arborescence, où les développeurs manipulent directement les nœuds d’objet dans l’arborescence pour modifier le rendu ou le comportement d’une application. Windows Presentation Foundation (WPF) utilise également plusieurs métaphores de structure d’arborescence pour définir des relations entre les éléments de programme. Pour la plupart, les développeurs WPF peuvent créer une application dans le code ou définir des parties de l’application en XAML, en se représentant mentalement la métaphore de l’arborescence des objets, mais en appelant pour cela une API spécifique ou en utilisant un balisage spécifique, au lieu d’utiliser une API générale de manipulation d’une arborescence d’objets, comme ce peut être le cas dans le modèle DOM XML. WPF expose deux classes d’assistance qui fournissent une vue métaphorique d’arborescence, LogicalTreeHelper et VisualTreeHelper. Les termes « arborescence d’éléments visuels » et « arborescence logique » sont également utilisés dans la documentation WPF, car ils sont utiles pour comprendre le comportement de certaines fonctionnalités clés de WPF. Cette rubrique définit ce que représentent l’arborescence visuelle et l’arborescence logique, explique comment ces arborescences LogicalTreeHelper sont liées à un concept d’arborescence d’objets global, et présente et VisualTreeHelpers.

Arborescences dans WPF

La structure d’arborescence la plus complète dans WPF est l’arborescence d’objets. Si vous définissez une page d’application en XAML, puis chargez le code XAML, la structure d’arborescence est créée en fonction des relations d’imbrication des éléments dans le balisage. Si vous définissez une application ou une partie de l’application dans le code, l’arborescence est créée en fonction de la façon dont vous affectez les valeurs de propriété pour les propriétés qui implémentent le modèle de contenu d’un objet donné. Dans WPF, il existe deux façons que l’arborescence d’objets complète soit conceptualisée et puisse être signalée à son API publique : en tant qu’arborescence logique et en tant qu’arborescence visuelle. Les distinctions entre l’arborescence logique et l’arborescence visuelle ne sont pas nécessairement importantes, mais elles peuvent parfois provoquer des problèmes avec certains sous-systèmes WPF et affecter les choix que vous effectuez dans le balisage ou le code.

Même si vous ne manipuliez pas toujours directement l’arborescence logique ou l’arborescence d’éléments visuels, la compréhension des concepts présidant à l’interaction des arborescences est utile pour comprendre la technologie WPF. La pensée de WPF comme une métaphore d’arbre d’un certain type est également essentielle pour comprendre comment l’héritage des propriétés et le routage des événements fonctionnent dans WPF.

Remarque

L’arborescence d’objets étant plus un concept qu’une API réelle, vous pouvez aussi vous représenter ce concept sous la forme d’un graphe d’objets. Dans la pratique, des relations existent entre les objets au moment de l’exécution, où la métaphore de l’arborescence cesse de fonctionner. Néanmoins, en particulier avec l’interface utilisateur définie en XAML, la métaphore de l’arborescence est suffisamment pertinente pour que la plus grande partie de la documentation WPF utilise le terme « arborescence d’objets » pour faire référence à ce concept général.

L’arborescence logique

Dans WPF, vous ajoutez du contenu aux éléments d’interface utilisateur en définissant les propriétés des objets qui sauvegardent ces éléments. Par exemple, vous ajoutez des éléments à un ListBox contrôle en manipulant sa Items propriété. En procédant ainsi, vous placez des éléments dans la ItemCollection valeur de propriété Items . De même, pour ajouter des objets à un DockPanel, vous manipulez sa Children valeur de propriété. Ici, vous ajoutez des objets au UIElementCollection. Pour obtenir un exemple de code, consultez Guide pratique pour ajouter un élément dynamiquement.

Dans XAML (Extensible Application Markup Language), lorsque vous placez des éléments de liste dans un ListBox ou des contrôles ou d’autres éléments d’interface utilisateur dans un DockPanel, vous utilisez également les Items propriétés et Children les propriétés, explicitement ou implicitement, comme dans l’exemple suivant.

<DockPanel
  Name="ParentElement"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <!--implicit: <DockPanel.Children>-->
  <ListBox DockPanel.Dock="Top">
    <!--implicit: <ListBox.Items>-->
    <ListBoxItem>
      <TextBlock>Dog</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Cat</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Fish</TextBlock>
    </ListBoxItem>
  <!--implicit: </ListBox.Items>-->
  </ListBox>
  <Button Height="20" Width="100" DockPanel.Dock="Top">Buy a Pet</Button>
  <!--implicit: </DockPanel.Children>-->
</DockPanel>

Si vous deviez traiter ce XAML en tant que XML dans un Document Object Model, et si vous aviez inclus les balises placées en commentaire comme étant implicites (ce qui est autorisé), l’arborescence DOM XML obtenue aurait inclus des éléments pour <ListBox.Items> et les autres éléments implicites. Mais XAML ne procède pas de cette façon quand vous lisez les balises et que vous écrivez dans les objets : le graphe d’objets obtenu n’inclut tout simplement pas ListBox.Items. Elle a toutefois une ListBox propriété nommée Items qui contient un ItemCollection, et qui ItemCollection est initialisée mais vide lorsque le ListBox code XAML est traité. Ensuite, chaque élément d’objet enfant qui existe en tant que contenu pour l’objet ListBox est ajouté au par les appels de l’analyseur ItemCollection à ItemCollection.Add. Cet exemple de traitement du code XAML dans une arborescence d’objets semble jusqu’à présent être un exemple où l’arborescence d’objets créée est fondamentalement l’arborescence logique.

Toutefois, l’arborescence logique n’est pas le graphique d’objet entier qui existe pour l’interface utilisateur de votre application au moment de l’exécution, même avec les éléments de syntaxe implicite XAML pris en compte. La principale raison de cela est les visuels et les modèles. Par exemple, considérez le Button. L’arborescence logique signale l’objet Button et sa chaîne Content. Mais il y a plus que ce bouton dans l’arborescence d’objets à l’exécution. En particulier, le bouton apparaît uniquement à l’écran comme il le fait, car un modèle de contrôle spécifique Button a été appliqué. Les visuels provenant d’un modèle appliqué (tels que le modèle défini Border en gris foncé autour du bouton visuel) ne sont pas signalés dans l’arborescence logique, même si vous examinez l’arborescence logique pendant l’exécution (par exemple, la gestion d’un événement d’entrée à partir de l’interface utilisateur visible, puis la lecture de l’arborescence logique). Pour rechercher les éléments visuels du modèle, vous devez au lieu de cela examiner l’arborescence d’éléments visuels.

Pour plus d’informations sur la façon dont la syntaxe XAML est mappée au graphe d’objets créé et la syntaxe implicite en XAML, consultez Syntaxe XAML en détail ou XAML dans WPF.

Objectif de l’arborescence logique

L’arborescence logique existe pour que les modèles de contenu puissent facilement itérer sur leurs éventuels objets enfants, et pour que les modèles de contenu puissent être extensibles. De même, l’arborescence logique fournit un framework pour certaines notifications, comme quand tous les objets de l’arborescence logique sont chargés. En fait, l’arborescence logique est une approximation d’un graphe d’objets à l’exécution au niveau du framework, ce qui exclut les éléments visuels, mais convient pour de nombreuses opérations de requêtes sur la composition de votre propre application à l’exécution.

En outre, les références de ressources statiques et dynamiques sont résolues en regardant vers le haut par le biais de l’arborescence logique des Resources collections sur l’objet demandeur initial, puis en continuant l’arborescence logique et case activée chaque FrameworkElement (ouFrameworkContentElement) pour une autre Resources valeur qui contient un ResourceDictionary, éventuellement contenant cette clé. L’arborescence logique est utilisée pour la recherche de ressources quand l’arborescence logique et l’arborescence d’éléments visuels sont présentes. Pour plus d’informations sur les dictionnaires et la recherche de ressources, consultez la page Ressources XAML.

Composition de l’arborescence logique

L’arborescence logique est définie au niveau de l’infrastructure WPF, ce qui signifie que l’élément de base WPF le plus pertinent pour les opérations d’arborescence logique est soit FrameworkElement soit FrameworkContentElement. Toutefois, comme vous pouvez le voir si vous utilisez réellement l’API LogicalTreeHelper , l’arborescence logique contient parfois des nœuds qui ne sont pas ou FrameworkElementFrameworkContentElement. Par exemple, l’arborescence logique signale la Text valeur d’un TextBlock, qui est une chaîne.

Remplacement de l’arborescence logique

Les auteurs de contrôles avancés peuvent remplacer l’arborescence logique en remplaçant plusieurs API qui définissent la façon dont un objet général ou un con mode tente l ajoute ou supprime des objets dans l’arborescence logique. Pour obtenir un exemple montrant comment remplacer l’arborescence logique, consultez Remplacer l’arborescence logique.

Héritage de la valeur de propriété

L’héritage des valeurs de propriété fonctionne à travers une arborescence hybride. Les métadonnées réelles qui contiennent la propriété qui active l’héritage Inherits de propriété sont la classe de niveau FrameworkPropertyMetadata framework WPF. Par conséquent, le parent qui contient la valeur d’origine et l’objet enfant qui hérite de cette valeur doivent être FrameworkElement ou FrameworkContentElement, et ils doivent tous les deux faire partie d’une arborescence logique. Cependant, pour les propriétés WPF existantes qui prennent en charge l’héritage des propriétés, l’héritage de la valeur des propriétés peut se perpétuer à travers un objet intermédiaire qui ne se trouve pas dans l’arborescence logique. Ceci est principalement approprié pour avoir des éléments de modèle utilisant des valeurs de propriétés héritées définies sur l’instance changée en modèle ou à des niveaux néanmoins plus élevés de la composition de niveau page, et par conséquent plus haut dans l’arborescence logique. Pour que l’héritage des valeurs de propriétés fonctionne de manière cohérente dans cette limite, la propriété qui hérite doit être inscrite comme propriété attachée, et vous devez suivre ce modèle si vous prévoyez de définir une propriété de dépendance personnalisée avec un comportement d’héritage de propriété. L’arborescence exacte utilisée pour l’héritage de propriétés ne peut pas être entièrement anticipée par une méthode d’utilitaire de classe d’assistance, même au moment de l’exécution. Pour plus d’informations, consultez Héritage de valeur de propriété.

L’arborescence d’éléments visuels

Outre le concept de l’arborescence logique, il existe également le concept de l’arborescence visuelle dans WPF. L’arborescence visuelle décrit la structure des objets visuels, comme représenté par la Visual classe de base. Quand vous écrivez un modèle pour un contrôle, vous définissez ou vous redéfinissez l’arborescence d’éléments visuels qui s’applique pour ce contrôle. L’arborescence d’éléments visuels est également intéressante pour les développeurs désireux un contrôle de plus bas niveau sur le dessin pour des raisons de performances et d’optimisation. Une exposition de l’arborescence visuelle dans le cadre de la programmation d’applications WPF conventionnelle est que les itinéraires d’événements pour un événement routé se déplacent principalement le long de l’arborescence visuelle, et non l’arborescence logique. Cette subtilité du comportement des événements routés peut ne pas être immédiatement apparente, sauf si vous êtes un créateur de contrôles. Le routage des événements à travers l’arborescence d’éléments visuel permet aux contrôles qui implémentent la composition au niveau de l’élément visuel de gérer les événements ou de créer des méthodes setter d’événement.

Arborescences, éléments de contenu et hôtes de contenu

Les éléments de contenu (classes dérivées de ContentElement) ne font pas partie de l’arborescence visuelle ; ils n’héritent Visual pas et n’ont pas de représentation visuelle. Pour apparaître dans une interface utilisateur, il ContentElement doit être hébergé dans un hôte de contenu qui est à la fois un participant à l’arborescence Visual logique. En général, un tel objet est un FrameworkElement. Vous pouvez conceptualiser ce mécanisme de la façon suivante : l’hôte de contenu est comme un « navigateur » pour le contenu et il choisit comment afficher ce contenu dans la zone de l’écran contrôlée par l’hôte. Quand le contenu est hébergé, il peut devenir un participant à certains processus de l’arborescence qui sont normalement associés à l’arborescence d’éléments visuels. En règle générale, la FrameworkElement classe hôte inclut du code d’implémentation qui ajoute tout élément hébergé ContentElement à l’itinéraire d’événement via des sous-nœuds de l’arborescence logique de contenu, même si le contenu hébergé ne fait pas partie de la véritable arborescence visuelle. Cela est nécessaire pour qu’un ContentElement événement routé puisse sourcer un événement routé qui achemine vers n’importe quel élément autre que lui-même.

Parcours des arborescences

La LogicalTreeHelper classe fournit les méthodes GetParentet FindLogicalNode les GetChildrenméthodes de traversée d’arborescence logique. Dans la plupart des cas, vous n’avez pas à parcourir l’arborescence logique des contrôles existants, car ces contrôles exposent presque toujours leurs éléments enfants logiques comme une propriété de collection dédiée qui prend en charge les accès à la collection, comme Add, un indexeur, etc. La traversée d’arborescence est principalement un scénario utilisé par les auteurs de contrôles qui choisissent de ne pas dériver des modèles de contrôle prévus tels que ItemsControl ou Panel où les propriétés de collection sont déjà définies et qui ont l’intention de fournir leur propre prise en charge des propriétés de collection.

L’arborescence visuelle prend également en charge une classe d’assistance pour la traversée d’arborescence visuelle. VisualTreeHelper L’arborescence visuelle n’est pas exposée aussi facilement par le biais de propriétés spécifiques au contrôle. Par conséquent, la VisualTreeHelper classe est la méthode recommandée pour parcourir l’arborescence visuelle si nécessaire pour votre scénario de programmation. Pour plus d’informations, consultez Vue d’ensemble du rendu graphique WPF.

Remarque

Il est parfois nécessaire d’examiner l’arborescence d’éléments visuels d’un modèle appliqué. Vous devez être prudent quand vous utilisez cette technique. Même si vous parcourez une arborescence visuelle pour un contrôle où vous définissez le modèle, les consommateurs de votre contrôle peuvent toujours modifier le modèle en définissant la Template propriété sur des instances, et même l’utilisateur final peut influencer le modèle appliqué en modifiant le thème système.

Routes pour les événements routés en tant qu’« arborescence »

Comme mentionné précédemment, la route de tout événement routé donné suit un chemin unique et prédéterminé d’une arborescence qui est un hybride de représentations de l’arborescence d’éléments visuels et de l’arborescence logique. La route d’événements peut suivre des directions vers le haut ou vers le bas de l’arborescence, selon qu’il s’agit d’une route d’événements de tunneling ou de propagation. Le concept de route d’événements n’a pas de prise en charge directe par une classe d’assistance qui pourrait être utilisée pour parcourir la route d’événements indépendamment du déclenchement d’un événement qui routerait effectivement. Il existe une classe qui représente l’itinéraire, EventRoutemais les méthodes de cette classe sont généralement destinées à une utilisation interne uniquement.

Dictionnaires et arborescences de ressources

La recherche dans le dictionnaire de ressources pour toutes les Resources définies dans une page parcourt l’arborescence logique. Les objets qui ne sont pas dans l’arborescence logique peuvent référencer des ressources avec des clés, mais la séquence de recherche des ressources commence à l’endroit où cet objet est connecté à l’arborescence logique. Dans WPF, seuls les nœuds d’arborescence logique peuvent avoir une Resources propriété qui contient un ResourceDictionary, il n’y a donc aucun avantage à parcourir l’arborescence visuelle à la recherche de ressources clés à partir d’un ResourceDictionary.

Cependant, la recherche de ressources peut également s’étendre au-delà de l’arborescence logique immédiate. Pour le balisage de l’application, la recherche de ressources peut continuer ensuite dans les dictionnaires de ressources de niveau application, puis dans la prise en charge des thèmes et les valeurs système qui sont référencées en tant que clés ou propriétés statiques. Les thèmes eux-mêmes peuvent également référencer des valeurs système en dehors de l’arborescence logique des thèmes si les références de ressource sont dynamiques. Pour plus d’informations sur les dictionnaires de ressources et la logique de la recherche, consultez Ressources XAML.

Voir aussi