Partager via


Arbres dans WPF

Dans de nombreuses technologies, les éléments et les composants sont organisés dans une arborescence où les développeurs manipulent directement les nœuds d’objet dans l’arborescence pour affecter 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 du code ou définir des parties de l’application en XAML tout en pensant conceptuellement à la métaphore de l’arborescence d’objets, mais appellent une API spécifique ou utilisent un balisage spécifique pour le faire plutôt que certaines API de manipulation d’arborescence d’objets générales telles que vous pouvez utiliser dans le DOM XML. WPF expose deux classes d’assistance qui fournissent une vue métaphorique d’arborescence, LogicalTreeHelper et VisualTreeHelper. Les termes arborescence visuelle et arborescence logique sont également utilisés dans la documentation WPF, car ces mêmes arborescences sont utiles pour comprendre le comportement de certaines fonctionnalités WPF clés. Cette rubrique définit ce que représentent l’arborescence visuelle et l’arborescence logique, discute de la façon dont ces arborescences sont liées à un concept d’arborescence d’objets global, et présente LogicalTreeHelper et VisualTreeHelpers.

Arbres 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, la structure d’arborescence est créée en fonction de la façon dont vous affectez des valeurs de propriété pour les propriétés qui implémentent le modèle de contenu pour 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 manipulez pas toujours l’arborescence logique ou l’arborescence visuelle directement, comprendre les concepts de l’interaction des arborescences est utile pour comprendre WPF en tant que technologie. 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

Étant donné que l’arborescence d’objets est plus d’un concept qu’une API réelle, une autre façon de considérer le concept est un graphe d’objet. En pratique, lors de l'exécution, il existe des relations entre les objets où la métaphore de l'arbre se décompose. Néanmoins, en particulier avec l'interface utilisateur définie par XAML, la métaphore de l'arborescence est suffisamment pertinente pour que la plupart de la documentation WPF utilise le terme arborescence d'objets pour faire référence à ce concept général.

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 code XAML en tant que CODE XML sous un modèle objet de document et si vous aviez inclus les balises commentées comme implicites (ce qui aurait été légal), l’arborescence DOM XML résultante aurait inclus des éléments pour <ListBox.Items> et les autres éléments implicites. Toutefois, XAML ne fonctionne pas de cette manière : lorsque vous lisez le balisage et écrivez dans les objets, le graphe d'objets résultant n'inclut pas littéralement ListBox.Items. Elle a toutefois une ListBox propriété nommée Items qui contient un ItemCollection, et que ItemCollection est initialisé mais vide lorsque le ListBox XAML est traité. Ensuite, chaque élément enfant existant en tant que contenu pour ListBox est ajouté à ItemCollection par des appels de l'analyseur à ItemCollection.Add. Cet exemple de traitement xaml dans une arborescence d’objets est jusqu’à présent un exemple où l’arborescence d’objets créée est essentiellement 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 à ce bouton dans l’arborescence d’objets d’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 trouver les visuels du modèle, vous devrez plutôt examiner l'arbre visuel.

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 afin que les modèles de contenu puissent facilement itérer sur leurs objets enfants possibles, et afin que les modèles de contenu puissent être extensibles. En outre, l’arborescence logique fournit une infrastructure pour certaines notifications, par exemple lorsque tous les objets de l’arborescence logique sont chargés. En fait, l’arborescence logique est une approximation d’un graphique d’objet d’exécution au niveau de l’infrastructure, qui exclut les visuels, mais convient à de nombreuses opérations d’interrogation sur la composition de votre propre application au moment de 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 en vérifiant chaque FrameworkElement (ou FrameworkContentElement) pour une autre Resources valeur qui contient un ResourceDictionary, éventuellement contenant cette clé. L’arborescence logique est utilisée pour la recherche de ressources lorsque l’arborescence logique et l’arborescence visuelle sont présentes. Pour plus d’informations sur les dictionnaires de ressources et la recherche, consultez 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 ni FrameworkElement ni FrameworkContentElement. Par exemple, l’arborescence logique signale la Text valeur d’un TextBlock, qui est une chaîne.

Substitution 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 modèle de contenu ajoute ou supprime des objets dans l’arborescence logique. Pour obtenir un exemple de remplacement de l’arborescence logique, consultez Remplacer l’arborescence logique.

Héritage des valeurs des propriétés

L’héritage des valeurs de propriété fonctionne via une arborescence hybride. Les métadonnées réelles qui contiennent la propriété Inherits activant l’héritage de propriété appartiennent à la classe du framework WPF FrameworkPropertyMetadata. 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. Toutefois, 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 propager à travers un objet intermédiaire qui n’est pas dans l’arbre logique. Principalement, cela est pertinent pour que les éléments de modèle utilisent toutes les valeurs de propriété héritées définies sur l’instance qui est modèleée, ou à des niveaux encore plus élevés de composition au niveau de la page et, par conséquent, plus haut dans l’arborescence logique. Pour que l’héritage de valeur de propriété fonctionne de manière cohérente sur une telle limite, la propriété héritée doit être enregistrée en tant que propriété jointe, et vous devez suivre ce modèle si vous envisagez 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é ne peut pas être entièrement anticipée par une méthode 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é.

Arborescence visuelle

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. Lorsque vous écrivez un modèle pour un contrôle, vous définissez ou redéfinissez l’arborescence visuelle qui s’applique à ce contrôle. L’arborescence visuelle est également intéressante pour les développeurs qui veulent un contrôle de niveau inférieur sur le dessin pour des raisons de performances et d’optimisation. Une caractéristique de l'arborescence visuelle dans le cadre de la programmation classique d'applications WPF est que les routes d'événements d'un événement routé se déplacent principalement tout au long de l'arborescence visuelle, et non de l'arborescence logique. Cette subtilité du comportement des événements routés peut ne pas sembler évidente à première vue, sauf si vous êtes un développeur de contrôles. Le routage d’événements via l’arborescence visuelle permet aux contrôles qui implémentent la composition au niveau visuel de gérer les événements ou de créer des setters d’événements.

Arbres, é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, un ContentElement doit être hébergé dans un hôte de contenu qui soit à la fois un Visual et un participant logique à l'arborescence. En général, un tel objet est un FrameworkElement. Vous pouvez conceptualiser que l’hôte de contenu est un peu comme un « navigateur » pour le contenu et choisit comment afficher ce contenu dans la région d’écran que l’hôte contrôle. Lorsque le contenu est hébergé, il peut devenir un participant à certains processus d’arborescence qui sont normalement associés à l’arborescence visuelle. 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 puisse déclencher un événement routé qui achemine vers n’importe quel élément autre que lui-même.

Traversée d’arbre

La classe LogicalTreeHelper fournit les méthodes GetChildren, GetParent et FindLogicalNode pour la traversée de l’arborescence logique. Dans la plupart des cas, vous n'aurez généralement pas à parcourir l’arborescence logique des contrôles existants, car ces contrôles exposent presque toujours leurs éléments enfants logiques en tant que propriété de collection dédiée qui prend en charge l’accès à la collection, par exemple 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 visuelle d’un modèle appliqué. Vous devez être prudent lors de l’utilisation de 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.

Itinéraires pour les événements routés sous la forme d’une « arborescence »

Comme mentionné précédemment, l’itinéraire d’un événement routé donné se déplace le long d’un chemin unique et prédéterminé d’une arborescence qui est un hybride des représentations visuelles et logiques. L’itinéraire de l’événement peut se déplacer vers le haut ou vers le bas dans l’arborescence selon qu’il s’agit d’un événement routé par enchâssement ou par bulles. Le concept d’itinéraire d’événement n’a pas de classe d’assistance directement prise en charge qui peut être utilisée pour « parcourir » l’itinéraire d’événement indépendamment de déclencher un événement qui est réellement acheminé. 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 de ressources et arborescences

La recherche de dictionnaire de ressources pour toutes les Resources définies dans une page parcourt essentiellement l’arborescence logique. Les objets qui ne se trouvent pas dans l’arborescence logique peuvent référencer des ressources clés, mais la séquence de recherche de ressources commence au point 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.

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

Voir aussi