Markuperweiterungen und WPF-XAML

Dieses Thema bietet eine Einführung das Konzept der Markuperweiterungen für XAML und enthält eine Erläuterung der Syntaxregeln, des Zweck und des zugrunde liegenden Klassenobjektmodels. Markuperweiterungen sind eine allgemeine Funktion der XAML-Sprache und der .NET-Implementierung von XAML-Diensten. In diesem Thema werden speziell Markuperweiterungen zur Verwendung in WPF XAML beschrieben.

XAML-Prozessoren und Markuperweiterungen

Im Allgemeinen kann ein XAML-Parser einen Attributwert entweder als Literalzeichenfolge interpretieren, die in eine Primitive konvertiert werden kann, oder auf bestimmte Weise in ein Objekt konvertieren. Eine Möglichkeit ist das Verweisen auf einen Typkonverter; dies wird im Thema TypeConverter und XAML dokumentiert. Bestimmte Szenarios erfordern jedoch ein anderes Verhalten. Ein XAML-Prozessor kann z.B. angewiesen werden, dass der Wert eines Attributs nicht zu einem neuen Objekt im Objektdiagramm führen soll. Stattdessen soll das Attribut ein Objektdiagramm ergeben, das auf ein bereits erstelltes Objekt in einem anderen Teil des Diagramms oder auf ein statisches Objekt verweist. Ein anderes Szenario ist, dass ein XAML-Prozessor angewiesen werden kann, Syntax zu verwenden, die für den Konstruktor eines Objekts nicht standardmäßige Argumente bereitstellt. Bei derartigen Szenarios eine Markuperweiterung die Lösung sein.

Grundlegende Markuperweiterungssyntax

Eine Markuperweiterung kann zur Bereitstellung von Werten für Eigenschaften zur Verwendung von Attributen, für Eigenschaften zur Verwendung von Eigenschaftenelementen oder für beides implementiert werden.

Wenn die Markuperweiterungssequenz zum Bereitstellen eines Attributwerts verwendet wird, wird dies durch die Verwendung von öffnenden und schließenden geschweiften Klammern ({ und }) in der Syntax für den XAML-Prozessor kenntlich gemacht. Das Zeichenfolgentoken, das unmittelbar auf die öffnende geschweifte Klammer folgt, bestimmt den Typ der Markuperweiterung.

Bei Verwendung in der Eigenschaftenelementsyntax entspricht das Erscheinungsbild der Markuperweiterung einem Element, das zum Bereitstellen eines Eigenschaftenelementwerts verwendet wird: eine XAML-Elementdeklaration, die auf die Markuperweiterungsklasse als Element verweist und von spitzen Klammern (<>) umschlossen ist.

XAML-definierte Markuperweiterungen

Es gibt mehrere Markuperweiterungen, die nicht spezifisch für die WPF-Implementierung von XAML gelten, sondern Implementierungen für Interna oder Funktionen von XAML als Sprache sind. Diese Markuperweiterungen werden als Teil der allgemeinen .NET Framework-XAML-Dienste in der System.Xaml-Assembly implementiert und sind im XAML-Sprachnamespace enthalten. Im Hinblick auf die allgemeine Markupverwendung sind diese Markuperweiterungen in der Regel durch das x:-Präfix in der Verwendung identifizierbar. Die MarkupExtension-Basisklasse (auch in System.Xaml definiert) stellt das Muster, bereit, das für alle Markuperweiterungen verwendet werden soll, damit sie in XAML-Readern und XAML-Writern (einschließlich in WPF-XAML) unterstützt werden.

  • x:Type stellt das Type -Objekt für den benannten Typ bereit. Diese Funktion wird am häufigsten in Stilen und Vorlagen verwendet. Weitere Informationen finden Sie unter x:Type-Markuperweiterung.

  • x:Static erzeugt statische Werte. Die Werte stammen aus Wert-Typ-Codeentitäten, die nicht direkt dem Typ eines Zieleigenschaftswerts entsprechen, jedoch diesem Typ entsprechend ausgewertet werden können. Weitere Informationen finden Sie unter x:Statische Markuperweiterung.

  • x:Null gibt null als Wert für eine Eigenschaft an und kann für Attribute oder Eigenschaftenelementwerte verwendet werden. Weitere Informationen finden Sie unter x:Null-Markuperweiterung.

  • x:Array bietet Unterstützung für die Erstellung von allgemeinen Arrays in XAML-Syntax, wenn die Auflistungsunterstützung von WPF-Basiselementen und Steuerelementmodellen bewusst nicht verwendet wird. Weitere Informationen finden Sie unter x:Array-Markuperweiterung.

Hinweis

Das x:-Präfix wird für die typische XAML-Namespacezuordnung der systeminternen XAML-Sprache im Stammelement einer XAML-Datei oder Produktion verwendet. Beispiel: die Visual Studio-Vorlagen für WPF-Anwendungen initiieren mit dieser x:-Zuordnung eine XAML-Datei. Sie können ein anderes Präfixtoken in Ihrer eigenen XAML-Namespacezuordnung auswählen, dieser Dokumentation wird jedoch die x:-Standardzuordnung für die Identifizierung der Entitäten zugrunde gelegt, die als Teil des XAML-Sprachnamespace definiert sind (im Gegensatz zu einem standardmäßigen WPF-Namespace oder anderen XAML-Namespaces, die keinem bestimmten Framework zugeordnet sind).

WPF-spezifische Markuperweiterungen

Bei der WPF-Programmierung werden am häufigsten die Markuperweiterungen mit Unterstützung für Ressourcenverweise (StaticResource und DynamicResource), und mit Unterstützung für Datenbindung verwendet (Binding).

  • StaticResource stellt einen Wert für eine Eigenschaft bereit, indem der Wert einer bereits definierten Ressource ersetzt wird. Schließlich wird eine StaticResource-Auswertung zur der XAML-Ladezeit vorgenommen, die während der tatsächlichen Laufzeit keinen Zugriff auf das Objektdiagramm hat. Weitere Informationen finden Sie unter StaticResource-Markuperweiterung

  • DynamicResource stellt einen Wert für eine Eigenschaft bereit, indem dieser Wert als Laufzeitverweis auf eine Ressource verwendet wird. Ein dynamischer Ressourcenverweis erzwingt jedes Mal eine neue Suche, wenn auf eine solche Ressource zugegriffen wird und diese zur Laufzeit Zugriff auf das Objektdiagramm hat. Um diesen Zugriff abzurufen, wird das DynamicResource Konzept von Abhängigkeitseigenschaften im WPF-Eigenschaftensystem und ausgewerteten Ausdrücken unterstützt. Daher kann DynamicResource nur für ein Abhängigkeitseigenschaftsziel verwendet werden. Weitere Informationen finden Sie unter DynamicResource-Markuperweiterung

  • Binding stellt einen datengebundenen Wert für eine Eigenschaft bereit, indem der Datenkontext verwendet wird, der zur Laufzeit für das übergeordnete Objekt gilt. Diese Markuperweiterung ist relativ komplex, da eine komplexe Inlinesyntax für die Angabe einer Datenbindung aktiviert wird. Weitere Informationen finden Sie unter Binding als Markuperweiterung.

  • RelativeSource stellt Quellinformationen für eine Binding bereit, die mehrere mögliche Beziehungen in der Laufzeitobjektstruktur navigieren kann. Hierdurch werden spezielle Quellbezüge für Bindungen bereitgestellt, die ohne vollständige Kenntnis der umgebenden Objektstruktur in Mehrzweckvorlagen oder in Code erstellt werden. Weitere Informationen finden Sie unter RelativeSource-Markuperweiterungen.

  • TemplateBinding ermöglicht die Verwendung von Vorlage-Eigenschaftswerten in Steuerelementvorlagen, die von den durch das Objektmodell definierten Eigenschaften der Klasse stammen, die die Vorlage verwenden werden. Dies bedeutet, dass die Eigenschaft in der Vorlagendefinition auf einen Kontext zugreifen kann, der erst vorhanden ist, wenn die Vorlage angewendet wird. Weitere Informationen finden Sie unter TemplateBinding Markuperweiterung. Weitere Informationen über die praktische Verwendung von TemplateBinding, finden Sie unter Beispiel zum Formatieren mit ControlTemplates.

  • ColorConvertedBitmap unterstützt ein relativ komplexes Bildverarbeitungsszenario. Weitere Informationen finden Sie unter ColorConvertedBitmap-Markuperweiterung

  • ComponentResourceKey und ThemeDictionary unterstützen Aspekte der Ressourcensuche, insbesondere für Ressourcen und Designs, mit benutzerdefinierten Steuerelementen enthalten sind. Weitere Informationen finden Sie unter ComponentResourceKey-Markuperweiterung, ThemeDictionary-Markuperweiterung, oder Übersicht über das Erstellen von Steuerelementen.

*Erweiterungsklassen

Für die allgemeinen XAML- und WPF-spezifischen Markuperweiterungen wird das Verhalten der einzelnen Markuperweiterungen für einen XAML-Prozessor über eine *Extension-Klasse identifiziert die von MarkupExtension abgeleitet wird und eine Implementierung der ProvideValue-Methode bereitstellt. Diese für jede Erweiterung verwendete Methode stellt das Objekt bereit, das bei der Auswertung der Markuperweiterung zurückgegeben wird Das zurückgegebene Objekt wird normalerweise auf Grundlage der verschiedenen Zeichenfolgentoken ausgewertet, die an die Markuperweiterung übergeben werden.

Beispiel: Die ProvideValue-Klasse stellt die Oberflächenimplementierung der tatsächlichen Ressourcensuche bereit, sodass die zugehörige x:Key-Implementierung das angeforderte Objekt zurückgibt. Hierbei ist die Eingabe dieser Implementierung eine Zeichenfolge, die für die Suche nach der Ressource gemäß des zugehörigen StaticResourceExtension verwendet wird. Ein großer Teil dieser Implementierungsdetails ist nicht von Bedeutung, wenn Sie eine bestehende Markuperweiterung verwenden.

Einige Markuperweiterungen verwenden keine Zeichenfolgentokenargumente. Dies ist darauf zurückzuführen, dass sie einen statischen oder konsistenten Wert zurückgeben, oder darauf, dass der Kontext zum Bestimmen des zurückzugebenden Werts mit einem der durch den serviceProvider-Parameter übergebenen Dienste verfügbar ist.

Das *Extension-Namensmuster dient der besseren Übersichtlichkeit und Konsistenz. Es ist nicht erforderlich, damit ein XAML-Prozessor diese Klasse als Unterstützung für eine Markuperweiterung identifiziert. Sofern die Codebasis „System.Xaml“ enthält und die .NET Framework-XAML-Dienstimplementierung verwendet, müssen alle Elemente, die als XAML-Markuperweiterung erkannt werden sollen, von MarkupExtension abgeleitet werden Konstruktionssyntax unterstützen. WPF definiert Klassen für Markuperweiterungen, die dem *Extension-Benennungsmuster nicht folgen, z. B. Binding. Der Grund hierfür ist normalerweise, dass die Klasse Szenarios unterstützt, die über die reine Unterstützung von Markuperweiterungen hinausgehen. Im Fall von Binding unterstützt diese Klasse zur Laufzeit den Zugriff auf Methoden und Eigenschaften des Objekts.

Erweiterungsklasse – Interpretation der Initialisierungstext

Die Zeichenfolgentoken, die auf den Namen der Markuperweiterung folgen und sich innerhalb der Klammern befinden, werden von einem XAML-Prozessor auf eine der folgenden Arten interpretiert:

  • Ein Komma fungiert immer als Trennzeichen einzelner Token.

  • Wenn die einzelnen getrennten Token keine Gleichheitszeichen enthalten, wird jedes Token als Konstruktorargument behandelt. Jeder Konstruktorparameter muss als Typ angegeben werden, der von der Signatur erwartet wird, und die Angabe muss in der von der Signatur erwarteten Reihenfolge erfolgen.

    Hinweis

    Ein XAML-Prozessor muss den Konstruktor aufrufen, der der Argumentanzahl der Anzahl von Paaren entspricht. Wenn Sie eine benutzerdefinierte Markuperweiterung implementieren, dürfen Sie aus diesem Grund nicht mehrere Konstrukte mit derselben Argumentanzahl bereitstellen. Das Verhalten eines XAML-Prozessors, wenn mehrere Konstruktorpfade der Markuperweiterung mit derselben Parameteranzahl vorhanden sind, ist nicht definiert. Sie sollten jedoch davon ausgehen, dass ein XAML-Prozessor eine Ausnahme auslösen kann, wenn diese Situation in den Typdefinitionen der Markuperweiterung vorliegt.

  • Wenn die einzelnen Token Gleichheitszeichen enthalten, ruft der XAML-Prozessor zuerst den parameterlosen Konstruktor für die Markuperweiterung auf. Dann wird jedes Name=Wert-Paar als in der Markuperweiterung vorhandener Eigenschaftenname und als Wert interpretiert, der der Eigenschaft zuzuweisen ist.

  • Wenn in einer Markuperweiterung ein paralleles Ergebnis zwischen dem Konstruktorverhalten und dem Verhalten der Eigenschafteneinstellung vorliegt, können Sie zwischen den Verhalten wählen. Wenn in einer Markuperweiterung ein paralleles Ergebnis zwischen dem Konstruktorverhalten und dem Verhalten der Eigenschafteneinstellung vorliegt, können Sie zwischen den Verhalten wählen. Die Verwendung von Eigenschaft=Wert-Paaren mit mehreren festlegbaren Eigenschaften für Markuperweiterungen ist gängiger, da hierbei ein geringeres Risiko besteht, Konstruktorparameter versehentlich zu vertauschen. (Bei der Angabe von Eigenschaft=Wert-Paaren spielt die Reihenfolge der Eigenschaften keine Rolle). Des Weiteren ist nicht garantiert, dass eine Markuperweiterung einen Konstruktorparameter angibt, der alle festlegbaren Eigenschaften festlegt. Beispiel: Binding ist eine Markuperweiterung mit vielen Eigenschaften, die durch die Erweiterung in der Form Eigenschaft=Wert festlegbar sind, Binding unterstützt jedoch nur zwei Konstruktoren: einen Standardkonstruktor und einen Konstruktor, der einen Anfangspfad festlegt.

  • Ein Komma kann nicht ohne Vorschub als Literalzeichen an eine Markuperweiterung übergeben werden.

Escapesequenzen und Markuperweiterungen

Bei der Attributbehandlung in einem XAML-Prozessor werden geschweifte Klammern als Bezeichner für Markuperweiterungssequenzen verwendet. Falls erforderlich, können Sie geschweifte Klammern als Literalzeichen angeben, indem Sie eine Escapesequenz in Form eines leeren Paars geschweifter Klammern gefolgt von der literalen geschweiften Klammer angeben. Weitere Informationen finden Sie unter {}-Escapesequenz – Markuperweiterung.

Schachtelung von Markuperweiterungen in der XAML-Verwendung

Schachtelung mehrerer Markuperweiterungen wird unterstützt, und jeder Markuperweiterung wird tiefsten zuerst ausgewertet. Betrachten Sie z.B. die folgende Syntax:

<Setter Property="Background"  
  Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />  

Die x:Static-Anweisung wird hier zuerst ausgewertet und gibt eine Zeichenfolge zurück. Diese Zeichenfolge wird dann als Argument für DynamicResource verwendet.

Markuperweiterungen und Eigenschaftenelementsyntax

Bei Verwendung als Objektelement, das in einen Eigenschaftenelementwert geladen wird, ist eine Markuperweiterungsklasse visuell nicht von regulären typgestützten Objekten zu unterscheiden, die in XAML verwendet werden können. In diesem Fall besteht der praktische Unterschied zwischen einem typischen Objektelement und einer Markuperweiterung darin, dass die Markuperweiterung entweder als typisierter Wert ausgewertet oder als Ausdruck verzögert wird. Aus diesem Grund unterscheiden sich die Mechanismen für mögliche Typfehler bei Eigenschaftswerten für die Markuperweiterung. Dies ist mit der Behandlung einer Eigenschaft mit später Bindung in anderen Programmiermodellen vergleichbar. Ein normales Objektelement wird beim Analysieren des XAML hinsichtlich der Typübereinstimmung mit der von ihm festgelegten Zieleigenschaft ausgewertet.

Die meisten Markuperweiterungen würden keinen Inhalt oder weitere Eigenschaftenelementsyntax aufweisen, wenn sie in der Objektelementsyntax in ein Eigenschaftenelement geladen werden. Deshalb würden Sie das Objektelementtag schließen und keine untergeordneten Elemente bereitstellen. Wenn ein Objektelement von einem XAML-Prozessor erkannt wird, wird der Konstruktor für diese Klasse aufgerufen, die das aus dem analysierten Element erstellte Objekt instanziiert. Dies gilt auch für Markuperweiterungsklassen. Wenn Sie die Markuperweiterung in Objektelementsyntax verwenden möchten, müssen Sie einen parameterlosen Konstruktor angeben. Einige vorhandene Markuperweiterungen verfügen über mindestens einen erforderlichen Eigenschaftswert, der für die effektive Initialisierung angegeben werden muss. Wenn dies der Fall ist, wird dieser Eigenschaftswert in der Regel als Eigenschaftenattribut für das Objektelement angegeben. Markuperweiterungen mit erforderlichen Eigenschaften (sowie die Namen dieser Eigenschaften) werden auf den Referenzseiten XAML-Namespace (x:) Sprachfeatures und WPF-XAML-Erweiterungen aufgeführt. Des Weiteren wird auf den Referenzseiten darauf hingewiesen, wenn Objektelementsyntax oder Attributsyntax von einer Markuperweiterung nicht unterstützt werden. Ein wichtiger Fall ist X: Array-Markuperweiterung, die Attributsyntax nicht unterstützt, da der Inhalt des Arrays innerhalb der Tags als Inhalt angegeben werden muss. Die Arrayinhalte werden als allgemeine Objekte behandelt, sodass kein Standardtypkonverter für das Attribut zulässig ist. Außerdem erfordert X: Array-Markuperweiterung einen type-Parameter.

Siehe auch