WPF-XAML-Namescopes

XAML-Namescopes sind ein Konzept, das Objekte bezeichnet, die in XAML definiert sind. Die Namen in einem XAML-Namescope können verwendet werden, um Beziehungen zwischen den XAML-definierten Namen der Objekte und ihren Entsprechungen in einer Objektstruktur herzustellen. In der Regel werden XAML-Namescopes in verwaltetem WPF-Code beim Laden der einzelnen XAML Seitenstämme für XAML-Anwendungen erstellt. Als Programmierobjekte sind XAML-Namescopes durch die INameScope-Schnittstelle definiert und in der Praxis durch die Klasse NameScope implementiert.

Namescopes in geladenen XAML-Anwendungen

Im weiter gefassten Kontext der Programmierung oder der Informatik beinhalten Programmierkonzepte oft einen eindeutigen Bezeichner oder Namen, der für den Objektzugriff verwendet werden kann. Für Systeme, die Bezeichner oder Namen verwenden, definiert der Namescope die Grenzen, innerhalb derer ein Prozess oder eine Technik sucht, wenn ein Objekt mit diesem Namen angefordert wird, oder die Grenzen, innerhalb derer die Eindeutigkeit von identifizierenden Namen erzwungen wird. Diese allgemeinen Prinzipien gelten für XAML-Namescopes. In WPF werden XAML-Namescopes für das Stammelement einer XAML-Seite erstellt, wenn die Seite geladen wird. Jeder in der XAML-Seite angegebene Name wird, vom Stammelement ausgehend, einem entsprechenden XAML-Namescope hinzugefügt.

In WPF-XAML steuern Elemente, die gemeinsame Stammelemente sind (z. B. Page und Window), immer einen XAML-Namescope. Wenn ein Element wie FrameworkElement oder FrameworkContentElement das Stammelement der Seite im Markup ist, fügt ein XAML-Prozessor implizit einen Page-Stamm hinzu, damit das Page-Element einen funktionierenden XAML-Namescope bereitstellen kann.

Hinweis

WPF-Buildvorgänge erstellen für eine XAML-Produktion selbst dann einen XAML-Namescope, wenn für kein Element im XAML-Markup Name- oder x:Name-Attribute definiert sind.

Wenn Sie versuchen, denselben Namen in einem beliebigen XAML-Namescope zweimal zu verwenden, wird eine Ausnahme ausgelöst. Für WPF-XAML, das CodeBehind verwendet und Teil einer kompilierten Anwendung ist, wird die Ausnahme während der Erstellung durch WPF-Buildaktionen ausgelöst, wenn die generierende Klasse für die Seite während der Kompilierung des ausgehenden Markups erstellt wird. Für XAML, dessen Markup nicht durch Buildaktionen kompiliert wird, können XAML-Namescope-bezogene Ausnahmen während des Ladens des XAML ausgelöst werden. XAML-Designer können XAML-Namescope-Probleme auch zur Entwurfszeit vorausschauend behandeln.

Hinzufügen von Objekten zu Laufzeit-Objektstrukturen

Der Zeitpunkt, an dem das XAML analysiert wird, stellt den Zeitpunkt dar, an dem ein WPF-XAML-Namescope erstellt und definiert wird. Wenn Sie ein Objekt zu einer Objektstruktur hinzufügen, nachdem der die Struktur erzeugende XAML-Code analysiert wurde, wird ein Name- oder x:Name-Wert für das neue Objekt nicht automatisch die Informationen in einem XAML-Namescope aktualisieren. Um einen Namen für ein Objekt in einem WPF-XAML-Namescope hinzuzufügen, nachdem das XAML geladen wurde, müssen Sie die entsprechende Implementierung von RegisterName für das Objekt aufrufen, das den XAML-Namescope definiert, wobei es sich in der Regel um das Stammelement der XAML-Seite handelt. Wenn der Name nicht registriert ist, kann das hinzugefügte Objekt nicht anhand des Namens durch Methoden wie FindName referenziert werden, und Sie können diesen Namen nicht als Ziel für Animationen verwenden.

Das häufigste Szenario für Entwickler wird sein, dass Sie mithilfe von RegisterName Namen im XAML-Namescope für das aktuelle Stammelement der Seite registrieren. RegisterName ist Teil eines wichtigen Szenarios für Storyboards, die auf Objekte für Animationen abzielen. Weitere Informationen finden Sie unter Übersicht über Storyboards.

Wenn Sie RegisterName für ein anderes als das den XAML-Namescope definierende Objekt aufrufen, dann ist dieser Name immer noch in dem XAML-Namescope registriert, der das aufrufende Objekt enthält, so, als hätten Sie RegisterName für das den Namescope definierende Objekt aufgerufen.

XAML Namescopes im Code

Sie können XAML-Namescopes im Code erstellen und anschließend verwenden. Die an der XAML-Namescope beteiligten APIs und Konzepte sind auch bei reiner Codeverwendung dieselben, da der XAML-Prozessor von WPF diese APIs und Konzepte beim Verarbeiten des XAML selbst verwendet. Die Aufgaben dieser Konzepte und -APIs sind hauptsächlich, Objekte anhand des Namens innerhalb einer Objektstruktur zu finden, die in der Regel teilweise oder vollständig in XAML definiert ist.

Für Anwendungen, die programmgesteuert und nicht aus geladenem XAML erstellt werden, muss das Objekt, das einen XAML-Namescope definiert, INameScope implementieren oder eine von FrameworkElement oder FrameworkContentElement abgeleitete Klasse sein, um die Erstellung eines XAML-Namescope für seine Instanzen zu unterstützen.

Ebenso gilt, das bei jedem Element, das nicht von einem XAML-Prozessor geladen und verarbeitet wird, der XAML-Namescope für das Objekt standardmäßig nicht erstellt oder initialisiert wird. Sie müssen explizit für jedes Objekt einen neuen XAML-Namescope erstellen, für das Sie anschließend Namen registrieren möchten. Um einen XAML-Namescope zu erstellen, rufen Sie die statische SetNameScope-Methode auf. Geben Sie das Objekt, das als Besitzer fungieren wird, als dependencyObject-Parameter an und einen neuen NameScope-Konstruktoraufruf als value-Parameter.

Wenn das als dependencyObject für SetNameScope bereitgestellte Objekt keine INameScope-Implementierung, FrameworkElement oder FrameworkContentElement ist, hat der Aufruf von RegisterName für untergeordnete Elemente keine Auswirkungen. Wenn die explizite Erstellung des neuen XAML-Namescope fehlschlägt, werden Aufrufe von RegisterName eine Ausnahme auslösen.

Ein Beispiel der Verwendung von XAML-Namescope-APIs in Code finden Sie unter Definieren eines Namensbereichs.

XAML-Namescopes in Stilen und Vorlagen

Stile und Vorlagen in WPF bieten die Möglichkeit, Inhalte auf einfache Weise wiederzuverwenden und sie erneut anzuwenden. Allerdings können Stile und Vorlagen auch Elemente mit XAML-Namen, die auf der Vorlagenebene definiert sind, enthalten. Dieselbe Vorlage wird möglicherweise mehrmals auf einer Seite verwendet. Aus diesem Grund definieren Stile und Vorlagen ihre eigenen XAML-Namescopes, unabhängig davon, an welcher Position in einer Objektstruktur der Stil oder die Vorlage angewendet wird.

Betrachten Sie das folgenden Beispiel:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://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>

Hier wird dieselbe Vorlage auf zwei verschiedene Schaltflächen angewendet. Hätten Vorlagen keine diskreten XAML-Namensbereiche, würde der in der Vorlage verwendete TheBorder-Name einen Namenskonflikt im XAML-Namescope verursachen. Jede Vorlageninstanz hat ihren eigenen XAML-Namescope, weshalb in diesem Beispiel der Namescope jeder Instanz der Vorlage genau einen Namen enthält.

Auch Stile definieren ihre eigenen XAML-Namensbereich. Dies dient vor allem dazu, dass Teilen von Storyboards bestimmte Namen zugewiesen werden können. Diese Namen ermöglichen steuerelementspezifische Verhalten, die auf Elemente dieses Namens selbst dann angewendet werden, wenn die Vorlage als Teil einer Anpassung von Steuerelementen neu definiert wurde.

Aufgrund der separaten XAML-Namescopes ist die Suche nach benannten Elementen in einer Vorlage schwieriger, als die Suche nach einem ohne Vorlage benannten Element in einer Seite. Sie müssen zuerst die angewendete Vorlage durch Abrufen des Template-Eigenschaftswerts des Steuerelements, in dem die Vorlage angewendet wird, bestimmen. Danach rufen Sie die Vorlagenversion von FindName auf und übergeben das Steuerelement, auf das die Vorlage als zweiter Parameter angewendet wurde.

Wenn Sie Autor von Steuerelementen sind und eine Konvention erstellen, nach der ein bestimmtes benanntes Element in einer angewendeten Vorlage Ziel für ein Verhalten ist, das vom Steuerelement selbst definiert wird, können Sie dafür die GetTemplateChild-Methode aus dem Code der Steuerelementimplementierung verwenden. Die GetTemplateChild-Methode ist geschützt, sodass nur der Steuerelementautor Zugriff darauf besitzt.

Wenn Sie innerhalb einer Vorlage arbeiten und zum XAML-Namescope gelangen müssen, in dem die Vorlage angewendet wird, rufen Sie den Wert von TemplatedParent ab und dort dann FindName auf. Ein Beispiel für das Arbeiten innerhalb der Vorlage wäre, wenn Sie einen Ereignis-Handler dort implementieren möchten, wo das Ereignis von einem Element in einer angewendeten Vorlage ausgelöst wird.

FrameworkElement verfügt über die FindName-, RegisterName- und UnregisterName-Methoden. Wenn das Objekt, für das Sie diese Methoden aufrufen, einen XAML-Namescope besitzt, rufen die Methoden die Methoden des entsprechenden XAML-Namescopes auf. Andernfalls wird das übergeordnete Element daraufhin überprüft, ob es einen XAML-Namescope besitzt. Dieser Vorgang wird rekursiv fortgesetzt, bis ein XAML-Namescope gefunden wird (wobei Erfolg garantiert ist, da aufgrund des Verhaltens des XAML-Prozessors das Stammelement immer einen XAML-Namescope hat). FrameworkContentElement verhält sich analog, mit der Ausnahme, dass kein FrameworkContentElement jemals Besitzer eines XAML-Namescopes sein wird. Die Methoden sind für FrameworkContentElement vorhanden, sodass die Aufrufe gegebenenfalls an ein übergeordnetes FrameworkElement-Element weitergeleitet werden können.

SetNameScope wird verwendet, um einen neuen XAML-Namescope einem vorhandenen Objekt zuzuordnen. Sie können SetNameScope mehrmals aufrufen, um den XAML-Namescope zu bereinigen oder zurückzusetzen, aber das ist keine übliche Vorgehensweise. Außerdem wird GetNameScope normalerweise nicht im Code verwendet.

Implementierungen von XAML-Namescopes

Die folgenden Klassen implementieren INameScope direkt:

ResourceDictionary verwendet keine XAML-Namen oder -Namescopes. Stattdessen werden Schlüssel verwendet, da es sich um die Implementierung eines Wörterbuchs handelt. Der einzige Grund, warum ResourceDictionaryINameScope implementiert, besteht darin, dass Ausnahmen für den Benutzercode ausgelöst werden können, um den Unterschied zwischen einem echten XAML-Namescope und der Verarbeitung von Schlüsseln durch ResourceDictionary zu verdeutlichen, und um sicherzustellen, dass XAML-Namescopes nicht von übergeordneten Elementen auf ResourceDictionary angewendet werden.

FrameworkTemplate und Style implementieren INameScope durch explizite Schnittstellendefinitionen. Die expliziten Implementierungen ermöglichen es diesen XAML-Namescopes, sich konventionell zu verhalten, wenn auf sie über die INameScope-Schnittstelle zugegriffen wird, welches die Art und Weise ist, wie XAML-Namescopes durch WPF-interne Prozesse kommuniziert werden. Die expliziten Schnittstellendefinitionen sind jedoch nicht Teil der konventionellen API-Oberfläche von FrameworkTemplate und Style, da Sie nur selten die INameScope-Methoden für FrameworkTemplate und Style direkt aufrufen müssen und stattdessen andere APIs wie GetTemplateChild verwenden würden.

Die folgenden Klassen definieren ihren eigenen XAML-Namescope, indem sie die Hilfsklasse System.Windows.NameScope verwenden und eine Verbindung mit ihrer XAML-Namescopeimplementierung über die angefügte NameScope.NameScope-Eigenschaft herstellen:

Weitere Informationen