Freigeben über


XAML und benutzerdefinierte Klassen

Aktualisiert: November 2007

Extensible Application Markup Language (XAML) unterstützt das Definieren einer benutzerdefinierten Klasse oder Struktur in allen common language runtime (CLR)-Sprachen sowie das Zugreifen auf die Klasse mithilfe von XAML-Code. Dabei können Sie in einer Markupdatei auch eine Mischung aus Windows Presentation Foundation (WPF)-definiertem XAML-Code und den XAML-Tags Ihrer benutzerdefinierten Klasse verwenden. In diesem Thema werden die Anforderungen beschrieben, die eine benutzerdefinierte Klasse erfüllen muss, damit sie als XAML-Element verwendet werden kann.

Dieses Thema enthält folgende Abschnitte.

  • Benutzerdefinierte Klassen in Anwendungen oder Assemblys
  • Anforderungen für eine benutzerdefinierte Klasse als XAML-Element
  • Anforderungen für Eigenschaften einer benutzerdefinierten Klasse als XAML-Attribute
  • Anforderungen für XAML-Ereignishandler-Attributsyntax von Ereignissen einer benutzerdefinierten Klasse
  • Schreiben von Auflistungseigenschaften
  • Deklarieren von XAML-Inhaltseigenschaften
  • Serialisieren von XAML
  • Verwandte Abschnitte

Benutzerdefinierte Klassen in Anwendungen oder Assemblys

Sie können benutzerdefinierte Klassen, die in XAML verwendet werden, auf zwei unterschiedliche Arten definieren: in Code-Behind oder anderem Code, der die primäre Windows Presentation Foundation (WPF)-Anwendung erzeugt, oder als Klasse in einer separaten Assembly, z. B. einer ausführbaren Datei oder DLL, die in einer Klassenbibliothek verwendet wird. Jeder dieser Ansätze hat bestimmte Vor- und Nachteile.

  • Der Vorteil beim Erstellen einer Klassenbibliothek besteht darin, dass benutzerdefinierte Klassen dieser Art von vielen verschiedenen Anwendungen gemeinsam genutzt werden können. Eine separate Bibliothek erleichtert außerdem die Versionssteuerung von Anwendungen und vereinfacht das Erstellen einer Klasse, die Sie auf einer XAML-Seite als Stammelement verwenden möchten.

  • Der Vorteil beim Definieren der benutzerdefinierten Klassen in der Anwendung besteht darin, dass dieses Verfahren relativ einfach ist und die Bereitstellungs- und Testprobleme minimiert, die auftreten können, wenn Sie neben der primären ausführbaren Datei noch separate Assemblys verwenden. Ein wichtiger Nachteil ist jedoch, dass Sie keine Klassen verwenden können, die in derselben Assembly wie das Stammelement einer XAML-Seite definiert sind.

  • Unabhängig davon, ob sie in derselben Assembly oder in unterschiedlichen Assemblys definiert sind, müssen benutzerdefinierte Klassen zwischen dem CLR-Namespace und dem XML-Namespace zugeordnet sein, damit sie in XAML als Elemente verwendet werden können. Weitere Informationen finden Sie unter XAML-Namespaces und Namespacezuordnung.

Anforderungen für eine benutzerdefinierte Klasse als XAML-Element

Ihre Klasse muss die folgenden Anforderungen erfüllen, um als Objektelement instanziiert werden zu können:

  • Die benutzerdefinierte Klasse muss öffentlich sein und einen (parameterlosen) öffentlichen Standardkonstruktor unterstützen. (Strukturen mit verwaltetem Code unterstützen einen Konstruktor dieser Art implizit.)

  • Die benutzerdefinierte Klasse darf keine geschachtelte Klasse sein (geschachtelte Klassen und der "Punkt" in der Syntax führen zu Konflikten mit anderen WPF-Funktionen, z. B. angefügten Eigenschaften).

Sie aktivieren nicht nur die Objektelementsyntax, sondern auch die Eigenschaftenelementsyntax für alle anderen öffentlichen Eigenschaften, die das Objekt als Werttyp verwenden. Der Grund dafür ist, dass das Objekt jetzt als Objektelement instanziiert werden und den Eigenschaftenelementwert einer Eigenschaft dieser Art füllen kann.

Strukturen

Strukturen, die Sie als benutzerdefinierte Typen definieren, können immer in XAML in WPF konstruiert werden. Dies ist darauf zurückzuführen, dass die CLR-Compiler implizit einen Standardkonstruktor für eine Struktur erstellen, die alle Eigenschaftenwerte mit ihren Standardwerten initialisiert. In einigen Fällen ist das Standardkonstruktionsverhalten und/oder die Objektelementverwendung für eine Struktur nicht wünschenswert. Dies kann darauf zurückzuführen sein, dass die Struktur Werte und Funktionen konzeptuell als eine Einheit füllen soll, wobei die enthaltenen Werte sich gegenseitig ausschließende Interpretationen aufweisen und deshalb eine seiner Eigenschaften festgelegt werden kann. Ein WPF-Beispiel für eine solche Struktur ist GridLength. Im Allgemeinen sollte eine solche Struktur einen Typkonverter implementieren, damit die Werte mithilfe von Zeichenfolgenkonventionen in Attributform ausgedrückt werden können, mit denen die unterschiedlichen Interpretationen und Modi der Strukturwerte erstellt werden. Die Struktur sollte auch ein ähnliches Verhalten für die Codeerstellung mithilfe eines nicht-standardmäßigen Konstruktors zeigen.

Anforderungen für Eigenschaften einer benutzerdefinierten Klasse als XAML-Attribute

Eigenschaften müssen auf einen Pro-Wert-Typ (z. B. ein "Primitive") verweisen oder eine Klasse für einen Typ verwenden, der auf Klassenebene entweder über einen Standardkonstruktor oder einen dedizierten Typkonverter verfügt.

Alternativ dazu kann die Eigenschaft auch auf einen abstrakten Klassentyp oder eine Schnittstelle verweisen. Bei abstrakten Klassen oder Schnittstellen besteht zur Laufzeit die Anforderung, dass der Eigenschaftenwert mit Instanzen von praktischen Klassen gefüllt wird, die die Schnittstelle implementieren, oder mit Klasseninstanzen, die von der abstrakten Klasse abgeleitet sind.

Eigenschaften können für eine abstrakte Klasse deklariert werden. Sie können jedoch nur für praktische Klassen festgelegt werden, die von der abstrakten Klasse abgeleitet sind, da für die Erstellung des Objektelements für die Klasse ein öffentlicher Standardkonstruktor in der Klasse erforderlich ist.

Attributsyntax mit Typkonverteraktivierung

Wenn Sie auf Klassenebene einen dedizierten Typkonverter mit Attributen bereitstellen, aktiviert die angewendete Typkonvertierung die Attributsyntax für alle Eigenschaften, die diesen Typ instanziieren müssen. Ein Typkonverter aktiviert keine Objektelementnutzung des Typs. Die Objektelementnutzung wird nur durch das Vorhandensein eines Standardkonstruktors für diesen Typ aktiviert. Aus diesem Grund können Eigenschaften mit Typkonverteraktivierung in der Eigenschaftensyntax im Allgemeinen nicht verwendet werden, es sei denn, der Typ selbst unterstützt ebenfalls die Objektelementsyntax. Eine Ausnahme hierbei ist, dass Sie eine Eigenschaftenelementsyntax angeben können, wobei das Eigenschaftenelement jedoch eine Zeichenfolge enthält. Dies ist im Wesentlichen äquivalent zur Nutzung der Attributsyntax. Diese Art der Nutzung ist nicht gängig, es sei denn, für den Attributwert ist eine stabilere Verarbeitung von Leerräumen erforderlich. Die Beispiele unten zeigen z. B. ein Eigenschaftenelement, das eine Zeichenfolge verwendet, sowie die äquivalente Nutzung eines Attributs:

<Button>Hallo!
  <Button.Language>
    de-DE
  </Button.Language>
</Button>
<Button Language="de-DE">Hallo!</Button>

Beispiele für Eigenschaften, für die die Attributsyntax zulässig ist, die Eigenschaftenelementsyntax mit einem Objektelement per XAML jedoch nicht, sind verschiedene Eigenschaften, die den Cursor-Typ verwenden. Die Cursor-Klasse verfügt über den dedizierten Typkonverter CursorConverter, legt jedoch keinen Standardkonstruktor offen. Die Cursor-Eigenschaft kann also nur per Attributsyntax festgelegt werden, auch wenn es sich beim eigentlichen Cursor-Typ um einen Referenztyp handelt.

Typkonverter pro Eigenschaft

Alternativ dazu kann die Eigenschaft selbst auf der Eigenschaftenebene einen Typkonverter deklarieren. Auf diese Weise wird eine "Minisprache" ermöglicht, die Objekte vom Typ der Eigenschaft inline instanziiert, indem eingehende Zeichenfolgenwerte des Attributs basierend auf dem entsprechenden Typ als Eingabe für einen ConvertFrom-Vorgang verarbeitet werden. Dies wird in der Regel durchgeführt, um einen Accessor als Hilfsmittel bereitzustellen, und nicht nur aus dem alleinigen Grund, das Festlegen einer Eigenschaft in XAML zu ermöglichen. Sie können jedoch auch Typkonverter für Attribute verwenden, für die Sie vorhandene CLR-Typen nutzen möchten, die keinen Standardkonstruktor oder einen Typkonverter mit Attributen bereitstellen. Beispiele aus den WPF-APIs sind einige Eigenschaften, die den CultureInfo-Typ erfordern. In diesem Fall hat WPF den vorhandenen Microsoft .NET Framework-Typ CultureInfo angewendet, um Kompatibilitäts- und Migrationsszenarios besser verarbeiten zu können, die in älteren Versionen von Frameworks verwendet wurden. Der CultureInfo-Typ hat jedoch nicht die erforderlichen Konstruktoren oder die Typkonvertierung auf Typebene verwendet, um eine direkte Nutzung als XAML-Eigenschaftenwert zu ermöglichen.

Beim Offenlegen einer Eigenschaft, die per XAML verwendet werden kann, sollten Sie es besonders als Entwickler von Steuerelementen stets ernsthaft in Erwägung ziehen, die Eigenschaft mithilfe einer Abhängigkeitseigenschaft zu unterstützen. Dies gilt vor allem, wenn Sie die vorhandene Windows Presentation Foundation (WPF)-Implementierung des XAML-Prozessors verwenden, da Sie die Leistung verbessern können, indem Sie DependencyProperty als Unterstützung hinzufügen. Eine Abhängigkeitseigenschaft legt Eigenschaftensystemfunktionen für die Eigenschaft offen, die Benutzer von einer per XAML zugänglichen Eigenschaft erwarten. Dies schließt Funktionen wie Animation, Datenbindung und Unterstützung von Stilen ein. Weitere Informationen finden Sie unter Benutzerdefinierte Abhängigkeitseigenschaften und Laden von XAML und Abhängigkeitseigenschaften.

Schreiben eines Typkonverters und Hinzufügen von Attributen

Sie werden häufig eine benutzerdefinierte abgeleitete TypeConverter-Klasse schreiben müssen, um für Ihren Eigenschaftentyp eine Typkonvertierung bereitzustellen. Anweisungen zur Ableitung von einem Typkonverter und zur Erstellung eines Typkonverters, der die XAML-Nutzung unterstützt, sowie zur Anwendung des TypeConverterAttribute-Elements finden Sie unter TypeConverter und XAML.

Anforderungen für XAML-Ereignishandler-Attributsyntax von Ereignissen einer benutzerdefinierten Klasse

Um als CLR-Ereignis verwendet werden zu können, muss das Ereignis als öffentliches Ereignis einer Klasse offengelegt werden, die einen Standardkonstruktor unterstützt, oder einer abstrakten Klasse, bei der auf das Ereignis über abgeleitete Klassen zugegriffen werden kann. Um das Ereignis auf benutzerfreundliche Weise als Routingereignis verwenden zu können, muss Ihr CLR-Ereignis explizite add- und remove-Methoden implementieren. Diese Methoden fügen der CLR-Ereignissignatur Handler hinzu, entfernen diese und leiten sie an die Methoden AddHandler und RemoveHandler weiter. Die Methoden fügen die Handler dem Routingereignis-Handlerspeicher der Instanz hinzu (bzw. entfernen diese daraus), der das Ereignis zugeordnet ist.

Tipp

Sie können Handler für Routingereignisse direkt registrieren, indem Sie das AddHandler-Ereignis verwenden, und um absichtlich kein CLR-Ereignis zu definieren, das das Routingereignis offenlegt. Dies ist normalerweise nicht zu empfehlen, da das Ereignis die XAML-Attributsyntax zum Anfügen von Handlern nicht aktiviert und da die sich ergebende Klasse eine weniger transparente XAML-Sicht des Klassenobjektmodells bietet.

Schreiben von Auflistungseigenschaften

Eigenschaften, die einen Auflistungstyp verwenden, verfügen über eine XAML-Syntax, mit der Sie Objekte angeben können, die der Auflistung hinzugefügt werden. Diese Syntax weist zwei interessante Funktionen auf.

  • Sie müssen das Objekt, das das Auflistungsobjekt darstellt, in der Objektelementsyntax nicht angeben. Dieser Auflistungstyp ist jeweils implizit vorhanden, wenn Sie eine Eigenschaft in XAML angeben, die einen Auflistungstyp verwendet.

  • Untergeordnete Elemente der Auflistungseigenschaft werden verarbeitet, um zu Membern der Auflistung zu werden. Normalerweise wird der Codezugriff auf die Member einer Auflistung mithilfe von Auflistungsmethoden wie "Add" oder unter Verwendung einer Indexereigenschaft für Auflistungen durchgeführt. Die XAML-Syntax unterstützt jedoch keine Methoden oder Indexer. Auflistungen sind bekanntermaßen eine häufige Anforderung an die Erstellung einer Struktur von Elementen, und Sie müssen diese Auflistungen mithilfe von deklarativen XAML-Daten füllen. Aus diesem Grund werden untergeordnete Elemente einer Auflistungseigenschaft verarbeitet, indem sie der Auflistung hinzugefügt werden, bei der es sich um den Typwert der Auflistungseigenschaft handelt.

Der WPF-XAML-Prozessor verwendet die folgende Definition, um eine Auflistungseigenschaft zu beschreiben. Der Eigenschaftentyp der Eigenschaft muss einen der folgenden Typen implementieren:

Jeder dieser Typen verfügt über eine Add-Methode, die vom XAML-Prozessor verwendet wird, um der zugrunde liegenden Auflistung Elemente hinzuzufügen.

Tipp

Die allgemeinen List-und Dictionary-Schnittstellen (IList<T> und IDictionary<TKey, TValue>) werden für die Auflistungserkennung mithilfe des WPF XAML-Prozessors nicht unterstützt. Sie können jedoch als Basisklasse die List<T>-Klasse verwenden, da sie IList direkt implementiert, bzw. die Dictionary<TKey, TValue>-Klasse, da diese IDictionary direkt implementiert.

Beim Deklarieren einer Eigenschaft, die eine Auflistung verwendet, sollten Sie sorgfältig darauf achten, wie der Eigenschaftenwert in neuen Instanzen des Typs initialisiert wird. Wenn Sie die Eigenschaft nicht als Abhängigkeitseigenschaft implementieren, ist es ratsam, dass die Eigenschaft ein Unterstützungsfeld verwendet, das den Auflistungstypkonstruktor aufruft. Wenn es sich bei der Eigenschaft um eine Abhängigkeitseigenschaft handelt, kann es erforderlich sein, die Auflistungseigenschaft als Teil des standardmäßigen Typkonstruktors zu initialisieren. Dies ist der Fall, weil eine Abhängigkeitseigenschaft ihren Standardwert aus den Metadaten abruft und weil Sie normalerweise nicht möchten, dass der Anfangswert einer Auflistungseigenschaft eine statische freigegebene Auflistung ist (pro enthaltender Typinstanz sollte eine Auflistungsinstanz vorhanden sein). Weitere Informationen finden Sie unter Benutzerdefinierte Abhängigkeitseigenschaften.

Sie können für Ihre Auflistungseigenschaft einen benutzerdefinierten Auflistungstyp implementieren. Aufgrund der impliziten Behandlung der Auflistungseigenschaft muss der benutzerdefinierte Auflistungstyp keinen Standardkonstruktor bereitstellen, um implizit in XAML verwendet werden zu können. Sie können für den Auflistungstyp jedoch optional einen Standardkonstruktor bereitstellen. Dies kann ratsam sein, da Sie die Auflistung nur dann explizit als Objektelement deklarieren können, wenn Sie einen Standardkonstruktor bereitstellen. Einige Entwickler, die Markup verwenden, bevorzugen aus Gründen des Markupstils ggf. explizite Auflistungen. Außerdem kann ein Standardkonstruktor die Initialisierungsanforderungen vereinfachen, wenn Sie neue Objekte erstellen, die Ihren Auflistungstyp als Eigenschaftenwert verwenden.

Deklarieren von XAML-Inhaltseigenschaften

Die XAML-Sprache verwendet eine XAML-Inhaltseigenschaft. Jede Klasse, die in der Objektsyntax verwendet werden kann, kann über genau eine XAML-Inhaltseigenschaft verfügen. Um eine Eigenschaft als XAML-Inhaltseigenschaft Ihrer Klasse zu deklarieren, wenden Sie das ContentPropertyAttribute als Teil der Klassendefinition an. Geben Sie den Namen der gewünschten XAML-Inhaltseigenschaft im Attribut als Name an.

Sie können eine Auflistungseigenschaft als XAML-Inhaltseigenschaft angeben. Dies führt zur Verwendung dieser Eigenschaft, wobei das Objektelement über ein oder mehrere untergeordnete Elemente verfügen kann, ohne dass dazwischen stehende Auflistungsobjektelemente oder Eigenschaftenelementtags erforderlich sind. Diese Elemente werden dann als Wert für die XAML-Inhaltseigenschaft behandelt und der unterstützenden Auflistungsinstanz hinzugefügt.

Einige vorhandene WPF-XAML-Inhaltseigenschaften verwenden den Eigenschaftentyp von Object. Auf diese Weise wird eine XAML-Inhaltseigenschaft aktiviert, die primitive Werte wie String sowie einen einzelnen Verweisobjektwert verwenden kann. Wenn Sie dieses Modell anwenden, ist Ihr Typ für die Typermittlung und die Behandlung der möglichen Typen zuständig. Ein häufiger Grund für die Verwendung eines Object-Typmodells ist die gleichzeitige Unterstützung eines einfachen Verfahrens zum Hinzufügen von Objektinhalt als Zeichenfolge (die standardmäßig dargestellt wird) und eines erweiterten Verfahrens zum Hinzufügen von Objektinhalt, bei dem eine andere Darstellungsart als die Standarddarstellung angegeben wird.

Serialisieren von XAML

Bei bestimmten Szenarios, z. B. als Entwickler von Steuerelementen, sollten Sie auch sicherstellen, dass alle Objektdarstellungen, die in XAML instanziiert werden können, auch zurück in äquivalente XAML-Daten serialisiert werden können. Die Serialisierungsanforderungen sind in diesem Thema nicht beschrieben. Weitere Informationen finden Sie unter Übersicht über das Erstellen von Steuerelementen und Elementstruktur und Serialisierung.

Siehe auch

Konzepte

Übersicht über XAML

Benutzerdefinierte Abhängigkeitseigenschaften

Übersicht über das Erstellen von Steuerelementen

Übersicht über Basiselemente

Laden von XAML und Abhängigkeitseigenschaften