Markuperweiterungen und WPF-XAML
In diesem Thema wird das Konzept von Markuperweiterungen für XAML vorgestellt, einschließlich ihrer Syntaxregeln, ihres Zwecks und des Klassenobjektmodells, das ihnen zugrunde liegt. Markuperweiterungen sind ein allgemeines Feature der XAML-Sprache und der .NET-Implementierung von XAML-Diensten. In diesem Thema werden speziell Markup-Erweiterungen zur Verwendung in WPF-XAML detailliert erläutert.
XAML-Prozessoren und Markuperweiterungen
Im Allgemeinen kann ein XAML-Parser entweder einen Attributwert als Literalzeichenfolge interpretieren, die in einen Grundtyp konvertiert werden kann, oder es auf irgendeine Art und Art in ein Objekt konvertieren. Ein solches Mittel besteht darin, auf einen Typkonverter zu verweisen; dies ist im Thema TypeConverters und XAML-dokumentiert. Es gibt jedoch Szenarien, in denen ein anderes Verhalten erforderlich ist. Beispielsweise kann ein XAML-Prozessor angewiesen werden, dass ein Wert eines Attributs nicht zu einem neuen Objekt im Objektdiagramm führen soll. Stattdessen sollte das Attribut zu einem Objektdiagramm führen, das einen Verweis auf ein bereits erstelltes Objekt in einem anderen Teil des Diagramms oder auf ein statisches Objekt macht. Ein weiteres Szenario besteht darin, dass ein XAML-Prozessor angewiesen werden kann, eine Syntax zu verwenden, die dem Konstruktor eines Objekts nicht standardmäßige Argumente bereitstellt. Dies sind die Arten von Szenarien, in denen eine Markuperweiterung die Lösung bereitstellen kann.
Grundlegende Markuperweiterungssyntax
Eine Markuperweiterung kann implementiert werden, um Werte für Eigenschaften in einer Attributverwendung, Eigenschaften in einer Eigenschaftselementverwendung oder beides bereitzustellen.
Wenn die Syntax verwendet wird, um einen Attributwert bereitzustellen und eine Markuperweiterungssequenz für einen XAML-Prozessor zu unterscheiden, macht das Vorhandensein der öffnenden und schließenden geschweiften Klammern ({ und }) den Unterschied. Der Typ der Markuperweiterung wird dann durch das Zeichenfolgentoken unmittelbar nach der öffnenden geschweiften Klammer identifiziert.
Wenn sie in der Eigenschaftselementsyntax verwendet wird, sieht eine Markuperweiterung genauso aus wie jedes andere Element, das zur Bereitstellung eines Eigenschaftselementwerts verwendet wird: eine XAML-Elementdeklaration, die auf die Markuperweiterungsklasse als Element verweist und in Winkelklammern (<>) eingeschlossen ist.
XAML-Defined Markup-Erweiterungen
Es gibt mehrere Markup-Erweiterungen, die nicht spezifisch für die WPF-Implementierung von XAML sind, sondern stattdessen Implementierungen systeminterner Funktionen oder Merkmale von XAML als Sprache. Diese Markuperweiterungen werden in der System.Xaml-Assembly als Teil der allgemeinen .NET Framework-XAML-Dienste implementiert und befinden sich im XAML-Sprach-XAML-Namespace. Im Hinblick auf die allgemeine Markupverwendung sind diese Markuperweiterungen typischerweise durch das Präfix x:
erkennbar. Die MarkupExtension-Basisklasse (auch in System.Xaml definiert) stellt das Muster bereit, das alle Markup-Erweiterungen verwenden sollten, um in XAML-Lesern und XAML-Schreibern unterstützt zu werden, einschließlich in WPF-XAML.
x:Type
stellt das Type-Objekt für einen benannten Typ zur Verfügung. Diese Funktion wird in Stilen und Vorlagen am häufigsten verwendet. Einzelheiten finden Sie unter x:Type Markup Extension.x:Static
erzeugt statische Werte. Die Werte stammen aus Werttyp-Code-Entitäten, die nicht direkt vom Typ des Werts einer Zieleigenschaft sind, aber auf diesen Typ ausgewertet werden können. Ausführliche Informationen finden Sie unter x:Static Markup Extension.x:Null
gibtnull
als Wert für eine Eigenschaft an und kann entweder für Attribute oder Eigenschaftselementwerte verwendet werden. Weitere Informationen finden Sie in der Dokumentation zu x:Null Markup Extension.x:Array
bietet Unterstützung für die Erstellung allgemeiner Arrays in der XAML-Syntax, wenn die von WPF-Basiselementen und Steuerelementmodellen bereitgestellte Sammlungsunterstützung absichtlich nicht verwendet wird. Ausführliche Informationen finden Sie unter x:Array Markup Extension.
Anmerkung
Das Präfix x:
wird für die typische XAML-Namespacezuordnung der grundlegenden Funktionen der XAML-Sprache im Stammelement einer XAML-Datei verwendet. Beispielsweise initiieren die Visual Studio-Vorlagen für WPF-Anwendungen eine XAML-Datei mithilfe dieser x:
Zuordnung. Sie können ein anderes Präfixtoken in Ihrer eigenen XAML-Namespacezuordnung auswählen. In dieser Dokumentation wird jedoch davon ausgegangen, dass die standardmäßige zuordnung x:
als Mittel zum Identifizieren der Entitäten verwendet wird, die ein definierter Teil des XAML-Namespaces für die XAML-Sprache sind, im Gegensatz zu den WPF-Standardnamespaces oder anderen XAML-Namespaces, die nicht mit einem bestimmten Framework zusammenhängen.
WPF-Specific Markuperweiterungen
Die am häufigsten in der WPF-Programmierung verwendeten Markuperweiterungen sind diejenigen, die Ressourcenverweise unterstützen (StaticResource
und DynamicResource
), und diejenigen, die die Datenbindung unterstützen (Binding
).
StaticResource
stellt einen Wert für eine Eigenschaft bereit, indem der Wert einer bereits definierten Ressource ersetzt wird. EineStaticResource
-Auswertung wird letztendlich beim Laden von XAML durchgeführt. Sie hat keinen Zugriff auf den Objektgraph zur Laufzeit. Nähere Informationen finden Sie unter StaticResource Markup Extension.DynamicResource
stellt einen Wert für eine Eigenschaft bereit, indem dieser Wert zurückgestellt wird, indem er als Laufzeitverweis auf eine Ressource dient. Ein dynamischer Ressourcenverweis erzwingt bei jedem Zugriff auf eine solche Ressource eine neue Suche und hat zur Laufzeit Zugriff auf den Objektgraphen. Um diesen Zugriff zu erhalten, wird dasDynamicResource
-Konzept durch Abhängigkeitseigenschaften im WPF-Eigenschaftensystem und durch ausgewertete Ausdrücke unterstützt. Daher können Sie nurDynamicResource
für ein Abhängigkeitseigenschaftsziel verwenden. Ausführliche Informationen finden Sie unter DynamicResource Markup Extension.Binding
stellt einen datengebundenen Wert für eine Eigenschaft bereit, wobei der Datenkontext verwendet wird, der zur Laufzeit auf das übergeordnete Objekt angewendet wird. Diese Markuperweiterung ist relativ komplex, da sie eine wesentliche Inlinesyntax zum Angeben einer Datenbindung ermöglicht. Ausführliche Informationen finden Sie unter Binding Markup Extension.RelativeSource
stellt Quellinformationen für eine Binding bereit, die mehrere mögliche Beziehungen im Laufzeit-Objektbaum navigieren kann. Dies bietet eine spezialisierte Bereitstellung für Bindungen, die in Vorlagen mit Mehrfachnutzung oder im Code ohne vollständige Kenntnisse des umgebenden Objektbaums erstellt werden. Weitere Informationen finden Sie unter RelativeSource MarkupExtension.TemplateBinding
ermöglicht es einer Steuerelementvorlage, Werte für vorlagenbasierte Eigenschaften zu verwenden, die aus objektmodelldefinierten Eigenschaften der Klasse stammen, die die Vorlage verwenden wird. Mit anderen Worten, die Eigenschaft innerhalb der Vorlagendefinition kann auf einen Kontext zugreifen, der nur vorhanden ist, nachdem die Vorlage angewendet wurde. Weitere Informationen finden Sie unter TemplateBinding Markup Extension. Weitere Informationen zur praktischen Verwendung vonTemplateBinding
finden Sie unter Stilgestaltung mit ControlTemplates Beispiel.ColorConvertedBitmap
unterstützt ein vergleichsweise fortgeschrittenes Bildgebungsszenario. Weitere Informationen finden Sie unter ColorConvertedBitmap-Markup-Erweiterung.ComponentResourceKey
undThemeDictionary
unterstützen Aspekte der Ressourcenabfrage, insbesondere für Ressourcen und Themen, die in benutzerdefinierten Steuerelementen integriert sind. Weitere Informationen finden Sie unter ComponentResourceKey Markup Extension, ThemeDictionary Markup Extensionoder Control Authoring Overview.
*Erweiterungsklassen
Sowohl für die allgemeine XAML-Sprache als auch für WPF-spezifische Markuperweiterungen wird das Verhalten jeder Markuperweiterung für einen XAML-Prozessor über eine *Extension
-Klasse identifiziert, die von MarkupExtensionabgeleitet wird, und stellt eine Implementierung der ProvideValue-Methode bereit. Diese Methode von jeder Erweiterung stellt das Objekt bereit, das zurückgegeben wird, wenn die Markup-Erweiterung ausgewertet wird. Das zurückgegebene Objekt wird in der Regel basierend auf den verschiedenen String-Token ausgewertet, die an die Markup-Erweiterung übergeben werden.
Beispielsweise stellt die StaticResourceExtension-Klasse die Oberflächenimplementierung der tatsächlichen Ressourcensuche bereit, sodass die ProvideValue Implementierung das angeforderte Objekt zurückgibt, wobei es sich bei der Eingabe dieser bestimmten Implementierung um eine Zeichenfolge handelt, die verwendet wird, um die Ressource anhand der x:Key
nachzuschlagen. Viele dieser Implementierungsdetails sind unwichtig, wenn Sie eine vorhandene Markuperweiterung verwenden.
Einige Markuperweiterungen verwenden keine Argumente für String-Tokens. Dies liegt entweder daran, dass sie einen statischen oder konsistenten Wert zurückgeben oder der Kontext für den zurückgegebenen Wert über einen der dienste verfügbar ist, die über den serviceProvider
-Parameter übergeben werden.
Das *Extension
Benennungsmuster dient zur Einfachheit und Konsistenz. Es ist nicht erforderlich, dass ein XAML-Prozessor diese Klasse als Unterstützung für eine Markuperweiterung erkennt. Solange Ihre Codebasis System.Xaml enthält und .NET Framework XAML-Services-Implementierungen verwendet, müssen Sie lediglich von MarkupExtension ableiten und eine Konstruktionssyntax unterstützen, um als XAML-Markuperweiterung erkannt zu werden. WPF definiert markuperweiterungsfähige Klassen, die nicht dem Benennungsmuster *Extension
folgen, z. B. Binding. In der Regel ist dies der Grund dafür, dass die Klasse Szenarien über reine Markuperweiterungsunterstützung hinaus unterstützt. Im Fall von Bindingunterstützt diese Klasse den Laufzeitzugriff auf Methoden und Eigenschaften des Objekts für Szenarien, die nichts mit XAML zu tun haben.
Erweiterungsklasseninterpretation von Initialisierungstext
Die Zeichenfolgentoken, die dem Namen der Markuperweiterung folgen und sich noch in den geschweiften Klammern befinden, werden von einem XAML-Prozessor auf eine der folgenden Arten interpretiert:
Ein Komma stellt immer das Trennzeichen für einzelne Tokens dar.
Wenn die einzelnen getrennten Token keine Gleichheitszeichen enthalten, wird jedes Token als Konstruktorargument behandelt. Jeder Konstruktorparameter muss als der von dieser Signatur erwartete Typ und in der von dieser Signatur erwarteten Reihenfolge angegeben werden.
Anmerkung
Ein XAML-Prozessor muss den Konstruktor aufrufen, der der Argumentanzahl der Anzahl der Paare entspricht. Aus diesem Grund stellen Sie bei der Implementierung einer benutzerdefinierten Markuperweiterung nicht mehrere Konstruktoren mit derselben Argumentanzahl bereit. Das Verhalten eines XAML-Prozessors, wenn mehr als ein Markuperweiterungskonstruktorpfad mit derselben Parameteranzahl vorhanden ist, ist nicht definiert. Sie sollten jedoch davon ausgehen, dass ein XAML-Prozessor eine Ausnahme bei der Verwendung auslösen darf, wenn diese Situation in den Markuperweiterungstypdefinitionen vorhanden ist.
Wenn die einzelnen, getrennten Token Gleichheitszeichen enthalten, ruft ein XAML-Prozessor zuerst den parameterlosen Konstruktor für die Markuperweiterung auf. Anschließend wird jedes Name=Wert-Paar als ein Eigenschaftenname interpretiert, der in der Markup-Erweiterung vorhanden ist, und als ein Wert, der dieser Eigenschaft zugewiesen werden soll.
Wenn es ein paralleles Ergebnis zwischen dem Konstruktorverhalten und dem Eigenschafteneinstellungsverhalten in einer Markuperweiterung gibt, spielt es keine Rolle, welches Verhalten Sie verwenden. Es ist üblicher, die Eigenschaft
=
Wert--Paare für Markuperweiterungen zu verwenden, die mehr als eine festlegbare Eigenschaft haben, schon allein deswegen, weil Ihr Markup so bewusster wird und Sie weniger wahrscheinlich Konstruktorparameter vertauschen. (Wenn Sie Eigenschaft=Wert-Paare angeben, können diese Paare in beliebiger Reihenfolge vorhanden sein.) Außerdem besteht keine Garantie, dass eine Markuperweiterung einen Konstruktorparameter bereitstellt, der jede ihrer setzbaren Eigenschaften festlegt. Beispielsweise ist Binding eine Markuperweiterung mit vielen Eigenschaften, die über die Erweiterung einstellbar sind, in der Form von Eigenschaft=
Wert, jedoch unterstützt Binding nur zwei Konstruktoren: einen parameterlosen Konstruktor und einen, der einen anfänglichen Pfad festlegt.Ein buchstäbliches Komma kann nicht ohne Umwandlung an eine Markuperweiterung übergeben werden.
Escape-Sequenzen und Markup-Erweiterungen
Die Attributverarbeitung in einem XAML-Prozessor verwendet die geschweiften Klammern als Kennzeichen für eine Markup-Erweiterungssequenz. Es ist auch möglich, bei Bedarf einen literalen geschweiften Klammern-Attributwert zu erzeugen, indem man eine Escapesequenz mit einem leeren geschweiften Klammernpaar gefolgt von der literalen geschweiften Klammer eingibt. Siehe {} Escape-Sequenz - Markuperweiterung.
Verschachteln von Markuperweiterungen bei der Verwendung in XAML
Das Schachteln mehrerer Markuperweiterungen wird unterstützt, und jede Markuperweiterung wird zuerst am tiefsten ausgewertet. Betrachten Sie beispielsweise die folgende Verwendung:
<Setter Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
In dieser Verwendung wird die x:Static
-Anweisung zuerst ausgewertet und gibt eine Zeichenfolge zurück. Diese Zeichenfolge wird dann als Argument für DynamicResource
verwendet.
Markuperweiterungen und Eigenschaftselementsyntax
Wenn eine Markuperweiterungsklasse als Objektelement verwendet wird, das einen Eigenschaftselementwert ausfüllt, ist sie visuell nicht von einem typischen typgestützten Objektelement zu unterscheiden, das in XAML verwendet werden kann. Der praktische Unterschied zwischen einem typischen Objektelement und einer Markuperweiterung besteht darin, dass die Markuperweiterung entweder auf einen typierten Wert ausgewertet oder als Ausdruck zurückgestellt wird. Daher sind die Mechanismen für mögliche Typfehler bei Eigenschaftswerten für die Markup-Erweiterung anders, ähnlich wie spät gebundene Eigenschaften in anderen Programmiermodellen behandelt werden. Ein gewöhnliches Objektelement wird auf Übereinstimmung des Typs mit der Zieleigenschaft überprüft, die es beim Analysieren des XAML-Codes festlegt.
Die meisten Markuperweiterungen, die in der Objektelementsyntax zum Ausfüllen eines Eigenschaftselements verwendet werden, würden keinen Inhalt oder eine weitere Eigenschaftselementsyntax enthalten. Daher würden Sie das Element "object" schließen und keine untergeordneten Elemente verwenden. Wenn ein Objektelement von einem XAML-Prozessor gefunden wird, wird der Konstruktor für diese Klasse aufgerufen, der das aus dem analysierten Element erstellte Objekt instanziiert. Eine Markuperweiterungsklasse ist keine Ausnahme: Wenn die Markuperweiterung in der Objektelementesyntax nutzbar sein soll, müssen Sie einen parameterlosen Konstruktor bereitstellen. Einige vorhandene Markuperweiterungen verfügen über mindestens einen erforderlichen Eigenschaftenwert, der für die effektive Initialisierung angegeben werden muss. Wenn ja, wird dieser Eigenschaftswert in der Regel als Eigenschaftsattribut für das Objektelement angegeben. Im XAML-Namespace (x:), auf den Referenzseiten zu Spracheigenschaften und sowie den WPF-XAML-Erweiterungen, werden Markuperweiterungen mit erforderlichen Eigenschaften und deren Namen angegeben. Auf den Referenzseiten wird auch vermerkt, ob entweder die Syntax des Objektelements oder der Attributsyntax für bestimmte Markuperweiterungen unzulässig ist. Ein wichtiger Fall ist x:Array Markup Extension, die die Attributsyntax nicht unterstützen kann, da der Inhalt dieses Arrays innerhalb des Tags als Inhalt festgelegt werden muss. Der Arrayinhalt wird als allgemeine Objekte behandelt, daher ist kein Standardtypkonverter für das Attribut machbar. Außerdem erfordert x:Array Markup Extension einen type
Parameter.
Siehe auch
.NET Desktop feedback