Übersicht über Markuperweiterungen für XAML
Markuperweiterungen sind eine XAML-Technik zum Abrufen eines Werts, der weder ein primitiver noch ein spezifischer XAML-Typ ist. Für die Attributverwendung verwenden Markuperweiterungen die bekannte Zeichenfolge einer öffnenden geschweiften Klammer {, um den Markuperweiterungsbereich einzugeben, und einer schließenden geschweiften Klammer } zum Beenden. Bei der Verwendung von .NET Framework-XAML-Diensten können Sie einige der vordefinierten XAML-Sprachmarkuperweiterungen aus der System.Xaml-Assembly verwenden. Sie können auch eine untergeordnete Klasse aus der in System.Xaml definierten MarkupExtension-Klasse erstellen und eigene Markuperweiterungen definieren. Oder Sie können durch ein bestimmtes Framework definierte Markuperweiterungen verwenden, wenn Sie bereits auf dieses Framework verweisen.
Wenn auf eine Markuperweiterungsverwendung zugegriffen wird, kann der XAML-Objektwriter Dienste für eine benutzerdefinierte MarkupExtension-Klasse durch einen Dienstverbindungspunkt in der MarkupExtension.ProvideValue-Überschreibung bereitstellen. Die Dienste können verwendet werden, um Kontext zur Verwendung, zu bestimmten Funktionen des Objektwriters, zum XAML-Schemakontext usw. abzurufen.
Dieses Thema enthält folgende Abschnitte.
- XAML-definierte Markuperweiterungen
- Die MarkupExtension-Basisklasse
- Definieren des Unterstützungstyps für eine benutzerdefinierte-Markuperweiterung
- Konstruktormuster und Positionsargumente für eine benutzerdefinierte Markuperweiterung
- Benannte Argumente für eine benutzerdefinierte Markuperweiterung
- Zugreifen auf Dienstanbieterkontext aus der Implementierung einer Markuperweiterung
- Eigenschaftselementverwendung einer Markuperweiterung
- Attributieren für eine benutzerdefinierte Markuperweiterung
- Serialisierung von Markuperweiterungsverwendungen
- Markuperweiterungen im XAML-Knotenstream
- Verwandte Abschnitte
XAML-definierte Markuperweiterungen
Mehrere Markuperweiterungen werden von .NET Framework-XAML-Diensten für XAML-Sprachunterstützung implementiert. Diese Markuperweiterungen entsprechen Teilen der Spezifikation von XAML als Sprache. Diese sind in der Regel durch das x:-Präfix in der Syntax identifizierbar. Alle .NET Framework-XAML-Dienstimplementierungen für diese XAML-Sprachelemente sind von der MarkupExtension-Basisklasse abgeleitet.
Hinweis |
---|
Das x:-Präfix wird für die typische XAML-Namespacezuordnung des XAML-Sprachnamespace im Stammelement einer XAML-Produktion verwendet.Die Visual Studio-Projekt- und Seitenvorlagen für verschiedene bestimmte Frameworks initiieren z. B. eine XAML-Datei mit dieser x:-Zuordnung.Sie können ein anderes Präfixtoken in Ihrer eigenen XAML-Namespacezuordnung auswählen, in dieser Dokumentation wird jedoch die x:-Standardzuordnung für die Identifizierung der Entitäten vorausgesetzt, die als Teil des XAML-Sprachnamespace definiert sind (im Gegensatz zu einem standardmäßigen XAML-Namespace eines bestimmten Frameworks oder anderer beliebiger CLR- oder XML-Namespaces). |
x:Type
x:Type stellt das Type-Objekt für den benannten Typ bereit. Diese Funktionalität wird am häufigsten in Verzögerungsmechanismen verwendet, die den zugrunde CLR-Typ und die Typableitung als Gruppenmoniker oder -bezeichner verwenden. WPF-Formate und Vorlagen und ihre Verwendung von TargetType-Eigenschaften sind ein bestimmtes Beispiel. Weitere Informationen finden Sie unter x:Type-Markuperweiterung.
x:Static
x:Static erstellt statische Werte aus Werttyp-Codeentitäten, die nicht direkt dem Typ eines Eigenschaftswerts entsprechen, die jedoch diesem Typ entsprechend ausgewertet werden können. Dies ist nützlich zum Angeben von Werten, die bereits als bekannte Konstanten in einer Typdefinition vorhanden sind. Weitere Informationen finden Sie unter x:Statische Markuperweiterung.
x:Null
x:Null gibt null als Wert für einen XAML-Member an. Abhängig vom Design bestimmter Typen oder bei größeren Frameworkkonzepten ist null nicht immer ein Standardwert für eine Eigenschaft oder der implizite Wert eines leeren Zeichenfolgenattributs. Weitere Informationen finden Sie unter x:Null-Markuperweiterung.
x:Array
x:Array unterstützt die Erstellung von allgemeinen Arrays in XAML-Syntax, wenn die Auflistungsunterstützung von Basiselementen und Steuerelementmodellen bewusst nicht verwendet wird. Weitere Informationen finden Sie unter x:Array-Markuperweiterung. In XAML 2009 erfolgt der Zugriff auf Arrays nicht mit einer Erweiterung, sondern mit Sprachprimitiven. Weitere Informationen finden Sie unter XAML 2009-Sprachfunktionen.
x:Reference
x:Reference ist Teil von XAML 2009, einer Erweiterung des ursprünglichen Sprachsatzes (2006). x:Reference stellt einen Verweis auf ein anderes vorhandenes Objekt in einem Objektdiagramm dar. Dieses Objekt wird durch seinen x:Name identifiziert. Weitere Informationen finden Sie unter x:Reference-Markuperweiterung.
Andere x:-Konstrukte
Andere x:-Konstrukte zur Unterstützung von XAML-Sprachfunktionen sind zwar vorhanden, werden aber nicht als Markuperweiterungen implementiert. Weitere Informationen finden Sie unter Sprachfeatures des XAML-Namespace (x:).
Die MarkupExtension-Basisklasse
Um eine benutzerdefinierte Markuperweiterung zu definieren, die mit den Standardimplementierungen von XAML-Readern und XAML-Writern in System.Xaml interagieren kann, leiten Sie eine Klasse aus der abstrakten MarkupExtension-Klasse ab. Diese Klasse enthält eine Methode zum Überschreiben, ProvideValue. Möglicherweise müssen Sie zusätzliche Konstruktoren definieren, um Argumente für die Markuperweiterungssyntax und Zuordnung festlegbarer Eigenschaften zu unterstützen.
Durch ProvideValue verfügt eine benutzerdefinierte Markuperweiterung über Zugriff auf einen Dienstkontext, der die Umgebung meldet, in der die Markuperweiterung tatsächlich von einem XAML-Prozessor aufgerufen wird. Im Ladepfad ist dies in der Regel ein XamlObjectWriter. Im Speicherpfad ist dies in der Regel ein XamlXmlWriter. Beide melden den Dienstkontext als interne XAML-Dienstanbieterkontextklasse, die ein Dienstanbietermuster implementiert. Weitere Informationen zu den verfügbaren Diensten und ihren Inhalten finden Sie unter Typkonverter und Markuperweiterungen für XAML.
Die Markuperweiterungsklasse muss eine öffentliche Zugriffsebene verwenden; XAML-Prozessoren müssen immer in der Lage sein, die Unterstützungsklasse der Markuperweiterung zu instanziieren, um ihre Dienste zu verwenden.
Definieren des Unterstützungstyps für eine benutzerdefinierte-Markuperweiterung
Wenn Sie .NET Framework-XAML-Dienste oder auf .NET Framework-XAML-Diensten basierende Frameworks verwenden, stehen zwei Optionen zur Benennung des Markuperweiterungs-Unterstützungstyps zur Auswahl. Der Typname bestimmt, wie die XAML-Objektwriter einen Markuperweiterungs-Unterstützungstyp aufrufen, wenn sie auf eine Markuperweiterungssyntax in XAML stoßen. Verwenden Sie eine der folgenden Benennungsstrategien:
Nennen Sie den Typ so, dass er genau mit dem XAML-Markupverwendungstoken übereinstimmt. Um eine {Collate ...}-Erweiterungssyntax zu unterstützen, benennen Sie den Unterstützungstyp z. B. als Collate.
Benennen Sie den Typnamen mit dem Syntaxzeichenfolgentoken, ergänzt durch das Extension-Suffix. Um eine {Collate ...}-Erweiterungssyntax zu unterstützen, benennen Sie den Unterstützungstyp z. B. als CollateExtension.
Bei der Suche wird zuerst nach dem Extension-Klassennamen mit Suffix und anschließend nach dem Klassennamen ohne Extension-Suffix gesucht.
Hinsichtlich der Markupsyntax ist das Hinzufügen des Extension-Suffixes als Teil der Syntax zulässig. Dies verhält sich jedoch so, als wäre Extension wirklich Teil des Klassennamens, und XAML-Objektwriter würden keine Markuperweiterungs-Unterstützungsklasse für diese Verwendung auflösen, wenn die Unterstützungsklasse nicht über das Extension-Suffix verfügen würde.
Der Standardkonstruktor
Sie sollten für alle Markuperweiterungs-Unterstützungstypen einen öffentlichen Standardkonstruktor verfügbar machen. Ein Standardkonstruktor ist immer dann erforderlich, wenn ein XAML-Objektwriter die Markuperweiterung über eine Objektelementsyntax instanziiert. Die Unterstützung der Objektelementsyntax kann besonders bei der Serialisierung für eine Markuperweiterung erwartet werden. Sie können eine Markuperweiterung jedoch auch ohne öffentlichen Konstruktor implementieren, wenn Sie nur beabsichtigen, Attributverwendungen der Markuperweiterung zu unterstützen.
Wenn die Markuperweiterungsverwendung keine Argumente enthält, ist der Standardkonstruktor erforderlich, um die Verwendung zu unterstützen.
Konstruktormuster und Positionsargumente für eine benutzerdefinierte Markuperweiterung
Für eine Markuperweiterung mit beabsichtigter Argumentverwendung müssen die öffentlichen Konstruktoren den Modi der beabsichtigten Verwendung entsprechen. Anders ausgedrückt: wenn die Markuperweiterung entworfen wird, um ein Positionsargument als gültige Verwendung zu erfordern, sollten Sie einen öffentlichen Konstruktor mit einem Eingabeparameter unterstützen, der das Positionsargument akzeptiert.
Nehmen Sie z. B. an, dass die Collate-Markuperweiterung dazu gedacht ist, nur einen Modus zu unterstützen, wenn ein Positionsargument vorhanden ist, das seinen als CollationMode-Enumerationskonstante angegebenen Modus darstellt. In diesem Fall sollte ein Konstruktor mit folgender Form vorhanden sein:
public Collate(CollationMode collationMode) {...}
Grundlegend sind die an eine Markuperweiterung übergebenen Argumente Zeichenfolgen, da sie von den Attributwerten des Markups weitergeleitet werden. Sie können alle Argumente zu Zeichenfolgen machen und mit der Eingabe auf dieser Ebene arbeiten. Sie haben jedoch Zugriff auf bestimmte Verarbeitungsabläufe, die stattfinden, bevor die Markuperweiterungsargumente an die Unterstützungsklasse übergeben werden.
Die Verarbeitung arbeitet vom Prinzip her so, als sei die die Markuperweiterung ein zu erstellendes Objekt, und dann werden ihre Memberwerte festgelegt. Jede angegebene Eigenschaft, die festgelegt werden soll, wird ähnlich der Vorgehensweise ausgewertet, mit der ein angegebener Member auf einem erstellten Objekt festgelegt werden kann, wenn XAML analysiert wird. Es gibt jedoch zwei wichtige Unterschiede:
Wie zuvor erwähnt, muss ein Markuperweiterungs-Unterstützungstyp nicht über einen Standardkonstruktor verfügen, um in XAML instanziiert zu werden. Die Objektkonstruktion wird verzögert, bis die möglichen Argumente in der Textsyntax in Token unterteilt werden und entweder als Positionsargumente oder als benannte Argumente ausgewertet werden, und der entsprechende Konstruktor wird zu dieser Zeit aufgerufen.
Markuperweiterungssyntaxen können geschachtelt werden. Die innerste Markuperweiterung wird zuerst ausgewertet. Sie können somit eine solche Syntax voraussetzen und einen der Konstruktionsparameter als einen Typ deklarieren, der einen Wertkonverter erfordert (z. B. eine Markuperweiterung).
Der Einsatz dieser Verarbeitung wurde im vorherigen Beispiel gezeigt. Der XAML-Objektwriter der .NET Framework-XAML-Dienste verarbeitet die Namen von Enumerationskonstanten auf einer systemeigenen Ebene in Enumerationswerte.
Die Verarbeitung der Textsyntax eines Positionsparameters der Markuperweiterung kann sich auch auf einen Typkonverter stützen, der dem Typ im Konstruktionsargument zugeordnet ist.
Die Argumente werden als Positionsargumente bezeichnet, da die Reihenfolge, in der die Token in der Syntax vorkommen, der Positionsreihenfolge des Konstruktorparameters entspricht, dem sie zugewiesen sind. Betrachten Sie beispielsweise die folgende Konstruktorsignatur:
public Collate(CollationMode collationMode, object collateThis) {...}
Ein XAML-Prozessor erwartet zwei Positionsargumente für diese Markuperweiterung. Im Fall einer {Collate AlphaUp,{x:Reference circularFile}}-Syntax wird das AlphaUp-Token an den ersten Parameter gesendet und als benannte CollationMode-Enumerationskonstante ausgewertet. Das Ergebnis der inneren x:Reference wird an den zweiten Parameter gesendet und als Objekt ausgewertet.
In den XAML-Regeln für die Markuperweiterungssyntax und -verarbeitung ist das Komma das Trennzeichen zwischen Argumenten, und zwar unabhängig davon, ob es sich um Positionsargumente oder benannte Argumente handelt.
Doppelte Stelligkeit von Positionsargumenten
Wenn ein XAML-Objektwriter auf eine Markuperweiterungsverwendung mit Positionsargumenten stößt und mehrere Konstruktorargumente vorhanden sind, die diese Anzahl von Argumenten (eine doppelte Stelligkeit) annehmen, ist dies nicht notwendigerweise ein Fehler. Das Verhalten hängt von einer vom Benutzer anpassbaren XAML-Schema-Kontexteinstellung ab (SupportMarkupExtensionsWithDuplicateArity). Wenn SupportMarkupExtensionsWithDuplicateArity auf true festgelegt ist, sollte ein XAML-Objektwriter nicht nur aufgrund doppelter Stelligkeit eine Ausnahme auslösen. Das Verhalten über diesen Punkt hinaus ist nicht streng definiert. Beim Entwurf wird grundlegend vorausgesetzt, dass im Schemakontext Typinformationen für bestimmte Parameter verfügbar sind und explizite Umwandlungen versucht werden können, die mit den doppelten Kandidaten übereinstimmen, um zu ermitteln, welche Signatur die beste Übereinstimmung sein könnte. Wenn keine Signaturen die Tests des für einen XAML-Objektwriter ausgeführten Schemakontexts bestehen, kann dennoch eine Ausnahme ausgelöst werden.
Standardmäßig ist SupportMarkupExtensionsWithDuplicateArity im CLR-basierten XamlSchemaContext für .NET Framework-XAML-Dienste auf false festgelegt. Daher löst der standardmäßige XamlObjectWriter Ausnahmen aus, wenn er auf eine Markuperweiterungssyntax stößt, in der doppelte Stelligkeit in den Konstruktoren des Unterstützungstyps vorliegt.
Benannte Argumente für eine benutzerdefinierte Markuperweiterung
Durch XAML angegebene Markuperweiterungen können benannte Argumente für die Verwendung verwenden. Auf der ersten Ebene der Zerlegung in Token ist die Textsyntax in Argumente unterteilt. Das Vorhandensein eines Gleichheitszeichens (=) in einem Argument identifiziert ein Argument als benanntes Argument. Diese Argumente werden auch in ein Name-Wert-Paar zerlegt. In diesem Fall benennt der Name eine öffentliche festlegbare Eigenschaft des Unterstützungstyps der Markuperweiterung. Wenn Sie die Verwendung benannter Argumente unterstützen möchten, sollten Sie diese öffentlichen festlegbaren Eigenschaften bereitstellen. Bei den Eigenschaften kann es sich um geerbte Eigenschaften handeln, solange sie öffentlich bleiben.
Zugreifen auf Dienstanbieterkontext aus der Implementierung einer Markuperweiterung
Die verfügbaren Dienste sind für jeden Wertkonverter gleich. Der Unterschied besteht darin, wie die einzelnen Wertkonverter den Dienstkontext empfangen. Der Zugriff auf Dienste und die verfügbaren Dienste sind im Thema Typkonverter und Markuperweiterungen für XAML dokumentiert.
Eigenschaftselementverwendung einer Markuperweiterung
Die Szenarien für Markuperweiterungsverwendungen beinhalten oft die Verwendung von Markuperweiterungen als Attribute. Es ist jedoch potenziell auch möglich, die Unterstützungsklasse so zu definieren, dass die Verwendung von Eigenschaftselementen unterstützt wird.
Um die Eigenschaftselementverwendung der Markuperweiterung zu unterstützen, definieren Sie einen öffentlichen Standardkonstruktor. Dies sollte ein Instanzkonstruktor sein, kein statischer Konstruktor. Dies ist erforderlich, da ein XAML-Prozessor den Standardkonstruktor normalerweise für jedes Objektelement aufrufen muss, das von Markup verarbeitet wird, einschließlich Markuperweiterungsklassen als Objektelemente. Für erweiterte Szenarien können Sie nicht standardmäßige Konstruktionspfade für Klassen definieren. (Weitere Informationen finden Sie unter x:FactoryMethod-Direktive.) Sie sollten diese Muster jedoch nicht zu Markuperweiterungszwecken verwenden, da sich so die Suche nach dem Syntaxmuster sowohl für Designer als auch für Benutzer unformatierter Markups viel schwieriger gestaltet.
Attributieren für eine benutzerdefinierte Markuperweiterung
Um beide Entwurfsumgebungen und bestimmte XAML-Objektwriterszenarien zu unterstützen, sollten Sie einem Markuperweiterungs-Unterstützungstyp mehrere CLR-Attribute zuordnen. Diese Attribute melden die beabsichtigte Markuperweiterungsverwendung.
MarkupExtensionReturnTypeAttribute meldet die Type-Informationen für den Objekttyp, der ProvideValue zurückgibt. Durch seine reine Signatur gibt ProvideValue das Object-Element zurück. Aber verschiedene Consumer könnten präzisere Rückgabetypinformationen erfordern. Dies umfasst Folgendes:
Designer und IDEs, die in der Lage sein könnten, typfähige Unterstützung für Markupsyntaxen bereitzustellen
Erweiterte Implementierungen von SetMarkupExtension-Handlern für Zielklassen, die möglicherweise Reflektion zur Bestimmung des Rückgabetyps einer Markuperweiterung erfordern, anstatt sich basierend auf dem Namen zu bestimmten bekannten MarkupExtension-Implementierungen zu verzweigen
Serialisierung von Markuperweiterungsverwendungen
Wenn ein XAML-Objektwriter eine Markuperweiterungssyntax verarbeitet und ProvideValue aufruft, bleibt der entsprechende Kontext, der zuvor eine Markuperweiterungssyntax war, im XAML-Knotenstream erhalten, jedoch nicht im Objektdiagramm. Im Objektdiagramm wird nur der Wert beibehalten. Wenn Sie Entwurfsszenarios oder andere Gründe zum Beibehalten der ursprünglichen Markuperweiterungsverwendung in der serialisierten Ausgabe haben, müssen Sie eine eigene Infrastruktur entwerfen, um die Markuperweiterungsverwendungen vom Ladepfad des XAML-Knotenstreams zu verfolgen. Sie können Verhalten zum Neuerstellen der Elemente des Knotenstreams aus dem Ladepfad implementieren und die Elemente zur Serialisierung im Speicherpfad an XAML-Writer zurückgeben. Dabei wird der Wert an der entsprechenden Position des Knotenstreams ersetzt.
Markuperweiterungen im XAML-Knotenstream
Wenn Sie mit einem XAML-Knotenstream im Ladepfad arbeiten, wird eine Markuperweiterungsverwendung im Knotenstream als Objekt angezeigt.
Wenn die Markuperweiterungssyntax Positionsargumente enthält, wird sie als Startobjekt mit einem Initialisierungswert dargestellt. Als grobe Textdarstellung sieht der Knotenstream etwa folgendermaßen aus:
StartObject (XamlType ist der Definitionstyp der Markuperweiterung, nicht sein Rückgabetyp)
StartMember (der Name von XamlMember ist _InitializationText)
Value (Der Wert sind die Positionsargumente als Zeichenfolge einschließlich der dazwischenliegenden Trennzeichen.)
EndMember
EndObject
Eine Markuperweiterungsverwendung mit benannten Argumenten wird als Objekt mit Membern der relevanten Namen dargestellt, wobei jeder mit Textzeichenfolge-Werten festgelegt ist.
Das Aufrufen der ProvideValue-Implementierung einer Markuperweiterung erfordert den XAML-Schemakontext, da dafür eine Typzuordnung und die Erstellung eines Markuperweiterungs-Unterstützungstyps erforderlich sind. Dies ist ein Grund, weshalb Markuperweiterungssyntaxen in den standardmäßigen Knotenstreams der .NET Framework-XAML-Dienste auf diese Weise beibehalten werden: Der Reader eines Ladepfads verfügt häufig nicht über den erforderlichen verfügbaren XAML-Schemakontext.
Wenn Sie mit einem XAML-Knotenstream im Speicherpfad arbeiten, enthält eine Objektdiagrammdarstellung normalerweise keine Informationen, denen Sie entnehmen können, dass das zu serialisierende Objekt ursprünglich von einer Markuperweiterungssyntax und einem ProvideValue-Ergebnis bereitgestellt wurde. Szenarien, die Markuperweiterungsverwendungen für Roundtrips ausführen müssen, während sie ebenfalls andere Änderungen im Objektdiagramm erfassen, müssen eigene Techniken zum Beibehalten der Informationen einer Markuperweiterungsverwendung von der ursprünglichen XAML-Eingabe bereitstellen. Beispiel: Um die Markuperweiterungsverwendungen wiederherzustellen, müssen Sie möglicherweise mit dem Knotenstream für den Speicherpfad arbeiten, um Markuperweiterungsverwendungen wiederherzustellen oder um die ursprüngliche XAML und die Roundtrip-XAML auf bestimmte Art zusammenzuführen. Einige Frameworks, die XAML implementieren (z. B. WPF), verwenden Zwischentypen (Ausdrücke), um Fälle darzustellen, in denen die Werte durch Markuperweiterungssyntaxen bereitgestellt werden.
Siehe auch
Referenz
Konzepte
Markuperweiterungen und WPF-XAML