Portées de nom WPF
Mise à jour : novembre 2007
Les portées de noms sont à la fois un concept et les objets de programmation qui stockent les relations entre les noms définis XAML d'objets et leurs équivalents d'instance. Les portées de noms dans le code managé WPF sont créés en chargeant les pages pour une application XAML. Les portées de noms comme objet de programmation sont définis par l'interface INameScope et sont également implémentés par la classe pratique NameScope.
Cette rubrique comprend les sections suivantes.
- Les portées de noms dans les applications XAML chargées
- Portées de noms dans les styles et les modèles
- Portées de noms et API associées à des noms
- Rubriques connexes
Les portées de noms dans les applications XAML chargées
Les portées de noms sont créées sur l'élément racine pour une page XAML lorsque la page est traitée. Chaque nom spécifié dans la page est ajouté à une portée de nom pertinente. Les éléments qui sont des éléments racine courants (Page et Window, par exemple) contrôlent toujours une portée de nom. Si un élément, tel que FrameworkElement ou FrameworkContentElement, est l'élément racine de la page dans les balises, un processeur XAML ajoute alors implicitement une racine Page de sorte que le Page puisse fournir une portée de nom. Une portée de nom est créé même si aucun attribut Name ou x:Name n'est défini initialement dans le XAML.
Si vous essayez d'utiliser deux fois le même nom dans une portée de nom, une exception est déclenchée. Pour XAML qui contient du code-behind et qui fait partie d'une application compilée, cette exception est déclenchée lors de la création de la classe générée pour la page.
Ajout d'éléments aux arborescences d'éléments analysées
Toute addition à l'arborescence d'éléments après le chargement initial et le traitement doit appeler l'implémentation appropriée de RegisterName pour la classe qui définit la portée de nom. Sinon, l'objet ajouté ne peut pas être référencé par nom à l'aide de méthodes telles que FindName. La définition d'une propriété Name (ou x:Name, attribut) ne suffit pas pour enregistrer ce nom dans une portée de nom. L'ajout d'un élément nommé à une arborescence d'éléments qui a une portée de nom n'enregistre également pas le nom dans la portée de nom. Bien que des portées de noms puissent être imbriquées, vous enregistrez généralement des noms dans la portée de nom qui existe sur l'élément racine, afin que votre emplacement de portée de nom soit analogue à la portée de nom qui aurait été créée dans une page XAML chargée équivalente. Le plus souvent, les développeurs d'applications utiliseront RegisterName pour enregistrer des noms dans la portée de nom sur la racine actuelle. RegisterName fait partie d'un scénario important pour rechercher des tables de montage séquentiel qui s'exécuteront sous forme d'animations. Pour plus d'informations, consultez Vue d'ensemble des tables de montage séquentiel. Si vous appelez RegisterName sur un élément autre que l'élément racine dans la même arborescence logique, le nom est quand même enregistré à l'élément le plus proche de la racine, comme si vous aviez appelé RegisterName sur l'élément racine.
Portées de noms dans le code
Pour les applications créées par programme, plutôt qu'à partir de XAML chargé, l'élément racine doit implémenter INameScope ou est une classe dérivée FrameworkElement ou FrameworkContentElement, pour prendre en charge une portée de nom.
De même, pour tout élément qui n'est pas chargé et est traité par un processeur XAML, la portée de nom pour l'objet n'est pas créée ou initialisée par défaut. Vous devez créer explicitement une nouvelle portée de nom pour tout élément dans lequel vous envisagez d'enregistrer des noms par la suite. Pour créer une portée de nom pour un élément, vous appelez la méthode statique SetNameScope. Spécifiez l'élément comme paramètre dependencyObject et un nouvel appel de constructeur NameScope comme paramètre value.
Si l'objet a fourni comme dependencyObject pour SetNameScope n'est pas une implémentation INameScope, FrameworkElement ou FrameworkContentElement, alors l'appel de RegisterName sur tout élément enfant n'aura aucun effet. Si vous ne parvenez pas à créer la nouvelle portée de nom explicitement, des appels à RegisterName déclencheront une exception.
Pour obtenir un exemple de l'utilisation d'une portée de nom API dans du code, consultez Comment : définir une portée de nom.
Portées de noms dans les styles et les modèles
Les styles et les modèles dans WPF permettent de réutiliser et d'appliquer de nouveau le contenu simplement, mais les styles et les modèles peuvent aussi inclure des éléments avec des noms définis au niveau du modèle. Ce même modèle peut être utilisé plusieurs fois dans une page. Pour cette raison, les styles et les modèles définissent tous les deux leurs propres portées de noms, indépendamment de la page conteneur où le style ou le modèle est appliqué.
Prenons l'exemple suivant :
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
>
<Page.Resources>
<ControlTemplate x:Key="MyButtonTemplate" TargetType="{x:Type Button}">
<Border BorderBrush="Red" Name="TheBorder" BorderThickness="2">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Page.Resources>
<StackPanel>
<Button Template="{StaticResource MyButtonTemplate}">My first button</Button>
<Button Template="{StaticResource MyButtonTemplate}">My second button</Button>
</StackPanel>
</Page>
Ici, le même modèle est appliqué à deux boutons différents. Si les modèles n'avaient pas des portées de noms distinctes, le nom TheBorder utilisé dans le modèle provoquerait un conflit de nom. Chaque instanciation du modèle a sa propre portée de nom, c'est pourquoi dans cet exemple la portée de nom de chaque modèle instancié contiendrait exactement un nom.
Les styles obtiennent également leur propre portée de nom, essentiellement afin que les parties des tables de montage séquentiel puissent avoir des noms particuliers assignés. Ces noms activent des comportements spécifiques des contrôles qui cibleront des éléments de ce nom, même si le modèle a été redéfini dans le cadre de la personnalisation de contrôle.
À cause des portées de noms séparées, la recherche d'éléments nommés dans un modèle est plus difficile plus que la recherche d'un élément non basé sur des modèles dans une page. Vous devez tout d'abord déterminer le modèle appliqué en obtenant la valeur de propriété Template du contrôle où le modèle est appliqué. Vous appelez ensuite la version de modèle de FindName, en passant le contrôle où le modèle a été appliqué comme deuxième paramètre.
Si vous êtes auteur de contrôle et que vous générez une convention où un élément nommé particulier dans un modèle appliqué est la cible pour un comportement défini par le contrôle lui-même, vous pouvez utiliser la méthode GetTemplateChild de votre code d'implémentation de contrôle. La méthode GetTemplateChild est protégée, de sorte que seul l'auteur du contrôle y a accès.
Si vous travaillez à partir d'un modèle, et que vous devez récupérer la portée de nom où le modèle est appliqué, récupérez TemplatedParent, puis appelez FindName. Un exemple de travail dans le modèle serait si vous écrivez l'implémentation du gestionnaire d'événements où l'événement sera déclenché à partir d'un élément dans un modèle appliqué.
Portées de noms et API associées à des noms
FrameworkElement a FindName, RegisterName et les méthodes UnregisterName. Si l'élément sur lequel vous appelez ces méthodes possède une portée de nom, les méthodes d'éléments appellent simplement dans les méthodes de portées de noms. Sinon, l'élément parent est vérifié pour voir s'il possède une portée de nom, et ce processus se poursuit de manière récursive jusqu'à ce qu'une portée de nom soit trouvée (du fait du comportement du processeur XAML, il est certain qu'une portée de nom existera à la racine). FrameworkContentElement a des comportements analogues, si ce n'est qu'aucun FrameworkContentElement ne possédera jamais une portée de nom. Les méthodes existent sur FrameworkContentElement afin que les appels puissent être envoyés par la suite à un élément parent FrameworkElement.
SetNameScope est utilisé pour mapper une nouvelle portée de nom à un objet existant. Vous pouvez appeler SetNameScope plusieurs fois pour réinitialiser ou effacer la portée de nom, mais ce n'est pas d'usage courant. De même, GetNameScope n'est pas généralement utilisé à partir du code.
Implémentations des portées de noms
Les classes suivantes implémentent INameScope directement :
ResourceDictionary n'utilise pas de portées de noms ; il utilise à la place des clés, parce qu'il s'agit d'une implémentation de table de hachage de dictionnaire. La seule raison pour laquelle ResourceDictionary implémente INameScope est pour qu'il puisse déclencher des exceptions au code utilisateur qui aident à clarifier la distinction entre une véritable portée de nom et comment un ResourceDictionary gère des clés, et également pour s'assurer que les portées de noms ne sont particulièrement pas appliquées à un ResourceDictionary par des éléments parents.
FrameworkTemplate et Style implémentent INameScope via des définitions d'interface explicites. Les implémentations explicites permettent à ces portées de noms de se comporter de manière conventionnelle lorsqu'on y accède via l'interface INameScope, ce qui constitue la manière dont les portées de noms sont communiquées par les processus internes WPF. Mais les définitions d'interface explicites ne font pas partie de la surface API conventionnelle de FrameworkTemplate et de Style, car vous avez rarement à appeler directement les méthodes INameScope sur FrameworkTemplate et Style.
Les classes suivantes définissent leurs propres portées de noms, en utilisant la classe d'assistance System.Windows.NameScope et en se connectant à son implémentation de portée de nom à travers la propriété NameScope attachée :
Voir aussi
Concepts
Espaces de noms XAML et mappage d'espace de noms