XAML- und benutzerdefinierte Klassen für WPF

Extensible Application Markup Language (XAML) unterstützt mit der Implementierung in .NET die Möglichkeit zum Definieren einer benutzerdefinierten Klasse oder Struktur in einer beliebigen common language runtime (CLR)-Sprache und den anschließenden Zugriff auf diese Klasse mit dem XAML-Markup. Sie können eine Mischung aus Windows Presentation Foundation (WPF)-definierten Typen und den benutzerdefinierten Typen innerhalb derselben Markupdatei verwenden, in der Regel durch die Zuordnung der benutzerdefinierten Typen zu einem XAML-Namespace-Präfix. 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 auf einer XAML-Seite als Stammelement verwendet werden soll.

  • 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 Anwendungsdatei noch separate Assemblys verwenden. Ein wichtiger Nachteil ist jedoch, dass Sie keine Klassen verwenden können, die in derselben Assembly wie das Stammelement anderer XAML-Seiten in dieser Anwendung 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 für WPF-XAML.

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. (Hinweise zu Strukturen finden Sie im folgendem Abschnitt.)

  • Die benutzerdefinierte Klasse darf keine geschachtelte Klasse sein. Geschachtelte Klassen und der "Punkt" in ihrer allgemeinen CLR-Verwendungssyntax beeinträchtigen andere WPF- und/oder XAML-Funktionen, wie z. B. angefügte Eigenschaften.

Die Objektdefinition aktiviert 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 Eigenschaftswerte 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 sollten diese Strukturen einen Typkonverter so implementieren, dass die Werte mithilfe von Zeichenfolgenkonventionen, die die verschiedenen Interpretationen oder Modi der Strukturwerte erstellen, in Attributform ausgedrückt werden können. Die Struktur sollte auch ähnliches Verhalten durch einen nicht standardmäßigen Konstruktor für die Codekonstruktion verfügbar machen.

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 entweder über einen Standardkonstruktor oder einen dedizierten Typkonverter verfügt, auf den ein XAML-Prozessor zugreifen kann. In der CLR-XAML-Implementierung finden XAML-Prozessoren diese Konverter durch die systemeigene Unterstützung für Sprachprimitiven oder durch Anwenden von TypeConverterAttribute auf einen Typ oder ein Member in unterstützenden Typdefinitionen.

Alternativ dazu kann die Eigenschaft auch auf einen abstrakten Klassentyp oder eine Schnittstelle verweisen. Bei abstrakten Klassen oder Schnittstellen besteht für XAML-Analysen die Anforderung, dass der Eigenschaftswert mit Instanzen von praktischen Klassen gefüllt wird, die die Schnittstelle implementieren, oder mit Typinstanzen, die von der abstrakten Klasse abgeleitet sind.

Eigenschaften können in einer abstrakten Klasse deklariert werden, aber nur in praktischen Klassen festgelegt werden, die sich von der abstrakten Klasse ableiten. Das liegt daran, dass die Erstellung des Objektelements für die Klasse einen öffentlichen Standardkonstruktor erfordert.

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 bestimmte Eigenschaften mit dem CultureInfo-Typ. 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-Eigenschaftswert 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 gelegentlich 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.

HinweisHinweis

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 der Funktionen dieses Typs 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 im Markup werden verarbeitet, um zu Membern der Auflistung zu werden. Normalerweise wird der Codezugriff auf die Member einer Auflistung mithilfe von Listen-/Wörterbuchmethoden wie Add oder unter Verwendung eines Indexers 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.

Die .NET-XAML-Implementierung und somit der WPF-XAML-Prozessor verwenden die folgende Definition für die Elemente einer Auflistungseigenschaft. Der Eigenschaftentyp der Eigenschaft muss einen der folgenden Typen implementieren:

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

HinweisHinweis

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 Eigenschaftswert 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 hängt damit zusammen, dass eine Abhängigkeitseigenschaft ihren Standardwert aus Metadaten übernimmt und Sie in der Regel 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 ein lohnenswertes Verfahren sein. Sofern Sie keinen Standardkonstruktor bereitstellen, können Sie die Auflistung nicht explizit als Objektelement deklarieren. 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 Eigenschaftswert 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-Inhaltstyps 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 oder zusätzliche Daten angegeben werden.

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 (WPF)

Benutzerdefinierte Abhängigkeitseigenschaften

Übersicht über das Erstellen von Steuerelementen

Übersicht über Basiselemente

Laden von XAML und Abhängigkeitseigenschaften