XAML-Namescopes

Ein XAML-NameScope speichert Beziehungen zwischen den XAML-definierten Namen von Objekten und deren Instanzentsprechungen. Dieses Konzept ähnelt der breiteren Bedeutung des Begriffs "NameScope" in anderen Programmiersprachen und -technologien.

Definieren von XAML-NameScopes

Mit Namen in XAML-NameScopes kann Der Benutzercode auf die Objekte verweisen, die ursprünglich in XAML deklariert wurden. Das interne Ergebnis der XAML-Analyse besteht darin, dass die Laufzeit einen Satz von Objekten erstellt, die einige oder alle Beziehungen beibehalten, die diese Objekte in den XAML-Deklarationen hatten. Diese Beziehungen werden als bestimmte Objekteigenschaften der erstellten Objekte beibehalten oder für Hilfsmethoden in den Programmiermodell-APIs verfügbar gemacht.

Die typischste Verwendung eines Namens in einem XAML-NameScope ist ein direkter Verweis auf eine Objektinstanz, die durch den Markupkompilierungsdurchlauf als Projektbuildaktion aktiviert wird, kombiniert mit einer generierten InitializeComponent-Methode in den partiellen Klassenvorlagen.

Sie können auch die Hilfsmethode FindName zur Laufzeit verwenden, um einen Verweis auf Objekte zurückzugeben, die mit einem Namen im XAML-Markup definiert wurden.

Weitere Informationen zu Buildaktionen und XAML

Technisch gesehen durchläuft der XAML-Code selbst einen Markupcompilerdurchlauf gleichzeitig, den der XAML-Code und die partielle Klasse, die sie für CodeBehind definiert, zusammen kompiliert werden. Jedes Objektelement mit einem im Markup definierten Attribut "Name" oder "x:Name" generiert ein internes Feld mit einem Namen, der dem XAML-Namen entspricht. Dieses Feld ist zunächst leer. Anschließend generiert die Klasse eine InitializeComponent-Methode , die nur aufgerufen wird, nachdem der gesamte XAML-Code geladen wurde. Innerhalb der InitializeComponent-Logik wird jedes interne Feld dann mit dem FindName-Rückgabewert für die entsprechende Namenszeichenfolge aufgefüllt. Sie können diese Infrastruktur selbst beobachten, indem Sie sich die ".g" (generierten) Dateien ansehen, die nach der Kompilierung für jede XAML-Seite im Unterordner "/obj" eines Windows-Runtime App-Projekts erstellt werden. Sie können die Felder und die InitializeComponent-Methode auch als Member der resultierenden Assemblys sehen, wenn Sie sie überschreiben oder deren Benutzeroberflächensprachinhalte auf andere Weise untersuchen.

Hinweis Speziell für Visual C++-Komponentenerweiterungen (C++/CX)-Apps wird kein Sicherungsfeld für einen x:Name-Verweis für das Stammelement einer XAML-Datei erstellt. Wenn Sie auf das Stammobjekt aus C++/CX-CodeBehind verweisen müssen, verwenden Sie andere APIs oder Struktur-Traversal. Sie können beispielsweise FindName für ein bekanntes benanntes untergeordnetes Element aufrufen und dann "Parent" aufrufen.

Erstellen von Objekten zur Laufzeit mit XamlReader.Load

XAML kann auch als Zeichenfolgeneingabe für die XamlReader.Load-Methode verwendet werden, die analog zum anfänglichen XAML-Quellanalysevorgang fungiert. XamlReader.Load erstellt zur Laufzeit eine neue getrennte Struktur von Objekten. Die getrennte Struktur kann dann an einen Punkt auf der Hauptobjektstruktur angefügt werden. Sie müssen die erstellte Objektstruktur explizit verbinden, indem Sie sie einer Inhaltseigenschaftsauflistung wie Children hinzufügen oder eine andere Eigenschaft festlegen, die einen Objektwert akzeptiert (z. B. laden sie einen neuen ImageBrush für einen Fill-Eigenschaftswert).

XAML-NameScope-Auswirkungen von XamlReader.Load

Der vorläufige XAML-NameScope, der von der neuen Objektstruktur definiert wird, die von XamlReader.Load erstellt wurde, wertet alle definierten Namen im bereitgestellten XAML-Code aus, um eindeutig zu sein. Wenn Namen im bereitgestellten XAML-Code zu diesem Zeitpunkt nicht intern eindeutig sind, löst XamlReader.Load eine Ausnahme aus. Die getrennte Objektstruktur versucht nicht, den XAML-NameScope der Hauptanwendung mit dem XAML-Hauptnamenscope der Anwendung zusammenzuführen, wenn oder wenn sie mit der Hauptobjektstruktur der Anwendung verbunden ist. Nachdem Sie die Strukturen verbunden haben, verfügt Ihre App über eine einheitliche Objektstruktur, diese Struktur verfügt jedoch über separate XAML-NameScopes. Die Divisionen treten an den Verbindungspunkten zwischen Objekten auf, bei denen Sie festlegen, dass eine Eigenschaft der von einem XamlReader.Load-Aufruf zurückgegebene Wert ist.

Die Komplikation bei diskreten und getrennten XAML-NameScopes besteht darin, dass Aufrufe der FindName-Methode sowie direkte verwaltete Objektverweise nicht mehr mit einem einheitlichen XAML-NameScope ausgeführt werden. Stattdessen impliziert das bestimmte Objekt, für das FindName aufgerufen wird, den Bereich, wobei der Bereich der XAML-NameScope ist, in dem sich das aufrufende Objekt befindet. Im Direkten Referenzfall für verwaltete Objekte wird der Bereich von der Klasse impliziert, in der der Code vorhanden ist. In der Regel ist der CodeBehind für die Laufzeitinteraktion einer "Seite" von App-Inhalten in der partiellen Klasse vorhanden, die das Stammelement "page" zurückgibt und daher der XAML-NameScope der XAML-Stammnamescope ist.

Wenn Sie FindName aufrufen, um ein benanntes Objekt im XAML-Stamm-NameScope abzurufen, findet die Methode die Objekte nicht aus einem einzelnen XAML-NameScope, der von XamlReader.Load erstellt wurde. Wenn Sie "FindName" aus einem Objekt aufrufen, das aus dem einzelnen XAML-NameScope abgerufen wurde, findet die Methode keine benannten Objekte im XAML-Stamm-NameScope.

Dieses einzelne XAML-NameScope-Problem wirkt sich nur auf das Suchen von Objekten nach Namen in XAML-NameScopes aus, wenn der FindName-Aufruf verwendet wird.

Um Verweise auf Objekte abzurufen, die in einem anderen XAML-NameScope definiert sind, können Sie verschiedene Techniken verwenden:

  • Führen Sie die gesamte Struktur in einzelnen Schritten mit übergeordneten und/oder Auflistungseigenschaften durch, die in Der Objektstruktur vorhanden sind (z. B. die von Panel.Children zurückgegebene Auflistung).
  • Wenn Sie einen einzelnen XAML-NameScope aufrufen und den XAML-Stamm-NameScope verwenden möchten, ist es immer einfach, einen Verweis auf das derzeit angezeigte Hauptfenster abzurufen. Sie können den visuellen Stamm (das STAMM-XAML-Element, auch als Inhaltsquelle bezeichnet) aus dem aktuellen Anwendungsfenster in einer Codezeile mit dem Aufruf Window.Current.Contentabrufen. Anschließend können Sie zu "FrameworkElement" umwandeln und "FindName" aus diesem Bereich aufrufen.
  • Wenn Sie aus dem XAML-Stamm-NameScope aufrufen und ein Objekt innerhalb eines einzelnen XAML-NameScopes verwenden möchten, empfiehlt es sich, im Code vorauszuplanen und einen Verweis auf das Objekt beizubehalten, das von XamlReader.Load zurückgegeben und dann der Hauptobjektstruktur hinzugefügt wurde. Dieses Objekt ist jetzt ein gültiges Objekt zum Aufrufen von FindName innerhalb des einzelnen XAML-NameScopes. Sie können dieses Objekt als globale Variable verfügbar halten oder es anderweitig mithilfe von Methodenparametern übergeben.
  • Sie können Namen und XAML-NameScope-Überlegungen vollständig vermeiden, indem Sie die visuelle Struktur untersuchen. Mit der VisualTreeHelper-API können Sie die visuelle Struktur in Bezug auf übergeordnete Objekte und untergeordnete Auflistungen, basierend auf Position und Index, durchlaufen.

XAML-NameScopes in Vorlagen

Vorlagen in XAML bieten die Möglichkeit, Inhalte auf einfache Weise wiederzuverwenden und erneut zu verwenden. Vorlagen können jedoch auch Elemente mit Namen enthalten, die auf Vorlagenebene definiert sind. Dieselbe Vorlage wird möglicherweise mehrmals auf einer Seite verwendet. Aus diesem Grund definieren Vorlagen ihre eigenen XAML-NameScopes, unabhängig von der seite, auf die die Formatvorlage oder Vorlage angewendet wird. Betrachten Sie das folgende 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="MyTemplate">
      ....
      <TextBlock x:Name="MyTextBlock" />
    </ControlTemplate>
  </Page.Resources>
  <StackPanel>
    <SomeControl Template="{StaticResource MyTemplate}" />
    <SomeControl Template="{StaticResource MyTemplate}" />
  </StackPanel>
</Page>

Hier wird dieselbe Vorlage auf zwei verschiedene Steuerelemente angewendet. Wenn Vorlagen keine separaten XAML-NameScopes haben, würde der in der Vorlage verwendete Name "MyTextBlock" zu einer Namenskonflikt führen. Jede Vorlageninstanz hat ihren eigenen XAML-Namescope, weshalb in diesem Beispiel der Namescope jeder Instanz der Vorlage genau einen Namen enthält. Der XAML-Stammnamescope enthält jedoch nicht den Namen aus beiden Vorlagen.

Aufgrund der separaten XAML-NameScopes erfordert die Suche nach benannten Elementen innerhalb einer Vorlage aus dem Bereich der Seite, auf die die Vorlage angewendet wird, eine andere Technik. Anstatt FindName für ein objekt in der Objektstruktur aufzurufen, rufen Sie zuerst das Objekt ab, auf das die Vorlage angewendet wurde, und rufen Dann GetTemplateChild auf. Wenn Sie ein Steuerelementautor sind und eine Konvention generieren, bei der ein bestimmtes benanntes Element in einer angewendeten Vorlage das Ziel für ein Verhalten ist, das vom Steuerelement selbst definiert wird, können Sie die GetTemplateChild-Methode aus dem Implementierungscode des Steuerelements verwenden. Die GetTemplateChild-Methode ist geschützt, sodass nur der Autor des Steuerelements Zugriff darauf hat. Außerdem gibt es Konventionen, die Steuerelementautoren befolgen sollten, um Teile und Vorlagenteile zu benennen und diese als Attributwerte zu melden, die auf die Steuerelementklasse angewendet werden. Diese Technik macht die Namen wichtiger Teile für die Steuerung von Benutzern erkennbar, die möglicherweise eine andere Vorlage anwenden möchten, was die benannten Teile ersetzen muss, um die Steuerungsfunktionalität aufrechtzuerhalten.