Startseite  Inhaltsverzeichnis  <<  >>  

Kapitel 3 - Einführung in XAML

3.1 Einführung

Mit der WPF wurde über XAML (Extensible Application Markup Language) eine neue Sprache eingeführt, die der deskriptiven Beschreibung einer Benutzeroberfläche dient. Ziel ist es dabei nicht, dass nun sämtliche Benutzeroberflächen ausschließlich mit XAML erstellt werden. Stattdessen bietet XAML die Möglichkeit, zur Entwicklung einer Anwendung einen (Grafik)Designer für die Oberfläche hinzuzuziehen, der gegebenenfalls geschickter in der Verwendung von Grafiktools ist als ein Programmierer.

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.
XAML oder Code
Bei der Entwicklung von WPF-Anwendungen stellt sich von vornherein die Frage, ob Sie besser eine Oberfläche in XAML oder im Code erzeugen. Das hängt aber hauptsächlich von den Erfordernissen an die Anwendung und deren Entwicklung ab. Es folgen einige Punkte, die als Kriterium für Ihre Entscheidung dienen können, aber keinen Anspruch auf Vollständigkeit erheben:
3.1.2 XAML-Mapping
Als Wurzelelement kommt in der Regel ein 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.

ACHTUNG: Im gesamten Buch werden die Begriffe "Element" und "Klasse" sowie "Eigenschaft" und "Attribut" synonym verwendet, wobei sich die Bezeichnungen "Element" und "Attribut" in der Regel auf den Einsatz in einer XAML-Datei beziehen.

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.

Namen
Damit auf ein Element in einer XAML-Datei, also ein Objekt vom Typ einer Klasse, zugegriffen werden kann, muss ihm ein Bezeichner zugewiesen werden. Von der Klasse 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>
3.1.3 Schreibweise von Elementen und Attributen
Die Elemente innerhalb einer XAML-Datei definieren eine Hierarchie, welche als Ergebnis die Benutzeroberfläche festlegen. Dazu müssen z.B. Elemente in anderen Elementen verschachtelt werden. Dies geht in der WPF sogar soweit, dass Sie in einem Button wiederum weitere Elemente verschachteln können, so dass sich ein Button z.B. aus einem Textfeld und einem Bild zusammensetzt. Bei der Verschachtelung ist zu beachten, dass sich mehrere Elemente in der Regel immer in einem Containerelement befinden müssen. Layoutcontainer sind von der Klasse Panel abgeleitet und besitzen eine Eigenschaft Children, über die sie ihre Kindelemente verwalten.
<Button>
  <StackPanel>
    <TextBlock>Öffnen</TextBlock>
    <Image Source="Open.bmp" />
  </StackPanel>
</Button>
Attributschreibweise von Eigenschaften
Um die Eigenschaften eines Elements zu setzen, werden die Eigenschaften als Attribute angegeben (Attribute Syntax). Über das Attribut 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.
Eigenschaftselementschreibweise
Die so genannte Property Element Syntax verwendet zur Definition eines Eigenschaftswertes eine Schreibweise, die aus dem Namen des Elements und der durch einen Punkt getrennten Eigenschaft besteht. Das folgende Beispiel setzt beispielsweise ebenfalls den Hintergrund eines Buttons. Unterhalb des Elements 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>
3.1.4 Namespaces
Damit Sie in einer XAML-Datei auf die Typen der WPF und auf spezielle XAML-Schlüsselwörter zugreifen können, werden diese im Wurzelelement jeder XAML-Datei über zwei XML-Namespaces verfügbar gemacht. Der folgende Namespace wird als Default-Namespace definiert und stellt die meisten WPF-Typen zur Verfügung. Da er keinen Präfix definiert, können Sie auf einen Button direkt zugreifen, z.B. <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.

Eigene Namespace-Mappings
Neben den vordefinierten Namespaces und Typen können Sie auch andere CLR-Namespaces in XAML verfügbar machen. Dies können eigene oder die CLR-Namespaces sein. Um einen weiteren Namespace einzubinden beginnen Sie zuerst die XML-Namespace-Deklaration über xmlns. Durch einen Doppelpunkt getrennt geben Sie nun einen beliebigen Alias an, z.B. clr.
xmlns:clr
Jetzt 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.

BEISPIEL: XAMLNamespacesProj.zip - Download

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

3.2 Markup- und Spracherweiterungen
Wenn Sie einem Attribut eines Elements in XAML einen Wert zuweisen wird versucht, der entsprechenden Eigenschaft des erzeugten Objekts diesen Wert zuzuweisen. Ist der Typ der Eigenschaft nicht 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.
3.2.1 Markup-Erweiterungen von XAML
Es gibt zwei Typen von Markup-Erweiterungen, XAML- und WPF-spezifische. XAML selbst definiert einige Erweiterungen, die völlig unabhängig von der WPF sind. Sie werden über den Alias des XAML-Namespaces eingeleitet, z.B. x:Type.
ErweiterungBeschreibung
x:ArrayHiermit 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:NullUm den Wert null einem Element zuzuweisen, verwenden Sie diese Angabe.
<Button Background="{x:Null}" />
x:StaticEs wird eine statische Variable oder Eigenschaft eines Objekts, eine Konstante oder ein Aufzählungswert referenziert.
<Button Background="{x:Static Brushes.Blue}" />
x:TypeUm einen Typ anzugeben, z.B. in Stildefinitionen, nutzen Sie x:Type.
<Style TargetType="{x:Type Button}">

Tabelle 3.1: Markup-Erweiterungen von XAML

3.2.2 Markup-Erweiterungen der WPF
Auch die WPF definiert einige Markup-Erweiterungen. Diese werden später noch in den entsprechenden Kapiteln umfangreicher vorgestellt.

ErweiterungBeschreibung
BindingDefiniert eine Datenbindung für den Wert eines Attributs (wird im Kapitel Data Binding besprochen).
DynamicResourceDer Wert des Attributs stammt aus einer Ressource, wobei sich der Wert der Ressource ändern kann (wird im Kapitel Ressourcen besprochen).
StaticResourceDer Wert des Attributs stammt aus einer Ressource, wobei der Wert der Ressource nur einmal zu Beginn ausgewertet wird (wird im Kapitel Ressourcen besprochen).
TemplateBindingWeist 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

3.2.3 XAML-Spracherweiterungen
Neben den Markup-Erweiterungen definiert XAML noch einige Attribute und Direktiven, welche keine Entsprechungen in einer Klasse besitzen und dem XAML-Parser und -Compiler Informationen für eine besondere Verarbeitung liefern.

Attribut/DirektivenBeschreibung
x:ClassDas 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:CodeDefiniert einen Code-Bereich innerhalb einer XAML-Datei.
x:FieldModifierUm 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:KeyVergibt einen Schlüsselnamen für ein Element einer Ressource.
x:NameHiermit 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:XDataHiermit 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>

nach oben

3.3 XAMLPad

Um schnell das Ergebnis eines XAML-Codes zu betrachten, gibt es verschiedene Möglichkeiten. Sie können im Visual Studio den WPF Designer verwenden, allerdings ist dessen Geschwindigkeit, gerade zwischen der Umschaltung zwischen XAML und Designer, nicht sehr berauschend und auch seine Designmöglichkeiten sind recht beschränkt. Natürlich können Sie eine Anwendung übersetzen und ausführen, um das Ergebnis zu sehen, aber auch dass ist ziemlich umständlich. Bliebe noch die Lösung ein externes Tool wie Expression Blend zu nutzen, aber erstens wird es in Zukunft nicht kostenfrei sein und zweitens müssen Sie immer zwischen der grafischen Darstellung und Anzeige des XAML-Codes umschalten. Aushilfe bietet hier das Tool XAMLPad.

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).

XamlPad mit Editor, Ausgabebereich und geöffnetem Visual Tree

Abbildung 3.1: XamlPad mit Editor, Ausgabebereich und geöffnetem Visual Tree

nach oben

3.4 Loose XAML

Normalerweise wird eine XAML-Anwendung in eine Assembly kompiliert und wie eine "normale" .NET-Anwendung ausgeführt. Außerdem ist es einer Anwendung möglich, auch zur Laufzeit XAML-Code nachzuladen und zu interpretieren. Eine weitere Variante steht mit Loose XAML zur Verfügung. Damit wird der Internet Explorer durch ein Plug-In befähigt, eine einzelne XAML-Datei zu laden und anzuzeigen. Voraussetzung ist, dass im Wurzelelement der WPF- und bei Bedarf auch der XAML-Namespace angegeben werden. So erhalten Sie mit dem folgenden Code die wahrscheinlich kleinste "XAML-Anwendung" (auf jeden Fall, wenn man auch noch das Setzen der Hintergrundfarbe und der Beschriftung weglässt)
<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

nach oben

3.5 Verwendung des WPF Designers

Der integrierte WPF Designer im Visual Studio dient momentan der rudimentären Bearbeitung einer XAML-Datei auf grafischem Wege. Er besitzt allerdings noch keine Funktionalität, die sich hier zu beschreiben lohnt.

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.

Einen anderen Standard-XAML-Editor auswählen

Abbildung 3.2: Einen anderen Standard-XAML-Editor auswählen

nach oben

3.6 Dependency und Attached Properties

Das Eigenschaftssystem von .NET 3.0 wurde mit den neu eingeführten Dependency Properties (abhängigen Eigenschaften) erweitert. Sie basieren zwar auf den CLR-Eigenschaften und deren Schreibweise, werden aber anders deklariert. Die Dependency Properties stellen weitere Funktionalitäten zur Verfügung. Der Grund für die Einführung der Dependency Properties und damit deren Einsatzgebiete sind vielfältig. Viele Aufgaben in der WPF hängen davon ab zu überwachen, ob sich im System oder innerhalb der Anwendung Eigenschaftswerte geändert haben. Die Benachrichtigung über Änderungen dieser Werte und die automatische Aktualisierung der abhängigen Objekte gehören z.B. dazu. Bestimmte Features stehen nur für diese Eigenschaften zur Verfügung wie z.B.: Der Wert einer Eigenschaft ist somit auch vom verwendeten Stil, eingesetztem Data Binding oder Animationen abhängig. Der konkrete Wert muss dann von der WPF berechnet werden. Aufgrund des höheren Aufwands sind nicht alle Eigenschaften Dependency Properties. In eigenen Komponenten sollten Sie ebenfalls nur die Eigenschaften als Dependency Properties definieren, welche die speziellen Features der WPF nutzen.
Attached Properties
Attached Properties (angehangene Eigenschaften) sind eine spezielle Variante der Dependency Properties. Das besondere daran ist, dass die Eigenschaften zu einem Eltern-Element (meist einem Layoutcontainer) gehören aber die Werte in den Kindelementen gesetzt werden. Der Vorteil dabei ist, dass die Kindelemente nicht mit Informationen überlastet werden, keine überflüssigen Eigenschaften enthalten (für jeden Layoutcontainer müssten z.B. spezielle Eigenschaften bereitgestellt werden) und das ganze System dadurch auch erweiterbar ist.

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.

nach oben