19.3 XAML 

In diesem Abschnitt werden einige Spezialitäten des XAML-Dialekts besprochen, die in (fast) allen Programmen verwendet werden, die von den Klassen der WPF Gebrauch machen. Dazu gehören sowohl Windows-Programme als auch solche, die für das Webbrowser-Plugin Silverlight geschrieben worden sind.
Der Compiler bildet XAML in korrespondierende Sprachkonstrukte ab:
- Elementname
R Typ mit öffentlichem parameterlosen Konstruktor
- Attributname
R Eigenschaft, Ereignis
- Namensraum
R Namensraum in der CLR
19.3.1 Standardnamensräume 

XAML benötigt mindestens zwei Namensräume, die in einem neuen WPF-Projekt von Visual Studio automatisch hinzugefügt werden (der zweite enthält nur wenige Namen). Dadurch hat das Programm Zugriff auf alle wesentlichen Namen und Klassen, die zur Erstellung einer grafischen Oberfläche benötigt werden. Alle Element- und Attributnamen ohne explizite Angaben zum Namensraum verwenden den Namensraum, der keinen Namen hinter xmlns hat. Haben alle Namensräume Namen, müssen ausnahmslos alle Element- und Attributnamen mit Namensraum: qualifiziert werden. Es ist daher praktisch, um sich Tipparbeit zu sparen, dem Namensraum keinen Namen zu geben, in dem die meisten zu verwendenden Namen definiert sind. Die eckigen Klammern in der folgenden Syntax enthalten den optionalen Namensraum; kursiv gesetzte Teile können Sie Ihren Bedürfnissen anpassen. Zwei konkrete Spezifikationen sind nach der Syntax angegeben.
xmlns[:Namensraum]= "<Schema>" |
19.3.2 Zusätzliche Namensräume 

In vielen WPF-Anwendungen müssen Sie über die Standardangabe hinaus noch weitere Namensräume angeben: Manchmal ist es ein Namensraum der Common Language Runtime (CLR), manchmal auch ein Namensraum, der in der aktuellen Anwendung beschrieben ist.
Eine typische Angabe lautet:
xmlns:data="clr-namespace:System.Data;assembly=System.Data"
Der Alias, hier data, ist frei wählbar, muss aber eindeutig sein. Die Namensraumspezifikation setzt sich aus clr-namespace, einem Doppelpunkt und dem Namensraum zusammen. Danach folgt der Verweis auf die Bibliothek, in der der Namensraum definiert ist. Die Endung .dll wird ignoriert. Ist der Namensraum Teil des aktuellen Projekts, kann die Angabe entfallen.
xmlns:src="clr-namespace:WpfApplication2"
19.3.3 Tags 

Jedem Tag in XAML entspricht eine WPF-Klasse. Durch die Angabe in XAML wird das Element über den parameterlosen Konstruktor der Klasse automatisch instanziiert.
Hinweis |
Eigene Klassen in XAML müssen einen parameterlosen Konstruktor haben. |
Wie in XML können Elemente geschachtelt werden. Diese logische Struktur wird automatisch auf das Aussehen abgebildet, und Kindelemente sind Teil des Elternelements. Diese knappe Feststellung stellt eine der größten Neuerungen von WPF dar: das neue Inhaltsmodell. So können viele Steuerelemente (fast) beliebige andere Steuerelemente enthalten. Durch die schier endlosen Kombinationsmöglichkeiten sind praktisch alle Forderungen an eine Benutzeroberfläche erfüllbar. Dies birgt aber auch die Gefahr, »unbenutzbare« Programme zu entwerfen. Sie sollten sich daher am Motto »Weniger ist mehr« orientieren.
Das folgende Beispiel soll nur andeuten, wie weit man das neue Inhaltsmodell treiben kann. Eine selbst aktive Listbox auf eine aktive Taste zu setzen, ist gewöhnungsbedürftig. Das Aussehen zeigt Abbildung 19.1.
<Button Name="button1">
<ListBox Width="100">
<ListBoxItem>Franz</ListBoxItem>
<ListBoxItem>Willi</ListBoxItem>
<Label Background="Red">Ich bin ein 'Label'</Label>
</ListBox>
</Button>
Abbildung 19.1 Verschachtelte WPF-Komponenten
Das Attribut Name der Elemente hat eine besondere Bedeutung. Es gibt den Namen der Variablen an, unter der das automatisch generierte Objekt im Programmcode ansprechbar ist. Innerhalb eines Gültigkeitsbereiches muss damit die vergebene Bezeichnung eindeutig sein. Bereiche werden im Wesentlichen durch das Wurzelelement, Vorlagen und Stile gebildet.
Hinweis |
Auf Elemente ohne Attribut Name kann im Programm nicht zugegriffen werden. |
19.3.4 Attribute 

Eigenschaften und Ereignisse können in zweierlei Weise in XAML spezifiziert werden. Immer möglich ist eine Spezifikation eines solchen Wertes als Kindelement. Diese Schreibweise wird Property-Element-Syntax (Eigenschaftselementschreibweise) genannt. Dem Wertnamen wird der Elementname, durch einen Punkt getrennt, vorangestellt. Ist der Wert »einfach«, kann er als Attribut des Elements angegeben werden. Kursiv gesetzte Namen der folgenden Syntax passen Sie wieder Ihren Bedürfnissen an. Im folgenden Beispiel wird die Beschriftung einer Taste in beiden »Geschmacksrichtungen« angegeben und in einer dritten Schreibweise, die nur für den Inhalt eines Elements zugelassen ist:
<Element><Element.Attribut>Wert</Element.Attribut><Element> |
<Button><Button.Content>Beschriftung</Button.Content></Button>
<Button Content="Beschriftung"/>
<Button>Beschriftung</Button>
Es gibt Fälle, in denen die erste Schreibweise verwendet werden muss. Das folgende Beispiel definiert einen linearen Farbverlauf als Hintergrund der Taste:
<Button>
<Button.Background>
<LinearGradientBrush>
<GradientStop Color="Red" Offset="0.0" />
<GradientStop Color="White" Offset="1.0" />
</LinearGradientBrush>
</Button.Background>
</Button>
Einige Attributwerte sind aus mehreren Werten zusammengesetzt. Sie können wie oben als Element oder Attribut angegeben werden. Im folgenden Beispiel sind die Werte durch Kommata voneinander getrennt.
<Elem><Elem.Attribut><Eigenschaft AttX="X" AttY="Y"></Elem.Attribut></Elem> |
<Button><Button.Margin>
<Thickness Left="10" Top="5" Right="10" Bottom="5">
</Button.Margin></Button>
<Button Margin="10,5,10,5">
19.3.5 Konvertierungen 

Damit die Interpretation von Attributwerten funktioniert, muss der Typ des Attributs mit einem Konvertierer verknüpft sein, der die eigentliche Analyse der Zeichenkette übernimmt. Im letzten Beispiel hat Margin den Typ Padding und einen korrespondierenden Konvertierer vom Typ PaddingConverter, der sich um die Analyse der durch Kommata getrennten Werte kümmert. Die allgemeine Syntax lautet (kursive Bezeichner passen Sie Ihren Bedürfnissen an, Alternativen sind durch einen senkrechten Strich getrennt):
<... TypeConverter(GetType(Konvertierer))> _ |
Wenn ein Element eine Liste erwartet, aber nur ein einzelnes Kind direkt enthält, wird automatisch eine Auflistung mit einem einzigen Element erzeugt, wie in der folgenden Syntax zu sehen ist. Daher sind die beiden folgenden Beispieltasten gleichwertig, obwohl die mittleren Zeilen unterschiedlich sind.
<ElementX><ElementX.Children><ElementY/></ElementX.Children></ElementX> |
<StackPanel>
<StackPanel.Children><Button/></StackPanel.Children>
</StackPanel>
<StackPanel>
<Button/>
</StackPanel>
19.3.6 Indirektion und Markup-Erweiterungen 

Werte können indirekt spezifiziert werden, ein Wert ist dann durch den Wert eines anderen Elements oder Objekts gegeben. Dadurch können Sie einfach konsistente Oberflächen schaffen. Zum Beispiel kann die Hintergrundfarbe eines Elements die aller anderen Elemente bestimmen. Ändern Sie diese, sind automatisch alle anderen Farben auch angepasst.
Um die Auflösung der Referenz auf einen Wert (dies ergibt den Wert selbst) kümmert sich eine Klasse, die von System.Windows.Markup.MarkupExtension abgeleitet sein muss. Oft wird zur Kennzeichnung das Suffix Extension an den Klassennamen angehängt, das in XAML ausgelassen werden darf.
In der folgenden Syntax muss ExtensionElement von MarkupExtension abgeleitet sein. Die kursiv gesetzten Namen wählen Sie nach Bedarf. Welche Attribute NameX nötig sind, wird durch ExtensionElement bestimmt; sehr selten werden sie ganz fehlen. Innerhalb von WertX dürfen andere Referenzierungen stehen. Im folgenden Beispiel bestimmt die Hintergrundfarbe der ersten Taste die der zweiten. Die Verwendung der Attribute wird durch die Klasse Binding festgelegt. Eine eigene Erweiterung könnte zum Beispiel nur das Referenzobjekt spezifizieren und automatisch dessen Hintergrundfarbe nehmen.
<ExtensionElement ... /> |
<Button Name="Nr1" Background="Yellow">Quelle</Button>
<Button Name="Nr2">
<Button.Background>
<Binding ElementName="Nr1" Path="Background" />
</Button.Background>
Ziel
</Button>
Die Ableitung von MarkupExtension hat eine weitere Konsequenz. Durch eine besondere Syntax kann die Indirektion auch in Attributen verwendet werden, die nur Zeichenketten erlauben. Dazu wird die Spezifikation statt in Elementsyntax als Zeichenkette mit geschweiften Klammern geschrieben, und Anführungszeichen werden weggelassen. Auch hier sind Schachtelungen erlaubt.
Wie üblich passen Sie kursiv gesetzte Namen in der folgenden Syntax Ihren Bedürfnissen an. Wieder bestimmt ExtensionElement, ob und welche NameX nötig sind. Wegen der besonderen Bedeutung zeigen die beiden letzten Zeilen zusätzlich zwei spezielle Erweiterungen. Im folgenden Beispiel wird die Hintergrundfarbe einer Taste einmal direkt und dreimal indirekt angegeben. Die letzte Farbangabe wird während der Laufzeit angepasst, wenn die Hintergrundfarbe des Desktops verändert wird, während das Programm läuft.
<Element Attribut="{ExtensionElement Name1=Wert1, Name2=Wert2 ...}"/> |
<Button Background="Yellow" Name="Quelle">Farbe</Button>
<Button
Background="{Binding ElementName=Quelle, Path=Background}"
>fest</Button>
<Button
Background="{x:Static SystemColors.DesktopBrush}">statisch</Button>
<Button
Background="{DynamicResource {x:Static SystemColors.DesktopBrushKey}}"
>dynamisch</Button>
Hinweis |
Eine einzelne geschweifte Klammer »{« im Text erzeugen Sie durch »{}{». |
19.3.7 Verbundene Attribute 

Einige Attribute beziehen sich auf umgebende Elemente. Sie werden verbundene Attribute genannt, und ihrem Namen wird, getrennt durch einen Punkt, der Elementname des umgebenden Elements vorangestellt. Kursiv gesetzte Namen in der Syntax sind wieder frei wählbar. Das folgende Beispiel platziert die Taste relativ zum oberen Rand der Leinwand.
<ElementX><ElementY ElementX.Attribut="Wert"/></ElementX> |
<Canvas><Button Canvas.Top="10"/></Canvas>
Hinweis |
Im Code lautet die Syntax: Klasse.SetAttribut(ElementY, Wert). |
19.3.8 Code Behind 

Schließlich muss noch die Klasse an XAML gebunden werden, die die Funktionalität enthält, die nicht in XAML formuliert ist. Dies passiert durch das Attribut x:Class. Die folgenden zwei Codeabschnitte zeigen den XAML-Teil einer Applikation sowie den benutzerdefinierten Teil der Klasse des x:Class-Attributs. Theoretisch kann der Code der Klasse mit <x:Code> <![CDATA[...]]></x:Code> in den XAML-Teil eingebunden werden (Sonderzeichen sind dann zu kodieren (siehe Abschnitt 19.2.7, »Entitäten«). Ich halte dies aber nicht für ratsam, da sowohl die Trennung von Design und Code aufgehoben wird als auch die Flexibilität reduziert wird.
<Window |
19.3.9 Schlüsselwörter 

XAML definiert eine Reihe von Schlüsselwörtern, die besondere Aspekte bei der Entwicklung berücksichtigen. Die Tabelle 19.1 stellt Ihnen einige davon vor. In den folgenden Kapiteln werden Sie auf einige von diesen in den Beispielen stoßen.
Schlüsselwort | Bedeutung |
x:Class |
Verbindet das Wurzelelement in XAML mit der Code-Behind-Datei. |
x:Code |
Definition eines Inline-Codebereichs (mischt Design und Code!) |
x:Key |
Der eindeutige Name eines Elements in einer Ressource |
x:Name |
Benennung von Elementen, die keine Eigenschaft Name haben. |
Zum Abschluss zeigt Tabelle 19.2 die Markup-Erweiterungen im Namensraum x.
Erweiterung | Beschreibung |
x:Array |
Definition von Arrays in XAML |
x:Null |
Der Wert Nothing in XAML |
x:Static |
Referenzierung typgebundener Werte (Shared sowie Konstanten) |
x:Type |
Attributwert vom Typ Type (zum Beispiel in Stildefinitionen) |
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.