WPF-Namescopes

Aktualisiert: November 2007

Bei Namescopes handelt es sich sowohl um ein Konzept als auch um die Programmierobjekte, in denen Beziehungen zwischen den definierten XAML-Namen von Objekten und ihren entsprechenden Instanzen gespeichert sind. Namescopes im verwalteten WPF-Code werden beim Laden der Seiten für eine XAML-Anwendung erstellt. Namescopes als Programmierobjekte werden von der INameScope-Schnittstelle definiert und zudem von der praktischen Klasse NameScope implementiert.

Dieses Thema enthält folgende Abschnitte.

  • Namescopes in geladenen XAML-Anwendungen
  • Namescopes in Stilen und Vorlagen
  • Namescopes und namensbezogene APIs
  • Verwandte Abschnitte

Namescopes in geladenen XAML-Anwendungen

Namescopes werden auf dem Stammelement für eine XAML-Seite erstellt, wenn die Seite verarbeitet wird. Jeder in der Seite angegebene Name wird zu einem entsprechenden Namescope hinzugefügt. Elemente, bei denen es sich um allgemeine Stammelemente (z. B. Page und Window) handelt, steuern immer einen Namescope. Wenn ein Element wie FrameworkElement oder FrameworkContentElement das Stammelement der Seite in Markup bildet, fügt ein XAML-Prozessor implizit einen Page-Stamm hinzu, sodass die Page einen Namescope bereitstellen kann. Ein Namescope wird auch dann erstellt, wenn zunächst kein Name- oder x:Name-Attribut im XAML definiert wurde.

Wenn Sie versuchen, denselben Namen zweimal in einem Namescope zu verwenden, wird eine Ausnahme ausgelöst. Für XAML, das Code-Behind enthält und Teil einer kompilierten Anwendung ist, wird diese Ausnahme ausgelöst, wenn die generierte Klasse für die Seite erstellt wird.

Hinzufügen von Elementen zu analysierten Elementstrukturen

Alle nach dem anfänglichen Laden und Verarbeiten zur Elementstruktur hinzugefügten Elemente müssen die entsprechende Implementierung von RegisterName für die Klasse aufrufen, die den Namescope definiert. Andernfalls kann durch Methoden wie FindName nicht über den Namen auf das hinzugefügte Objekt verwiesen werden. Allein durch Festlegen einer Name-Eigenschaft (oder x:Name-Attribut) wird ein Name noch nicht in einem Namescope registriert. Auch durch Hinzufügen eines benannten Elements zu einer Elementstruktur, die über einen Namescope verfügt, wird der Name nicht in diesem Namescope registriert. Obwohl Namescopes geschachtelt werden können, werden Namen in der Regel in den Namescopes registriert, die sich auf dem Stammelement befinden, sodass die Namescopeposition dem Namescope entspricht, der in einer entsprechenden geladenen XAML-Seite erstellt worden wäre. Das häufigste Szenario bei der Anwendungsentwicklung stellt die Registrierung von Namen im Namescope auf dem aktuellen Stamm mithilfe von RegisterName dar. RegisterName ist Teil eines wichtigen Szenarios zum Suchen nach Storyboards, die als Animationen ausgeführt werden. Weitere Informationen finden Sie unter Übersicht über Storyboards. Wenn Sie RegisterName für ein anderes Element als das Stammelement in derselben logischen Struktur aufrufen, wird der Name dennoch in dem Element registriert, das dem Stamm am nächsten liegt, als ob Sie RegisterName für das Stammelement aufgerufen hätten.

Namescopes in Code

Für Anwendungen, die programmgesteuert und nicht mithilfe eines geladenen XAML erstellt werden, muss das Stammelement INameScope implementieren oder eine abgeleitete FrameworkElement- oder FrameworkContentElement-Klasse sein, um einen Namescope zu unterstützen.

Zudem wird für jedes Element, das nicht von einem XAML-Prozessor geladen und verarbeitet wird, der Namescope für das Objekt nicht standardmäßig erstellt oder initialisiert. Sie müssen einen neuen Namescope für jedes Element explizit erstellen, in dem anschließend Namen registriert werden sollen. Um für ein Element einen Namescope zu erstellen, rufen Sie die statische SetNameScope-Methode auf. Geben Sie das Element als dependencyObject-Parameter und einen neuen NameScope-Konstruktoraufruf als value-Parameter an.

Wenn es sich bei dem als dependencyObject für SetNameScope bereitgestellten Objekt nicht um eine INameScope-Implementierung, ein FrameworkElement oder ein FrameworkContentElement handelt, dann hat das Aufrufen von RegisterName für untergeordnete Elemente keine Auswirkungen. Wenn Sie den neuen Namescope nicht explizit erstellen, wird durch Aufrufe von RegisterName eine Ausnahme ausgelöst.

Ein Beispiel zur Verwendung von Namescope-APIs in Code finden Sie unter Gewusst wie: Definieren eines Namensbereichs.

Namescopes in Stilen und Vorlagen

Mithilfe von Stilen und Vorlagen in WPF können Sie Inhalt auf einfache Weise wiederverwenden und erneut anwenden, Stile und Vorlagen können jedoch auch Elemente mit Namen enthalten, die auf Vorlagenebene definiert wurden. Dieselbe Vorlage kann in einer Seite mehrmals verwendet werden. Deshalb definieren Stile und Vorlagen ihre eigenen Namescopes, und zwar unabhängig von der Seite, auf die der Stil oder die Vorlage angewendet wird.

Betrachten Sie das folgende Beispiel:

<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>

Hier wird dieselbe Vorlage auf zwei verschiedene Schaltflächen angewendet. Wenn Vorlagen keine diskreten Namescope enthalten würden, würde der in der Vorlage verwendete TheBorder-Name einen Namenskonflikt auslösen. Jede Instanziierung der Vorlage verfügt über einen eigenen Namescope, sodass in diesem Beispiel der Namescope jeder instanziierten Vorlage genau einen Namen enthält.

Auch Stile haben einen eigenen Namescope, und zwar in der Regel so, dass Teilen von Storyboards bestimmte Namen zugewiesen werden können. Diese Namen ermöglichen steuerelementspezifische Verhalten, die sich auf Elemente mit diesem Namen beziehen, selbst wenn die Vorlage im Zusammenhang mit einer Anpassung von Steuerelementen neu definiert wurde.

Aufgrund der separaten Namescopes ist die Suche nach benannten Elementen in einer Vorlage schwieriger als die Suche nach einem nicht auf einer Vorlage basierenden Element in einer Seite. Sie müssen die angewendete Vorlage zunächst durch Abrufen des Template-Eigenschaftenwerts des Steuerelements festlegen, auf das die Vorlage angewendet wird. Anschließend rufen Sie die Vorlagenversion von FindName auf und übergeben das Steuerelement, auf das die Vorlage als zweiter Parameter angewendet wurde.

Wenn Sie Autor eines Steuerelements sind und eine Konvention erstellen, in der ein bestimmtes benanntes Element in einer angewendeten Vorlage das Ziel für ein Verhalten darstellt, das vom Steuerelement selbst definiert wird, können Sie die GetTemplateChild-Methode aus dem Code zur Implementierung des Steuerelements verwenden. Die GetTemplateChild-Methode ist geschützt, sodass nur der Steuerelementautor Zugriff darauf hat.

Wenn Sie in einer Vorlage arbeiten und den Namescope abrufen müssen, auf den die Vorlage angewendet wird, rufen Sie TemplatedParent ab, und rufen Sie dort dann FindName auf. Ein Beispiel für das Arbeiten innerhalb der Vorlage wäre das Schreiben der Ereignishandlerimplementierung, wobei das Ereignis von einem Element in einer angewendeten Vorlage ausgelöst wird.

Namescopes und namensbezogene APIs

FrameworkElement verfügt über FindName-, RegisterName- und UnregisterName-Methoden. Falls das Element, für das diese Methoden aufgerufen werden, über einen Namescope verfügt, werden die Elementmethoden einfach in den Namescopemethoden aufgerufen. Andernfalls wird das übergeordnete Element daraufhin überprüft, ob es über einen Namescope verfügt, und dieser Vorgang wird rekursiv fortgesetzt, bis ein Namescope gefunden wird (aufgrund des Verhaltens des XAML-Prozessors muss sich am Stamm ein Namescope befinden). FrameworkContentElement hat analoge Verhalten, wobei jedoch ein FrameworkContentElement niemals über einen Namescope verfügt. Die Methoden befinden sich auf FrameworkContentElement, sodass die Aufrufe schließlich an ein übergeordnetes FrameworkElement-Element weitergeleitet werden können.

Mit SetNameScope wird einem vorhandenen Objekt ein neuer Namescope zugeordnet. Sie können SetNameScope mehrmals aufrufen, um den Namescope zurückzusetzen oder zu löschen, diese Verwendung ist jedoch nicht üblich. Auch GetNameScope wird in der Regel nicht über Code verwendet.

Namescope-Implementierungen

Die folgenden Klassen implementieren INameScope direkt:

ResourceDictionary verwendet Schlüssel anstelle von Namescopes, da es die Implementierung einer Wörterbuch-Hashtabelle ist. ResourceDictionary implementiert INameScope nur deshalb, um Ausnahmen für Benutzercode auslösen zu können, die den Unterschied zwischen einem wahren Namescope und der Verwendung von Schlüsseln durch ResourceDictionary verdeutlichen, sowie um sicherzustellen, dass Namescopes nicht durch übergeordnete Elemente auf ein ResourceDictionary angewendet werden.

FrameworkTemplate und Style implementieren INameScope durch explizite Schnittstellendefinitionen. Die expliziten Implementierungen ermöglichen ein konventionelles Verhalten dieser Namescopes, wenn über die INameScope-Schnittstelle auf sie zugegriffen wird. Auf diese Weise werden Namescopes durch interne WPF-Prozesse übermittelt. Die expliziten Schnittstellendefinitionen sind jedoch nicht Teil der konventionellen API-Schnittstelle von FrameworkTemplate und Style, da die INameScope-Methoden für FrameworkTemplate und Style nur selten direkt aufgerufen werden müssen.

Folgende Klassen definieren ihren eigenen Namescope, indem sie die System.Windows.NameScope-Hilfsklasse verwenden und über die angefügte NameScope-Eigenschaft eine Verbindung zur Namescope-Implementierung herstellen:

Siehe auch

Konzepte

XAML-Namespaces und Namespacezuordnung

Referenz

x:Name-Attribut