Definieren benutzerdefinierter Typen für die Verwendung in .NET XAML-Diensten

Wenn Sie benutzerdefinierte Typen definieren, bei denen es sich um Geschäftsobjekte handelt oder um Typen, die nicht von bestimmten Frameworks abhängig sind, gibt es bestimmte bewährte Methoden für XAML, die Sie befolgen können. Wenn Sie diese Vorgehensweisen befolgen, können die .NET XAML-Dienste und deren XAML-Reader und XAML-Writer die XAML-Merkmale Ihres Typs erkennen und ihn in einem XAML-Knotendatenstrom mithilfe des XAML-Typsystems angemessen darstellen. In diesem Thema werden bewährte Methoden für Typ- und Memberdefinitionen sowie die CLR-Attributierung von Typen oder Elementen beschrieben.

Konstruktormuster und Typdefinitionen für XAML

Um als Objektelement instanziiert werden zu können, muss eine benutzerdefinierte Klasse die folgenden Anforderungen erfüllen:

  • Die benutzerdefinierte Klasse muss öffentlich sein und einen parameterlosen öffentlichen Konstruktor verfügbar machen. (Hinweise zu Strukturen finden Sie im folgenden Abschnitt.)

  • Die benutzerdefinierte Klasse darf keine geschachtelte Klasse sein. Der zusätzliche „Punkt“ im vollständigen Pfadnamen macht die Trennung zwischen Klasse und Namespace uneindeutig und beeinträchtigt andere XAML-Features wie angefügte Eigenschaften. Wenn ein Objekt als Objektelement instanziiert werden kann, kann das erstellte Objekt die Form des Eigenschaftselements für alle Eigenschaften ausfüllen, die das Objekt als zugrunde liegenden Typ verwenden.

Sie können weiterhin Objektwerte für Typen bereitstellen, die diesen Kriterien nicht entsprechen, wenn Sie einen Wertkonverter aktivieren. Weitere Informationen finden Sie unter Typkonverter und Markuperweiterungen für XAML.

Strukturen

Strukturen können in XAML stets anhand der CLR-Definition erstellt werden. Dies liegt daran, dass ein CLR-Compiler implizit einen parameterlosen Konstruktor für eine Struktur erstellt. Dieser Konstruktor initialisiert alle Eigenschaftswerte mit ihren Standardwerten.

In einigen Fällen sind für Strukturen das standardmäßige Konstruktionsverhalten nicht ratsam. Das könnte daran liegen, dass die Struktur dazu gedacht ist, Werte zu füllen und konzeptionell als Vereinigung zu funktionieren. Als Vereinigung können die enthaltenen Werte sich gegenseitig ausschließende Interpretationen haben, weshalb keine ihrer Eigenschaften festlegbar ist. Ein Beispiel für eine solche Struktur im WPF-Vokabular ist GridLength. Solche Strukturen müssen einen Typkonverter implementieren, sodass die Werte in Attributform ausgedrückt werden können, und zwar gemäß Zeichenfolgenkonventionen, die die verschiedenen Interpretationen oder Modi der Strukturwerte erstellen. Die Struktur muss über einen parameterlosen Konstruktor ein ähnliches Verhalten auch für die Codekonstruktion verfügbar machen.

Schnittstellen

Schnittstellen können als zugrunde liegende Typen von Membern verwendet werden. Das XAML-Typsystem prüft die zuweisbare Liste und erwartet, dass das Objekt, das als Wert angegeben ist, der Schnittstelle zugewiesen werden kann. Es gibt kein Konzept, wie die Schnittstelle als XAML-Typ dargestellt werden muss, solange ein entsprechender zuweisbarer Typ die XAML-Konstruktionsanforderungen unterstützt.

Factorymethoden

Factorymethoden sind ein XAML 2009-Feature. Sie ändern das XAML-Prinzip, dass Objekte über parameterlose Konstruktoren verfügen müssen. Factorymethoden sind in diesem Artikel nicht dokumentiert. Weitere Informationen finden Sie unter x:FactoryMethod-Anweisung.

Enumerationen

Enumerationen weisen ein natives XAML-Typkonvertierungsverhalten auf. In XAML angegebene Namen von Enumerationskonstanten werden anhand des zugrunde liegenden Enumerationstyps aufgelöst und geben den Enumerationswert an einen XAML-Objektwriter zurück.

XAML unterstützt für Enumerationen einen Flagsstil mit angewendetem FlagsAttribute. Weitere Informationen finden Sie unter XAML-Syntax im Detail. (XAML-Syntax im Detail ist für die WPF-Zielgruppe geschrieben, aber die meisten Informationen in diesem Thema sind für XAML relevant, die nicht spezifisch für ein bestimmtes Implementierungsframework ist.)

Memberdefinitionen

Typen können Member für die XAML-Verwendung definieren. Es ist möglich, dass Typen Member definieren, die in XAML verwendet werden können, auch wenn dieser spezifische Typ nicht in XAML verwendet werden kann. Dies ist aufgrund der CLR-Vererbung möglich. Solange ein Typ, der das Element erbt, die XAML-Verwendung als Typ unterstützt und das Element die XAML-Verwendung für seinen zugrunde liegenden Typ unterstützt oder eine native XAML-Syntax zur Verfügung hat, kann dieses Element in XAML verwendet werden.

Eigenschaften

Wenn Sie Eigenschaften als öffentliche CLR-Eigenschaften mit den typischen CLR-Accessormustern get und set und sprachgerechten Schlüsselwörtern definieren, kann das XAML-Typsystem die Eigenschaft als Member mit den entsprechenden Informationen für XamlMember-Eigenschaften wie IsReadPublic und IsWritePublic melden.

Bestimmte Eigenschaften können eine Textsyntax ermöglichen, indem TypeConverterAttribute angewendet wird. Weitere Informationen finden Sie unter Typkonverter und Markuperweiterungen für XAML.

Bei Fehlen einer Textsyntax oder nativen XAML-Konvertierung sowie einer weiteren Dereferenzierung, wie z. B. Verwendung einer Markuperweiterung, muss der Typ einer Eigenschaft (TargetType im XAML-Typsystem) in der Lage sein, eine Instanz an einen XAML-Objektwriter zurückzugeben, indem der Zieltyp als CLR-Typ behandelt wird.

Bei Verwenden von XAML 2009 kann die x:Reference-Markuperweiterung verwendet werden, um Werte bereitzustellen, wenn die vorstehenden Überlegungen nicht zutreffen. Dies ist jedoch eher ein Problem der Verwendung als der Typdefinition.

Ereignisse

Wenn Sie Ereignisse als öffentliche CLR-Ereignisse definieren, kann das XAML-Typsystem das Ereignis als Member mit IsEvent als true melden. Die Verknüpfung der Ereignishandler liegt nicht im Rahmen der Möglichkeiten von .NET XAML-Diensten, sondern wird spezifischen Frameworks und Implementierungen überlassen.

Methoden

Inlinecode für Methoden ist keine XAML-Standardfunktionalität. In den meisten Fällen verweisen Sie nicht direkt aus XAML auf Methodenmember, denn die Rolle von Methoden in XAML besteht nur darin, Unterstützung für bestimmte XAML-Muster zu bieten. Die x:FactoryMethod-Anweisung ist eine Ausnahme.

Felder

Die Leitlinien für den CLR-Entwurf raten von nicht statischen Feldern ab. Bei statischen Feldern können Sie nur über die x:Static-Markuperweiterung auf statische Feldwerte zugreifen. In diesem Fall machen Sie in der CLR-Definition nichts Besonderes, um ein Feld für x:Static-Verwendungen verfügbar zu machen.

Anfügbare Member

Anfügbare Member werden in XAML durch ein Muster für eine Accessormethode für einen definierenden Typ verfügbar gemacht. Der definierende Typ selbst muss nicht als Objekt in XAML verwendbar sein. Ein gängiges Muster besteht darin, eine Dienstklasse zu deklarieren, deren Rolle darin besteht, den anfügbaren Member zu besitzen und die damit verbundenen Verhaltensweisen zu implementieren, aber keine andere Funktion, wie z. B. eine Darstellung auf der Benutzeroberfläche, zu erfüllen. In den folgenden Abschnitten stellt der Platzhalter propertyName den Namen des anfügbaren Members dar. Dieser Name muss in der XamlName-Grammatik gültig sein.

Achten Sie auf Namenskonflikte zwischen diesen Mustern und anderen Methoden eines Typs. Wenn ein Member vorhanden ist, der einem der Muster entspricht, kann er von einem XAML-Prozessor als Pfad für die Verwendung eines anfügbaren Members interpretiert werden, auch wenn das nicht Ihre Absicht war.

Der GetPropertyName-Accessor

Die Signatur für den GetPropertyName-Accessor muss wie folgt sein:

public static object GetPropertyName(object target)

  • Das target-Objekt kann als spezifischerer Typ in Ihrer Implementierung angegeben werden. Damit können Sie die Verwendung Ihres anfügbaren Members begrenzen. Verwendungen außerhalb des beabsichtigten Bereichs lösen ungültige Umwandlungsausnahmen aus, die dann als XAML-Analysefehler ausgegeben werden. Der Parametername target ist nicht vorgeschrieben, wird aber in den meisten Implementierungen gemäß Konvention target genannt.

  • Der Rückgabewert kann als spezifischerer Typ in Ihrer Implementierung angegeben werden.

Um eine für TypeConverter aktivierte Textsyntax für die Attributverwendung des anfügbaren Members zu unterstützen, wenden Sie TypeConverterAttribute auf den Accessor GetPropertyName an. Die Anwendung auf get anstelle von set mag nicht intuitiv erscheinen, aber diese Konvention kann das Konzept der nur lesbaren anfügbaren Member unterstützen, die serialisierbar sind, was in Designerszenarien nützlich ist.

Der SetPropertyName-Accessor

Die Signatur für den SetPropertyName-Accessor muss wie folgt sein:

public static void SetPropertyName(object target, object value)

  • Das Objekt target kann in Ihrer Implementierung als spezifischerer Typ angegeben werden, und zwar mit derselben Logik und denselben Konsequenzen wie im vorherigen Abschnitt beschrieben.

  • Das value-Objekt kann als spezifischerer Typ in Ihrer Implementierung angegeben werden.

Denken Sie daran, dass der Wert für diese Methode die Eingabe aus der XAML-Verwendung ist, normalerweise in Form von Attributen. Von der Attributform muss es Wertkonverterunterstützung für eine Textsyntax geben, und Sie attribuieren auf den Accessor von GetPropertyNames.

Anfügbare Memberspeicher

Die Accessormethoden reichen in der Regel nicht aus, um anfügbare Memberwerte in einem Objektgraphen zu platzieren oder um Werte aus dem Objektgraphen abzurufen und ordnungsgemäß zu serialisieren. Um diese Funktionalität bereitzustellen, müssen die target-Objekte in den vorherigen Accessorsignaturen in der Lage sein, Werte zu speichern. Der Speichermechanismus muss mit dem Prinzip des anfügbaren Members übereinstimmen, d. h. der Member kann an Ziele angefügt werden, bei denen der anfügbare Member nicht in der Memberliste enthalten ist. .NET XAML-Dienste bieten über die APIs IAttachedPropertyStore und AttachablePropertyServices eine Implementierungstechnik für anfügbare Memberspeicher. IAttachedPropertyStore wird von XAML-Autoren verwendet, um die Speicherimplementierung zu ermitteln, und muss in dem Typ implementiert werden, der das target für die Accessors ist. Die statischen AttachablePropertyServices-APIs werden im Rumpf der Accessors verwendet und verweisen auf das anfügbare Element anhand seiner AttachableMemberIdentifier.

Die ordnungsgemäße Attributierung Ihrer Typen, Member und Assemblys ist wichtig, um XAML-Typsysteminformationen den .NET XAML-Diensten zu melden. Das Melden von XAML-Typsysteminformationen ist relevant, wenn eine der folgenden Situationen vorliegt:

  • Sie beabsichtigen, Ihre Typen für die Verwendung mit XAML-Systemen zu verwenden, die direkt auf den XAML-Readern und -Writern der .NET XAML-Dienste basieren.
  • Sie definieren oder verwenden ein XAML verwendendes Framework, das auf diesen XAML-Readern und -Writern basiert.

Eine Auflistung der einzelnen XAML-bezogenen Attribute, die für die XAML-Unterstützung Ihrer benutzerdefinierten Typen relevant sind, finden Sie unter XAML-bezogene CLR-Attribute für benutzerdefinierte Typen und Bibliotheken.

Verwendung

Die Verwendung benutzerdefinierter Typen erfordert, dass der Markupautor ein Präfix für die Assembly und den CLR-Namespace zuordnet, die den benutzerdefinierten Typ enthalten. Dieses Verfahren ist in diesem Thema nicht dokumentiert.

Zugriffsebene

XAML bietet die Möglichkeit, Typen mit der Zugriffsebene internal zu laden und zu instanziieren. Diese Möglichkeit wird angeboten, damit der Benutzercode seine eigenen Typen definieren und diese Klassen dann aus dem Markup instanziieren kann, das ebenfalls zum Geltungsbereich desselben Benutzercodes gehört.

Ein Beispiel für WPF ist, wenn der Benutzercode ein UserControl definiert, das als Möglichkeit zur Umgestaltung eines Benutzeroberflächenverhaltens gedacht ist, aber nicht als Teil eines möglichen Erweiterungsmechanismus, der durch die Deklaration der unterstützenden Klasse mit der Zugriffsebene public impliziert werden könnte. Ein solches UserControl kann mit internal-Zugriff deklariert werden, wenn der unterstützende Code in dieselbe Assembly kompiliert wird, in der auf ihn als XAML-Typ verwiesen wird.

Bei einer Anwendung, die XAML mit voller Vertrauenswürdigkeit lädt und XamlObjectWriter verwendet, ist das Laden von Klassen mit der Zugriffsebene internal stets aktiviert.

Bei einer Anwendung, die XAML mit teilweiser Vertrauenswürdigkeit lädt, können Sie die Eigenschaften der Zugriffsebene mithilfe der XamlAccessLevel-API steuern. Darüber hinaus müssen Verzögerungsmechanismen (wie z. B. das WPF-Vorlagensystem) in der Lage sein, alle Berechtigungen auf Zugriffsebene weiterzugeben und sie für die späteren Auswertungen zur Laufzeit beizubehalten. Dies wird intern durch Übergabe der XamlAccessLevel-Informationen gehandhabt.

WPF-Implementierung

WPF XAML verwendet ein Zugriffsmodell mit teilweiser Vertrauenswürdigkeit. Wenn BAML unter teilweiser Vertrauenswürdigkeit geladen wird, ist der Zugriff auf AssemblyAccessTo für die Assembly eingeschränkt, die die BAML-Quelle ist. Zur Verzögerung verwendet WPF IXamlObjectWriterFactory.GetParentSettings als Mechanismus zur Übergabe der Informationen auf Zugriffsebene.

In der WPF XAML-Terminologie ist ein interner Typ ein Typ, der von derselben Assembly definiert wird, die auch den referenzierenden XAML-Code enthält. Ein solcher Typ kann über einen XAML-Namespace zugeordnet werden, der den Teil „assembly=“ einer Zuordnung absichtlich weglässt, wie z. B. xmlns:local="clr-namespace:WPFApplication1". Wenn BAML auf einen internen Typ verweist und dieser Typ die Zugriffsebene internal hat, wird eine GeneratedInternalTypeHelper-Klasse für die Assembly generiert. Wenn Sie GeneratedInternalTypeHelper vermeiden möchten, müssen Sie entweder die Zugriffsebene public verwenden oder die betreffende Klasse in eine separate Assembly einbinden und diese Assembly abhängig gestalten.

Weitere Informationen