Namescopes XAML
Un namescope XAML stocke les relations entre les noms définis par XAML des objets et leurs équivalents d’instance. Ce concept est similaire à la signification plus large du terme namescope dans d’autres langages de programmation et technologies.
Définition des namescopes XAML
Les noms dans les portées de noms XAML permettent au code utilisateur de référencer les objets qui ont été initialement déclarés en XAML. Le résultat interne de l’analyse XAML est que le runtime crée un ensemble d’objets qui conservent certaines ou toutes les relations que ces objets avaient dans les déclarations XAML. Ces relations sont conservées en tant que propriétés d’objet spécifiques des objets créés ou exposées à des méthodes utilitaires dans les API du modèle de programmation.
L’utilisation la plus classique d’un nom dans un namescope XAML est une référence directe à une instance d’objet, qui est activée par le passage de compilation de balisage en tant qu’action de génération de projet, combinée à une méthode InitializeComponent générée dans les modèles de classes partielles.
Vous pouvez également utiliser la méthode utilitaire FindName vous-même au moment de l’exécution pour renvoyer une référence aux objets définis avec un nom dans le balisage XAML.
En savoir plus sur les actions de génération et XAML
Ce qui se passe techniquement est que le code XAML lui-même subit une passe de compilateur de balisage en même temps que le code XAML et la classe partielle qu’il définit pour code-behind sont compilés ensemble. Chaque élément d’objet avec un attribut Name ou x :Name défini dans le balisage génère un champ interne avec un nom qui correspond au nom XAML. Ce champ est initialement vide. Ensuite, la classe génère une méthode InitializeComponent appelée uniquement une fois que tout le code XAML est chargé. Dans la logique InitializeComponent , chaque champ interne est ensuite renseigné avec la valeur de retour FindName pour la chaîne de nom équivalente. Vous pouvez observer cette infrastructure vous-même en examinant les fichiers .g » (générés) créés pour chaque page XAML dans le sous-dossier /obj d’un projet d’application Windows Runtime après la compilation. Vous pouvez également voir les champs et la méthode InitializeComponent en tant que membres de vos assemblys résultants si vous réfléchissez sur eux ou examinez le contenu de leur langage d’interface.
Remarque Plus précisément pour les applications d’extensions de composant Visual C++ (C++/CX), un champ de stockage pour une référence x :Name n’est pas créé pour l’élément racine d’un fichier XAML. Si vous devez référencer l’objet racine à partir de C++/CX code-behind, utilisez d’autres API ou traversées d’arborescences. Par exemple, vous pouvez appeler FindName pour un élément enfant nommé connu, puis appeler Parent.
Création d’objets au moment de l’exécution avec XamlReader.Load
Xaml peut également être utilisé comme entrée de chaîne pour la méthode XamlReader.Load , qui agit de manière analogue à l’opération d’analyse de source XAML initiale. XamlReader.Load crée une arborescence déconnectée d’objets au moment de l’exécution. L’arborescence déconnectée peut ensuite être attachée à un point sur l’arborescence d’objets principale. Vous devez connecter explicitement votre arborescence d’objets créée, soit en l’ajoutant à une collection de propriétés de contenu telle que Children, soit en définissant une autre propriété qui prend une valeur d’objet (par exemple, en chargeant une nouvelle imageBrush pour une valeur de propriété Fill).
Implications de la portée de noms XAML de XamlReader.Load
Le namescope XAML préliminaire défini par la nouvelle arborescence d’objets créée par XamlReader.Load évalue tous les noms définis dans le code XAML fourni pour l’unicité. Si les noms dans le code XAML fourni ne sont pas uniques en interne à ce stade, XamlReader.Load lève une exception. L’arborescence d’objets déconnectée ne tente pas de fusionner son namescope XAML avec la portée de noms XAML de l’application principale, si ou lorsqu’elle est connectée à l’arborescence d’objets d’application principale. Une fois que vous avez connecté les arborescences, votre application a une arborescence d’objets unifiée, mais cette arborescence comporte des portées de noms XAML discrètes. Les divisions se produisent au niveau des points de connexion entre les objets, où vous définissez une propriété sur la valeur retournée par un appel XamlReader.Load .
La complication d’avoir des portées de noms XAML discrètes et déconnectées est que les appels à la méthode FindName ainsi que les références d’objets managées directes ne fonctionnent plus sur un namescope XAML unifié. Au lieu de cela, l’objet particulier sur lequel FindName est appelé implique l’étendue, avec l’étendue étant le namescope XAML dans lequel l’objet appelant se trouve. Dans le cas de référence d’objet managé direct, l’étendue est implicite par la classe où le code existe. En règle générale, le code-behind pour l’interaction au moment de l’exécution d’une « page » du contenu de l’application existe dans la classe partielle qui appuie la racine « page », et par conséquent, le namescope XAML est le namescope XAML racine.
Si vous appelez FindName pour obtenir un objet nommé dans le namescope XAML racine, la méthode ne trouve pas les objets d’un namescope XAML discret créé par XamlReader.Load. À l’inverse, si vous appelez FindName à partir d’un objet obtenu à partir du namescope XAML discret, la méthode ne trouve pas d’objets nommés dans le namescope XAML racine.
Ce problème de namescope XAML discret affecte uniquement la recherche d’objets par nom dans les portées de noms XAML lors de l’utilisation de l’appel FindName .
Pour obtenir des références à des objets définis dans un autre namescope XAML, vous pouvez utiliser plusieurs techniques :
- Parcourez l’arborescence entière en étapes discrètes avec les propriétés Parent et/ou collection connues pour exister dans votre arborescence d’objets (par exemple, la collection retournée par Panel.Children).
- Si vous appelez à partir d’un namescope XAML discret et souhaitez que le namescope XAML racine soit toujours facile à obtenir une référence à la fenêtre principale actuellement affichée. Vous pouvez obtenir la racine visuelle (l’élément XAML racine, également appelé source de contenu) à partir de la fenêtre d’application actuelle dans une ligne de code avec l’appel
Window.Current.Content
. Vous pouvez ensuite effectuer un cast vers FrameworkElement et appeler FindName à partir de cette étendue. - Si vous appelez à partir du namescope XAML racine et souhaitez un objet dans un namescope XAML discret, la meilleure chose à faire est de planifier à l’avance dans votre code et de conserver une référence à l’objet retourné par XamlReader.Load , puis ajouté à l’arborescence d’objets principale. Cet objet est maintenant un objet valide pour appeler FindName dans le namescope XAML discret. Vous pouvez conserver cet objet disponible en tant que variable globale ou le transmettre à l’aide de paramètres de méthode.
- Vous pouvez éviter les noms et les considérations relatives à la portée de noms XAML entièrement en examinant l’arborescence visuelle. L’API VisualTreeHelper vous permet de parcourir l’arborescence visuelle en termes d’objets parents et de collections enfants, en fonction uniquement de la position et de l’index.
Étendues de noms XAML dans les modèles
Les modèles en XAML permettent de réutiliser et de réappliquer du contenu de manière simple, mais les modèles peuvent également 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 modèles définissent leurs propres portées de noms XAML, indépendamment de la page contenante où le style ou le modèle est appliqué. Prenons cet exemple :
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Page.Resources>
<ControlTemplate x:Key="MyTemplate">
....
<TextBlock x:Name="MyTextBlock" />
</ControlTemplate>
</Page.Resources>
<StackPanel>
<SomeControl Template="{StaticResource MyTemplate}" />
<SomeControl Template="{StaticResource MyTemplate}" />
</StackPanel>
</Page>
Ici, le même modèle est appliqué à deux contrôles différents. Si les modèles n’avaient pas de portées de noms XAML discrètes, le nom « MyTextBlock » utilisé dans le modèle entraînerait une collision de noms. Chaque instanciation du modèle a sa propre portée de nom XAML. Ainsi, dans cet exemple, la portée de nom XAML de chaque modèle instancié contient un et un seul nom. Toutefois, le namescope XAML racine ne contient pas le nom de l’un ou l’autre modèle.
En raison des portées de noms XAML distinctes, la recherche d’éléments nommés dans un modèle à partir de l’étendue de la page dans laquelle le modèle est appliqué nécessite une technique différente. Au lieu d’appeler FindName sur un objet dans l’arborescence d’objets, vous obtenez d’abord l’objet dont le modèle est appliqué, puis appelez GetTemplateChild. Si vous êtes un 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 d’un comportement défini par le contrôle lui-même, vous pouvez utiliser la méthode GetTemplateChild à partir de votre code d’implémentation de contrôle. La méthode GetTemplateChild est protégée. Seul l’auteur du contrôle y a accès. En outre, il existe des conventions que les auteurs de contrôle doivent suivre pour nommer des parties et des parties de modèle et signaler ces éléments en tant que valeurs d’attribut appliquées à la classe de contrôle. Cette technique rend les noms des parties importantes détectables pour contrôler les utilisateurs qui souhaitent peut-être appliquer un autre modèle, ce qui doit remplacer les parties nommées afin de conserver les fonctionnalités de contrôle.