Grafische Tools können z.B. XAML-Code exportieren und auch direkt Visual Studio-Projekte nutzen (z.B. Expression Blend). Der XAML-Code wird dann in ein Projekt importiert (oder das Projekt wurde direkt bearbeitet) und der Entwickler heftet die Programmlogik an die bereits erstellte Oberfläche. Soweit zur Theorie.
XAML basiert auf der Syntax von XML. Dies bedeutet eine XAML-Datei besitzt ein Wurzelelement, welches alle anderen Elemente einschließt. Weiterhin müssen alle Elemente korrekte verschachtelt sein, z.B.
<StackPanel> <Button> ... <Button> <StackPanel>statt
<StackPanel> <Button> ... </StackPanel> </Button>Attribute werden in doppelte Anführungszeichen gesetzt und die Namen der Elemente müssen die Groß- und Kleinschreibung beachten.
<Button Width="100">Obwohl XAML die XML-Syntax verwendet, ist zumindest der typische XML-Prolog
<?xml version="1.0" ?>nicht notwendig. Achten Sie weiterhin darauf, dass XAML case-sensitiv ist, d.h. die Groß- und Kleinschreibung wird beachtet.
Windows- oder ein Page-Element zum Einsatz, wenn es sich um ein Fenster oder eine Seite in einer
Navigationsanwendung handelt oder ein ResourceDictionary und Application-Element, wenn Sie eine Ressourcen- oder die Anwendungsdatei mit
XAML beschreiben. Theoretisch könnte als Wurzelelement sogar Button oder TextBox verwendet werden, allerdings macht dies für eine Anwendungsentwicklung
sicherlich nicht viel Sinn.
Einige Elemente verfügen über eine Eigenschaft Content, die genau ein beliebiges anderes Element aufnehmen kann. Die Klasse Window und
die Klasse Button besitzen z.B. eine solche Eigenschaft. Damit in das Wurzelelement mehrere Elemente eingefügt werden können, fügt man zuerst ein
Containerelement wie ein Grid oder ein StackPanel ein. Darin können weitere Elemente eingebettet werden, da diese Komponenten über eine Eigenschaft Children
verfügen, die eine Collection von UIElement-Objekten verwaltet (d.h. sie können mehrere Komponenten aufnehmen). Andere Containerelemente sind z.B.
Listen und Menüs. Allerdings besitzen diese keine Eigenschaft Children sondern sie verwenden eine Eigenschaft Items, die vom Typ ItemCollection ist und mehrere Menü- und Listeneinträge verwalten.
Die Bestandteile einer XAML-Datei sind Namespaces, Elemente und Attribute. Diese haben eine spezielle Bedeutung.
<Button> an, wird an dieser Stelle eine Button-Instanz erzeugt.
public sein und Werttypen darstellen. Für alle anderen Typen muss ein so genannter Typkonvertierer bereitgestellt werden. Die WPF liefert bereits eine Anzahl solcher Typkonvertierer mit.
HINWEIS: Da die Angabe eines Elements in XAML zum Aufruf des Standardkonstruktors einer Klasse führt erkennen Sie an dieser Stelle schon einige Einschränkungen bei der Verwendung von XAML. Sie können z.B. keinen beliebigen Konstruktor aufrufen. Es ist ebenfalls nicht möglich neue, eigenständige Klassen zu definieren, da sich diese immer in der partiellen Klasse befinden würden, die durch die XAML-Datei definiert wird.
FrameworkElement erben alle Komponente eine Eigenschaft Name, die für das Element einen eindeutigen Namen festlegt. Dieser Name
kann im Code wie der Name einer Variablen verwendet werden. Besitzt ein Element in XAML keine Eigenschaft Name, können Sie dennoch einen Namen vergeben.
Dazu verwenden Sie das Attribut x:Name. Sie erhalten dann ebenfalls einen allgemein gültigen Bezeichner für das Element.
Weiterhin sind einige Elemente nur innerhalb anderer Elemente verfügbar. Die IntelliSense-Hilfe zeigt dies in den meisten Fällen auch korrekt an. In einigen Ausnahmen dürfen Sie aber auch Elemente/Attribute verwenden, die nicht angeboten und im Visual Studio-Editor unterstrichen dargestellt werden.
Im folgenden XAML-Code wird beispielsweise ein Button durch ein Button-Element definiert. Er entspricht dabei der Button-Klasse aus
dem .NET Framework (und dabei nicht aus dem Namespace System.Windows.Forms sondern System.Windows.Controls). Des Weiteren wird im Element
Button ein Attribut Width verwendet, um die Breite des Buttons festzulegen. Die Werte für ein Attribut werden immer in Anführungszeichen
eingeschlossen. Eine notwendige Typumwandlung der Zeichenkette "100" in den Double-Wert 100.0 (es ist tatsächlich eine Gleitkommazahl) findet über einen Typkonvertierer
automatisch statt.
<Button Width="100">Hallo</Button>
Panel abgeleitet und besitzen eine Eigenschaft Children, über die sie ihre Kindelemente verwalten.
<Button>
<StackPanel>
<TextBlock>Öffnen</TextBlock>
<Image Source="Open.bmp" />
</StackPanel>
</Button>
Background wird beispielsweise die Hintergrundfarbe des Buttons definiert.
<Button Background="LightBlue" Content="Hallo" />Diese Schreibweise ist sehr kompakt, birgt aber einen Nachteil. Da einem Attribut nur Strings zugewiesen werden können, sind die möglichen Zuweisungen begrenzt. Es muss aber eine Möglichkeit geben, einem Hintergrund auch als komplexen Farbverlauf zu definieren. In diesem Fall kommt die folgende Schreibweise zum Einsatz.
Button.Background wird dadurch ein LinearGradientBrush-Objekt erzeugt und der Eigenschaft Background des Buttons zugewiesen.
<Button Content="Hallo">
<Button.Background>
<LinearGradientBrush>
<GradientStop Color="Yellow" Offset="0.0" />
<GradientStop Color="Blue" Offset="1.0" />
</LinearGradientBrush>
</Button.Background>
</Button>
<Button>.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"Es wäre allerdings auch möglich hier einen Präfix zu vergeben. Allerdings ist dies nicht üblich und würde zu einer umständlicheren Schreibweise führen.
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" <wpf:Button>...</wpf:Button>Der zweite Namespace definiert für den XAML-Namespace den Präfix
x. Natürlich könnte man auch hier einen anderen Namespace-Alias dafür verwenden, was
aber auch nicht üblich ist. Das Mapping macht den Namespace System.Windows.Markup in XAML verfügbar.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"ACHTUNG: In den Beispielen in diesem Tutorial wird auf die Angabe dieser Namespaces im Wurzelelement in der Regel verzichtet. Stattdessen werden im Wurzelelement drei Punkte als Platzhalter für diese Angaben, und meist noch den Titel des Fensters angegeben.
xmlns. Durch einen Doppelpunkt getrennt geben Sie nun einen beliebigen Alias an, z.B. clr.
xmlns:clrJetzt weisen Sie über zwei Name-Wert-Paare dem XML-Namespace einen CLR-Namespace und die ihn enthaltende Assembly zu. Der Name des Namespaces wird über
clr-namespace eingeleitet. Um beispielsweise den Namespace System einzubinden, schreiben Sie:
xmlns:clr="clr-namespace:System"Danach wird, getrennt durch ein Semikolon, die Assembly angegeben, in welcher der Namespace definiert wird. Befindet sich der Namespace in der gleichen Assembly wie das Projekt, kann die Angabe der Assembly weggelassen werden. Die Assembly wird ohne die Endung .dll und ohne Pfadangaben angegeben. Optional können Versionsinformationen etc. angegeben werden.
xmlns:clr="clr-namespace:System;assembly=mscorlib"Die partielle Klasse, welche hinter der XAML-Datei liegt, muss nicht auf den Namespace des Projekts gemappt werden. Verwenden Sie aber andere Klassen aus dem aktuellen Projekt, muss auch der Projekt-Namespace separat eingebunden werden.
In der Code-Behind-Datei wird eine Klasse ZeichenFueller deklariert, welche eine öffentliche Eigenschaft Anzahl besitzt. Darüber wird angegeben, wie oft der Buchstabe (A) (hier einfach nur beispielhaft) über die überschriebene Methode ToString() zurückgegeben werden soll. Diese Klasse soll später in XAML verwendet werden,
namespace XAMLNamespacesProj
{
public partial class Window1: System.Windows.Window
{
public Window1()
{
InitializeComponent();
}
}
public class ZeichenFueller
{
private int anzahl;
public int Anzahl
{
get { return anzahl; }
set { anzahl = value; }
}
public override string ToString()
{
return new String('A', anzahl);
}
}
}
Listing 3.1: Beispiele\Kap03\XAMLNamespacesProj\Window1.xaml.cs
In die XAML-Datei des Fensters werden nun zwei zusätzliche Namespaces eingebunden. Statt den Wert der Eigenschaft Width über eine Zeichenkette anzugeben, soll er über
den Datentyp Double aus dem Namespace System zugewiesen werden. Beachten Sie, dass auch in diesem Fall die Typkonvertierer zum Einsatz
kommen, denn auch die Angabe 200 im clr:Double-Element ist letztendlich ein String. Der Namespace System befindet sich in der Assembly mscorlib, so
dass zum Einbinden die Angabe clr-namespace:System;assembly=mscorlib verwendet wird. Als Präfix für den Namespace wird clr verwendet.
Um die selbst definierte Klasse zu verwenden, muss nur der Namespace der Klasse eingebunden werden, da er sich in der gleichen Assembly befindet. Als Präfix wird diesmal das Kürzel prj verwendet.
Der öffentlichen Eigenschaft Anzahl wird der Wert 10 übergeben, so dass 10x der Buchstabe A über die Methode ToString() zurückgegeben wird.
Zur Auswertung des erstellten ZeichenFueller-Objekts (es wird ein String an dieser Stelle erwartet) wird automatisch die Methode ToString() des Objekts aufgerufen, die ja in der Klasse überschrieben wurde.
<Window x:Class="XAMLNamespacesProj.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:clr="clr-namespace:System;assembly=mscorlib"
xmlns:prj="clr-namespace:XAMLNamespacesProj"
Title="XAMLNamespacesProj" Height="300" Width="300">
<StackPanel>
<Button>
<Button.Width>
<clr:Double>
200
</clr:Double>
</Button.Width>
<prj:ZeichenFueller Anzahl="10" />
</Button>
<TextBox Width="200">
<clr:String>Hallo</clr:String>
</TextBox>
</StackPanel>
</Window>
Listing 3.2: Beispiele\Kap03\XAMLNamespacesProj\Window1.xaml
String,
konvertiert ein Typkonvertierer die angegebene Zeichenkette in den benötigten Typ. Im folgenden Element wird der Hintergrund eines Buttons gefärbt. Da die Eigenschaft Background vom
Typ Brush ist, wird der Typkonvertierer aktiv, der eine Zeichenkette in ein Brush-Objekt überführt.
<Button Background="LightYellow" ...>Jetzt kann es aber erforderlich sein, dass der Wert eines Attributs nicht als String interpretiert werden soll. So kann es z.B. wünschenswert sein, nicht immer ein neues
Brush-Objekt zu erzeugen sondern stattdessen ein bereits vorhandenes zu verwenden. Über Data Binding können Sie den Wert eines Attributs auf den Wert eines Attributs (d.h. einer Eigenschaft) eines anderen Elements setzen. Um dies dem XAML-Parser mitzuteilen, wird eine andere Schreibweise benötigt (der XAML-Parser ist für das Interpretieren des XAML-Codes verantwortlich).
Diese spezielle Schreibweise wird Markup-Erweiterung genannt. Dazu wird die Zeichenkette, deren Aufbau abhängig von der Erweiterung ist, in geschweifte Klammern gesetzt. Bei den beiden folgenden Buttons wird die Hintergrundfarbe nur beim ersten Button auf einen konkreten Wert gesetzt. Im zweiten Button wird über ein Binding die Farbe des ersten Buttons verwendet. Der Elementname entspricht dabei dem Namen des ersten Buttons und der Wert von Path der Eigenschaft, an die angebunden werden soll.
<Button Background="LightYellow" Name="BtnVorgabe" />
<Button Background="{Binding ElementName=BtnVorgabe, Path=Background}" />
In der Attributschreibweise werden die Markup-Erweiterungen also in geschweifte Klammern eingeschlossen. Die konkrete Erweiterung wird dann durch das erste Wort nach der öffnenden Klammer identifiziert, z.B. Binding wie im folgenden Beispiel.
... Background="{Binding ...}"
In der Eigenschaftselementschreibweise wird die Erweiterung wie ein XAML-Element formuliert.
<Button.Background> <Binding ElementName="BtnVorgabe" Path="Background" /> </Button.Background>Eine Verschachtelung von solchen Erweiterungen ist ebenfalls möglich. Dabei wird die innerste Ebene zuerst ausgewertet. Eine öffnende geschweifte Klammer leitet als Wert eines Attributs immer eine Markup-Erweiterung ein. Möchten Sie die Klammer als normalen Text interpretieren, setzen Sie das Klammerpaar {} davor.
<TextBox Text="{}{hier steht Text}" />
Sämtliche Markup-Erweiterungen sind Klassen, die von der Klasse MarkupExtension aus dem Namespace System.Windows.Markup abgeleitet sind. Die Erweiterung Binding wird z.B. durch die Klasse System.Windows.Data.Binding implementiert.
x:Type.
| Erweiterung | Beschreibung |
| x:Array | Hiermit können Sie Arrays in XAML definieren. Wenn Sie CLR-Typen wie String oder Double als Array-Elemente verwenden wollen, müssen Sie noch den Namensraum System einbinden und einen Präfix festlegen (z.B. clr). Über das Attribut Type wird der Typ der Array-Elemente angegeben.
<x:Array Type="clr:String" <clr:String>Eintrag 1</clr:String> <clr:String>Eintrag 2</clr:String> <clr:String>Eintrag 3</clr:String> </x:Array> |
| x:Null | Um den Wert null einem Element zuzuweisen, verwenden Sie diese Angabe.
<Button Background="{x:Null}" /> |
| x:Static | Es wird eine statische Variable oder Eigenschaft eines Objekts, eine Konstante oder ein Aufzählungswert referenziert.
<Button Background="{x:Static Brushes.Blue}" /> |
| x:Type | Um einen Typ anzugeben, z.B. in Stildefinitionen, nutzen Sie x:Type.
<Style TargetType="{x:Type Button}"> |
Tabelle 3.1: Markup-Erweiterungen von XAML
| Erweiterung | Beschreibung |
| Binding | Definiert eine Datenbindung für den Wert eines Attributs (wird im Kapitel Data Binding besprochen). |
| DynamicResource | Der Wert des Attributs stammt aus einer Ressource, wobei sich der Wert der Ressource ändern kann (wird im Kapitel Ressourcen besprochen). |
| StaticResource | Der Wert des Attributs stammt aus einer Ressource, wobei der Wert der Ressource nur einmal zu Beginn ausgewertet wird (wird im Kapitel Ressourcen besprochen). |
| TemplateBinding | Weist einer Eigenschaft in einem ControlTemplate einen Wert zu, der an anderer Stelle definiert wird (wird im Kapitel Styles besprochen) |
Tabelle 3.2: Auswahl der wichtigsten WPF-Markup-Erweiterungen
| Attribut/Direktiven | Beschreibung |
| x:Class | Das Attribut stellt eine Beziehung zwischen dem Wurzelelement einer XAML-Datei und einer partiellen Klasse her, z.B.
x:Class="Namespace.Klasse"Die Angabe von x:Class ist immer dann notwendig, wenn in der partiellen Klasse Code hinterlegt wird (auch wenn dieser in XAML eingebettet wird) und wenn Sie XAML-Elemente mit einem Namen versehen haben.
|
| x:Code | Definiert einen Code-Bereich innerhalb einer XAML-Datei. |
| x:FieldModifier | Um den automatisch generierten Zugriffsmodifizierer für ein Objekt zu ändern, geben Sie den neuen Modifizierer an, z.B. public. Standardmäßig ist er internal. Das Element muss dazu einen Namen besitzen.
<Button Name="Btn1" x:FieldModifier="public" /> |
| x:Key | Vergibt einen Schlüsselnamen für ein Element einer Ressource. |
| x:Name | Hiermit können Sie Elementen einen Namen vergeben, die nicht über eine Eigenschaft Name verfügen und auf die Sie später verweisen wollen.
<GradientBrush x:Name="Grad1" ...> |
| x:XData | Hiermit können Sie eine XML-Dateninsel erstellen, die z.B. Daten für das Data Binding bereitstellt (siehe Kapitel Data Binding). |
Tabelle 3.3: Auswahl von XAML-Attributen und -Direktiven
HINWEIS: Um in einem Elementinhalt die Whitespace-Zeichen zu erhalten, wird das Attribut xml:space mit dem Wert preserve angegeben. Der Standardwert default entfernt die Leerzeichen. Dieses Attribut wird allerdings über XML und nicht durch XAML definiert.
<clr:String xml:space="preserve"> Hallo </clr:String>
Sie finden XAMLPad nach der Installation des Windows SDKs im Programmordner START - PROGRAMME - MICROSOFT WINDOWS SDK - TOOLS. Im unteren Bereich geben Sie den XAML-Code in einem eher rudimentären Editor ein, der leider keine Syntaxhilfe unterstützt. Nach dem Start wird immer der zuletzt beim Schließen eingegebene XAML-Code angezeigt. Dazu speichert XAMLPad den zuletzt gültigen Code im Verzeichnis [LW]:\Programme\Microsoft SDKs\Windows\v6.0\Bin in der Datei Xaml-Pad_Saved.xaml.
Haben Sie gültigen Code eingegeben wird dieser sofort interpretiert und das Ergebnis im oberen Bereich angezeigt. Als Wurzelelement wird normalerweise ein Page-Element verwendet, damit es in XAML-Pad eingebettet werden kann. Sie können aber auch einen Layoutcontainer wie Grid oder StackPanel als Wurzelelement verwenden. Verwenden Sie Window als Wurzelelement, wird ein neues Fenster mit dem Inhalt geöffnet.
Fehler werden ganz unten in einem Statusbereich angezeigt. Sie können diese Vorgehensweise allerdings durch das Deaktivieren der Schaltfläche AUTO PARSE links oben aufheben. Über die Schaltfläche SHOW VISUAL TREE (in der Abbildung 3.1 nicht sichtbar) kann die komplette interne Verwaltung einer XAML-Seite betrachtet werden (1) und (2).
Abbildung 3.1: XamlPad mit Editor, Ausgabebereich und geöffnetem Visual Tree
<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Background="AliceBlue" Content="Loose XAML" />
Üblicherweise wird das Element Page als Wurzelelement verwendet (da die XAML-Ausgabe dadurch in das Hostfenster eingebettet werden kann), Window ist nicht möglich. Außerdem darf sich kein Programmcode in der XAML-Datei befinden, da dieser durch das integrierte Plug-In nicht interpretiert und schon gar nicht ausgeführt werden kann.
Wozu ist das nützlich? Wenn Sie einfache Beispiele für XAML-Code erstellen und anzeigen möchten und diese vielleicht noch über das Internet bereitstellen, ist dies sicher eine sehr geeignete Lösung (Sorry Firefox-User). Statt einfachen Beispielen lassen sich aber auch komplexe 3D-Grafiken mit Animationen in einer XAML-Datei verpacken. So könnten Sie eine Kursentwicklung oder eine Wetterkarte mit XAML erstellen und im Internet bereitstellen.
Innerhalb einer HTML-Seite können Sie über IFrames auch mehrere XAML-Dateien gleichzeitig laden und anzeigen. Dazu wird jede XAML-Datei in ein iframe-Element eingeschlossen. Diese Vorgehensweise macht natürlich auch nur im Internet Explorer Sinn, da momentan nur dieser Browser den XAML-Code über das Plug-In ausführen kann.
<html<>
<head>
<title>Loose XAML - Beispiele</title>
</head>
<body>
<table>
<tr>
<td><h3>Überschrift 1</h3>
<iframe src="Datei1.xaml"></iframe>
</td>
<td><h3>Überschrift 2</h3>
<iframe src="Datei2.xaml"></iframe>
</td>
</tr>
</table>
</body>
</html>
Listing 3.3: Beispiel für das Einbinden von mehreren XAML-Dateien in eine HTML-Seite
Standardmäßig wird eine XAML-Datei im Visual Studio mit dem WPF Designer geöffnet. Wenn Sie einen anderen Standardeditor definieren wollen, öffnen Sie den Kontextmenüpunkt OPEN WITH einer XAML-Datei und wählen einen anderen Standardeditor aus.
Abbildung 3.2: Einen anderen Standard-XAML-Editor auswählen
Durch den folgenden Code wird ein Button in einem Tabellengitter in der 1. Spalte und 2. Zeile positioniert. Die Eigenschaften Column und Row werden aber nicht durch die Button-Klasse sondern durch die Klasse Grid definiert. Aus diesem Grund muss vor den Attributen auch der Klassenname angegeben werden, z.B. Grid.Column.
<Grid> <Button Grid.Column="0" Grid.Row="1" Name="BtnImGrid">bzw. im Code
Grid.SetColumn(BtnImGrid, 0); Grid.SetRow(BtnImGrid, 1);HINWEIS: Wie Dependency und Attached Properties definiert werden, wird im Kapitel zum Erstellen eigener Komponenten erläutert.