18.2 XAML (Extended Application Markup Language)![Zur nächsten Überschrift](common/down.gif)
XAML ist eine deklarative Programmiersprache, deren Wurzeln auf XML zurückzuführen sind. XAML unterliegt damit auch denselben strengen Regeln wie XML:
- Elemente werden durch Tags beschrieben.
- Jedes Starttag bedarf zwingend eines Endtags.
- Die Groß-/Kleinschreibung muss berücksichtigt werden.
Auf eine detaillierte Einführung in XML soll an dieser Stelle verzichtet werden. Sollten Sie dennoch noch nicht mit XML Bekanntschaft gemacht haben, sollten Sie in Abschnitt 14.2 über wohlgeformten XML-Code nachlesen.
XAML ist eine Erweiterung der XML-Spezifikation. Sie werden viele bekannte Regeln der XML wiederfinden, aber auch mit Neuerungen konfrontiert, die mit XAML eingeführt worden sind, um das Gesamtkonzept besser unterstützen zu können.
18.2.1 Die Struktur einer XAML-Datei![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Sehen wir uns den XAML-Code an, den Visual Studio 2012 beim Erstellen eines neuen Fensters erzeugt.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
Listing 18.7 Struktur der XAML-Datei eines Fensters
Jede XML-Datei hat ein Wurzelelement, das alle anderen Elemente einschließt. Das gilt natürlich für die XAML-Datei einer WPF-Anwendung. Hierbei handelt es sich um das Element Window. Es weist von Anfang an einige Attribute auf, um eine gewisse Grundcharakteristik sicherzustellen:
- Das erste, x:Class, gibt die Code-Behind-Datei an, die den C#-Code des aktuellen XAML-Dokuments enthält.
- Mit xmlns werden zwei XML-Namespaces bekannt gegeben, damit die Elemente im XAML-Code einwandfrei identifiziert werden können.
- Mit dem Attribut Title wird anschließend die Zeichenfolge beschrieben, die in der Titelleiste des Fensters angezeigt wird, und Height und Width legen die Gesamthöhe bzw. -breite des Fensters fest.
Im Wurzelelement Window sind alle Komponenten enthalten, aus denen sich das Fenster zusammensetzt: Schaltflächen, Textboxen, Listenfelder usw. Da Window jedoch grundsätzlich nur ein direkt untergeordnetes Element haben kann, handelt es sich dabei in der Regel um ein Containersteuerelement, das seinerseits selbst beliebig viele Steuerelemente aufnehmen kann. Per Vorgabe wird immer ein Grid-Element vorgeschlagen. Es gibt aber noch zahlreiche weitere, die alle in irgendeiner Weise auf eine bestimmte Präsentation oder Ausrichtung der in ihnen enthaltenen Steuerelemente spezialisiert sind. In Kapitel 19 lernen Sie alle Containersteuerelemente kennen.
Neben dem Grid ist beispielsweise das StackPanel ein häufig verwendetes Containersteuerelement. Containersteuerelemente zeichnen sich dadurch aus, dass sie über die Eigenschaft Children verfügen, die eine Auflistung von UIElement-Objekten verwaltet. Im folgenden Codefragment sehen Sie das Stammelement Window nebst dem untergeordneten Container vom Typ Grid. Dieser enthält ein Button-Element.
<Window x:Class="WpfApplication1.MainWindow"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
Title="MainWindow" Height="163" Width="300">
<Grid>
<Button FontSize="18" Background="LightGray"
Name="btnButton1" Content="Der erste Button"></Button>
</Grid>
</Window>
Listing 18.8 Grid mit einem eingebetteten Button
Beachten Sie, dass die Schaltfläche keine Angaben zu ihren Abmessungen enthält. Es liegt in der Natur des übergeordneten Grid-Containers, dass der dem Grid als einziges Element untergeordnete Button dann den gesamten Containerbereich für sich beansprucht (siehe ). Wie Sie in dem Bereich des Grids auch mehrere Steuerelemente unterbringen können, werden Sie später noch sehen.
Abbildung 18.6 Die grafische Benutzeroberfläche des Listings 18.8
Das Element Button entspricht der gleichnamigen Klassendefinition im Namespace System.Windows.Control. Die Angabe <Button> im XAML-Code bewirkt die Instanziierung des entsprechenden Elements über den parameterlosen Konstruktor. Die Attribute FontSize, Background, Content und Name sind Eigenschaften der Klasse Button. Alternativ können Sie die Schaltfläche auch im Programmcode der Code-Behind-Datei erzeugen, beispielsweise nach dem Aufruf der Methode InitializeComponent im Konstruktor:
public MainWindow()
{
InitializeComponent();
Button btnButton1 = new Button();
btnButton1.FontSize = 18;
btnButton1.Background = new SolidColorBrush(Colors.LightGray);
btnButton1.Content = "Der erste Button";
this.AddChild(btnButton1);
}
Listing 18.9 Button aus Abbildung 18.6 mit Programmcode erzeugen
Mit der letzten Anweisung im Listing wird die Schaltfläche ihrem übergeordneten Container zugeordnet.
18.2.2 Eigenschaften eines XAML-Elements in Attributschreibweise festlegen![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Jedes WPF-Element hat zahlreiche Eigenschaften. Eine Möglichkeit ist es, diese im XAML-Code durch Attribute anzugeben. Beabsichtigen Sie, beispielsweise die Beschriftung, die Breite und die Höhe einer Schaltfläche festzulegen, müssen Sie die Eigenschaften Content, Height und Width als Attribute angeben:
<Button Height="50" Width="100" Content="Mein erster Button"></Button>
Allen Attributen werden Werte grundsätzlich immer als String übergeben. Die Zeichenfolge wird von einem Typkonverter anschließend in den von der Eigenschaft beschriebenen Datentyp umgewandelt. Bei Height und Width ist das die Umwandlung in den Datentyp Double.
Nicht immer muss der Zieldatentyp so einfach sein. Legen Sie zum Beispiel die Hintergrundfarbe Background fest, verbirgt sich dahinter die Konvertierung in den schon verhältnismäßig komplexen Typ Brush.
18.2.3 Eigenschaften im Eigenschaftsfenster festlegen![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Sie müssen natürlich die Eigenschaften eines Elements nicht im XAML-Code direkt angeben – auch wenn es dank der IntelliSense-Unterstützung nicht weiter schwierig ist. Sie können stattdessen die Eigenschaften auch im Eigenschaftsfenster von Visual Studio für die aktuell im Designer ausgewählte Komponente festlegen (siehe Abbildung 18.7). Das Eigenschaftsfenster ist sehr aufwendig gestaltet und keineswegs »langweilig«, sondern weist je nach Eigenschaft sehr viele trickreiche, aber auch intuitive Facetten auf. Diese im Einzelnen zu erörtern, würde den Rahmen sprengen. Ich kann Sie nur dazu auffordern, sich selbst ein wenig in diesem Fenster umzusehen und zu experimentieren.
Abbildung 18.7 Das Eigenschaftsfenster einer WPF-Komponente
18.2.4 Die Eigenschaft-Element-Syntax![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Die Attributschreibweise ist zwar kompakt, birgt aber den Nachteil, dass einer Eigenschaft nur eine Zeichenfolge zugewiesen werden kann. Diese wird meistens auf einen elementaren Datentyp zurückgeführt. Eigenschaften können aber auch komplexer Natur sein. Nehmen wir exemplarisch die Eigenschaft Background, die die Hintergrundfarbe beschreibt. Soll diese einheitlich Blau sein, ist die Attributschreibweise vollkommen ausreichend:
<Button Background="Blue"></Button>
Was ist aber, wenn die Hintergrundfarbe nicht durch eine konkrete Farbe, sondern einen Farbverlauf beschrieben werden soll? Dafür sind entsprechende Objekte notwendig, die von WPF bereitgestellt werden. In solchen Fällen ist die Attributschreibweise völlig ungeeignet, und es kommt die sogenannte Eigenschaft-Element-Syntax ins Spiel. Bei dieser Schreibweise wird zuerst der Typ des Elements genannt und dahinter, durch einen Punkt getrennt, die Eigenschaft.
<Button Height="100" Width="200">
<Button.Background>
<LinearGradientBrush>
<GradientStop Color="RoyalBlue" Offset="0.0" />
<GradientStop Color="White" Offset="1.0" />
</LinearGradientBrush>
</Button.Background>
Mein erster Button
</Button>
Listing 18.10 Button mit Farbverlauf
Das Codefragment beschreibt mit einem Objekt vom Typ LinearGradientBrush den linearen Farbverlauf des Hintergrunds einer Schaltfläche. Die beiden GradientStop-Objekte geben Position und Farbe des Farbverlaufs an.
Abbildung 18.8 Button mit linearem Farbverlauf
Die Eigenschaft-Element-Schreibweise können Sie auch bei einfachen Eigenschaften wie zum Beispiel Height und Width verwenden. Sie geben dann den gewünschten Wert zwischen den beiden Tags an:
<Button>
<Button.Height>50</Button.Height>
<Button.Width>100</Button.Width>
Mein erster Button
</Button>
Listing 18.11 Eigenschaft-Element-Syntax elementarer Properties
18.2.5 Inhaltseigenschaften![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Sie kennen nun die Attributschreibweise und die Eigenschaft-Element-Syntax. Doch wie verhält es sich mit der Beschriftung einer Schaltfläche? Diese ist tatsächlich ein Sonderfall unter den Eigenschaften, da die Beschriftung auf zweierlei Art und Weise vorgenommen werden kann. Entweder Sie geben dazu die Eigenschaft Content als Attribut an oder zwischen dem ein- und ausleitenden Element. Also entweder schreiben Sie
<Button Content=" Mein erster Button"></Button>
oder
<Button>Mein erster Button</Button>
Beide Varianten sind gleichwertig. Es steht nun die Frage im Raum, warum wir in der letztgenannten Form nicht ausdrücklich die Eigenschaft angeben müssen, also:
<Button>
<Button.Content>
Mein erster Button
</Button.Content>
</Button>
Die Antwort lautet: Weil Content als sogenannte Inhaltseigenschaft für das Button-Element definiert ist. Bei einer Inhaltseigenschaft kann auf die explizite Angabe der Eigenschaft in der Eigenschaft-Element-Schreibweise verzichtet werden. Bei einer Schaltfläche ist die Eigenschaft Content auch gleichzeitig die Inhaltseigenschaft, weil das Steuerelement Button von der Basis ContentControl abgeleitet ist. Hintergrund dabei ist, dass ContentControl mit dem Attribut ContentPropertyAttribute verknüpft ist, mit dem die Inhaltseigenschaft aller ableitenden Komponenten angegeben wird:
Listing 18.12 Definition einer Inhaltseigenschaft
Das Besondere der Inhaltseigenschaft Content ist deren Datentyp Object. Dadurch lässt sich der Inhaltseigenschaft des betreffenden Elements, also auch dem Button, ein beliebiges Objekt zuordnen, beispielsweise ein Rectangle-Element, wie im folgenden Listing gezeigt.
<Button Height="100" Width="200" Background="LightGray">
<Rectangle Height="30" Width="100" Fill="DarkBlue" />
</Button>
Listing 18.13 Eigenschaft »Content« einer Schaltfläche beschreibt ein »Rectangle«
Verschachteln von Elementen
Es lässt sich am Listing 18.13 sehr schön erkennen, wie das Element Rectangle in das Element Button eingebettet ist. Natürlich muss es sich bei dem eingebetteten Element nicht unbedingt um ein grafisches Element handeln, es kann auch ein herkömmliches Steuerelement sein, zum Beispiel eine ListBox:
<Button Height="100" Width="200">
<ListBox Width="80">
<ListBoxItem>Niederlande</ListBoxItem>
<ListBoxItem>Belgien</ListBoxItem>
<ListBoxItem>Frankreich</ListBoxItem>
</ListBox>
</Button>
Listing 18.14 Das Steuerelement »ListBox« mit mehreren eingebetteten Elementen
Wollen Sie mehrere Elemente der Schaltfläche unterordnen, beispielsweise ein Label, eine ListBox und darüber hinaus auch noch ein Image, scheint das im ersten Moment nicht möglich zu sein, weil die Content-Eigenschaft nur ein Element zulässt. Dennoch ist die Lösung ganz simpel. Sie wählen dann als dem Button untergeordnetes Element eines der Containersteuerelemente (z. B. ein Grid), in dem Sie die gewünschten Steuerelemente entsprechend positionieren. Die Verschachtelung der Elemente ineinander hat nahezu keine Grenzen und ist eine der Stärken der WPF. Es ermöglicht uns, mit relativ einfachen Mitteln neue Steuerelemente zu erstellen oder das Layout bestehender Steuerelemente nach unseren eigenen Vorstellungen oder denen unserer Kunden anzupassen.
Klassen mit beliebigen Inhalten
Content ist die Inhaltseigenschaft aller Steuerelemente, die von ContentControl abgeleitet sind. Dazu gehört auch die Klasse Button. In der WPF sind insgesamt sogar vier Klassen definiert, die eine Inhaltseigenschaft vorschreiben. Jede dieser Basisklassen zwingt ihren Ableitungen eine bestimmte Charakteristik hinsichtlich der Inhaltsbeschreibung auf. Bei einer ListBox sind es zum Beispiel gleich mehrere Elemente, die angegeben werden können, ohne dass die entsprechende Inhaltseigenschaft Items angegeben werden muss:
<ListBox>
<ListBoxItem>Freitag</ListBoxItem>
<ListBoxItem>Samstag</ListBoxItem>
<ListBoxItem>Sonntag</ListBoxItem>
</ListBox>
Listing 18.15 »ListBox«-Control mit mehreren Einträgen
Der Tabelle 18.1 sind die vier Klassen, die eine Inhaltseigenschaft vorschreiben, zu entnehmen.
Klasse | Beschreibung |
Legt die Eigenschaft Content als Inhaltseigenschaft fest. Diese Eigenschaft kann nur ein Element eines beliebigen Typs beschreiben (z. B. Button, CheckBox, Label). |
|
Diese Klasse ist selbst von ContentControl abgeleitet und ergänzt die Fähigkeit der Inhaltseigenschaft um die Möglichkeit eines beschreibenden Headers (z. B. Expander, GroupBox, TabItem). |
|
Legt die Eigenschaft Items als Inhaltseigenschaft fest. Items kann eine Auflistung mehrerer Elemente beschreiben (z. B. ListBox, ComboBox, Menu). |
|
Diese von ItemsControl abgeleitete Klasse dient als Basisklasse aller Steuerelemente, die mehrere Elemente enthalten, die durch genau einen Header beschrieben werden (z. B. MenuItem, ToolBar). |
Klassen, die UI-Elemente enthalten
Einen Sonderfall hinsichtlich der Inhaltseigenschaften bilden die Klassen, die selbst Steuerelemente enthalten und verwalten. Dabei handelt es sich um alle Layout-Container wie beispielsweise das Grid, das StackPanel oder das DockPanel. In Kapitel 19 werden wir alle Layout-Container eingehend besprechen. Aber an dieser Stelle sei im Zusammenhang mit den Inhaltseigenschaften schon das Folgende verraten: Alle diese Klassen leiten sich von der Basis Panel ab, in der die Eigenschaft Children als Inhaltseigenschaft festgelegt ist. Die Eigenschaft Children selbst beschreibt eine UIElementCollection, in der Objekte verwaltet werden, die sich auf den Typ UIElement zurückführen lassen. Dazu gehören beispielsweise alle Steuerelemente. Sehen wir uns nun noch im folgenden Listing am Beispiel des StackPanels an, wie der Inhaltseigenschaft Children Elemente hinzugefügt werden können.
<StackPanel>
<Button>OK</Button>
<Button>Übernehmen</Button>
<Button>Abbrechen</Button>
</StackPanel>
Listing 18.16 Hinzufügen von Steuerelementen zur Eigenschaft »Children« in XAML
Entwickeln Sie eine WPF-Anwendung, wird Ihr Schwerpunkt die XAML-Codierung sein. Nichtsdestotrotz können Sie alles, was Sie im XAML-Code schreiben, auch mit C#-Code erreichen. Um beispielsweise den XAML-Code aus Listing 18.16 durch C#-Code abzubilden, sind die folgenden Anweisungen notwendig:
StackPanel stackPanel = new StackPanel();
Button btnOK = new Button();
btnOK.Content = "OK";
Button btnUebernehmen = new Button();
btnUebernehmen.Content = "Übernehmen";
Button btnAbbrechen = new Button();
btnAbbrechen.Content = "Abbrechen";
stackPanel.Children.Add(btnOK);
stackPanel.Children.Add(btnUebernehmen);
stackPanel.Children.Add(btnAbbrechen);
this.AddChild(stackPanel);
Listing 18.17 Die Elemente des Listings 18.16 durch C#-Code erzeugt
Dieses Beispiel zeigt eindrucksvoll, dass XAML deutlich kürzer und kompakter ist als die Beschreibung der Oberfläche mittels Programmcode.
18.2.6 Typkonvertierung![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Die im XAML-Code definierten Elemente entsprechen einer Klasse, die Attribute einer Eigenschaft. Dabei wird den Attributen ein Wert immer als Zeichenfolge übergeben. Die meisten Eigenschaften sind aber nicht vom Datentyp String und müssen daher in den tatsächlichen Datentyp konvertiert werden. Betrachten wir dazu das einfache Beispiel der Eigenschaften Width und Height eines Buttons.
<Button Height="100" Width="200">Beenden</Button>
Beide Eigenschaften sind vom Typ Double. Die Folge ist, dass die angegebenen Werte in die Fließkommazahlen 100,0 bzw. 200,0 konvertiert werden müssen. In der WPF sind zahlreiche Typkonvertierungen vordefiniert. Das obige Beispiel zeigt eine recht einfache Typkonvertierung bei der Wertzuweisung an die Eigenschaft Width, nämlich von String in Double. Ein komplexer Fall liegt vor, wenn wir der Eigenschaft Background eine Farbe übergeben.
<Button Background="Blue"></Button>
Tatsächlich ist die Eigenschaft Background vom Typ Brush. Da die Klasse Brush abstrakt definiert ist, muss sie in eine ihrer Ableitungen konvertiert werden. Solange wir es mit einer monochromen Farbe zu tun haben, wird es eine Konvertierung in den Typ SolidColorBrush sein. Dieser Sachverhalt wird deutlich, wenn wir uns die entsprechende Eigenschaft-Element-Schreibweise ansehen:
<Button>
<Button.Background>
<SolidColorBrush Color="Blue" />
</Button.Background>
</Button>
Hier muss natürlich noch der Farbwert Blue konvertiert werden, da der Typ einer Farbe nicht String, sondern Color ist.
Die Technik der Typkonvertierung wird in XAML auch benutzt, um mehrere ähnliche Eigenschaften zusammenzufassen, um auf diese Weise den XAML-Code kompakter zu gestalten. Margin gehört zu dieser Gruppe von Eigenschaften. Margin ist vom Typ Thickness und legt den äußeren Rand eines Elements fest. In der Eigenschaft-Element-Schreibweise können Sie Margin wie folgt in einem StackPanel einsetzen:
<StackPanel>
<StackPanel.Margin>
<Thickness Left="100" Top="30" Right="50" Bottom="10" />
</StackPanel.Margin>
<Button>Button1</Button>
</StackPanel>
Listing 18.18 Festlegen der Eigenschaft »Margin« (aufwendige Schreibweise)
Typkonvertierung ist hierbei noch nicht im Spiel. Sie können aber die Eigenschaft Margin auch wie folgt festlegen:
<StackPanel Margin="100, 30, 50, 10">
<Button>Button1</Button>
</StackPanel>
Listing 18.19 Festlegen der Eigenschaft »Margin« (kompakte Schreibweise)
Diese Form des Einsatzes von Margin setzt voraus, dass die Zeichenfolge von einem Typkonvertierer passend umgesetzt wird (was natürlich der Fall ist). Selbstverständlich ist die Eigenschaft mit der erforderlichen Verhaltensweise ausgestattet, die Werte entsprechend zu verarbeiten.
18.2.7 Markup-Erweiterungen (Markup Extensions)![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Die Eigenschaften eines Elements werden in XAML durch Attribute beschrieben, die als Zeichenfolge angegeben und passend ausgewertet werden. Dieses Konzept wird auch in XML benutzt, hat aber seine Grenzen, wenn ein Eigenschaftswert durch einen Objektverweis beschrieben werden muss. An dieser Stelle kommen Markup-Erweiterungen ins Spiel, die uns das ermöglichen.
Im Grunde genommen stellen auch Typkonvertierer einen Weg dar, um ein Objekt an eine Eigenschaft zu binden. Der Unterschied zwischen einem Typkonvertierer und einer Markup-Erweiterung ist jedoch, dass Typkonvertierer nach einer festgelegten Regel arbeiten und im Hintergrund agieren, während Markup-Erweiterungen allgemein verwendbar sind.
Markup-Erweiterungen sind Attributwerte, die in geschweiften Klammern eingeschlossen angegeben sind. Im folgenden Beispielcode wird das Konzept der Markup-Erweiterung dazu benutzt, den Inhalt der Eigenschaft Text der TextBox txtUnten an die Eigenschaft Text der TextBox txtOben zu binden. Das hat zur Folge, dass zur Laufzeit eine Eingabe in der oberen TextBox sofort von der unteren TextBox übernommen wird.
<StackPanel>
<TextBox Name="txtOben"></TextBox>
<TextBox Name="txtUnten"
Text="{Binding ElementName=txtOben, Path=Text}">
</TextBox>
</StackPanel>
Listing 18.20 Die Eigenschaft »Text« mit einer Markup-Erweiterung
Mit den geschweiften Klammern geben wir an, dass der Attributwert weder ein Literal noch ein über einen Typkonvertierer umwandelbarer Wert ist. Die Klasse, die innerhalb der Markup-Erweiterung verwendet wird, ist Binding. Dieser Typ gehört zum Namespace System.Windows.Data. Der Parameter ElementName gibt das Element an, an das gebunden wird, Path beschreibt die Eigenschaft des Quellelements, aus der der Wert bezogen werden soll. Beachten Sie, dass zwischen den Parametern ein Komma gesetzt werden muss.
Eine Steuerelementeigenschaft, die mit einer Markup Extension an eine andere Eigenschaft gebunden wird, muss als Abhängigkeitseigenschaft implementiert sein (siehe Abschnitt 18.1.4).
Werden den Parametern einer Markup-Erweiterung mit dem Zuweisungsoperator = Werte übergeben (wie ElementName und Path in Listing 18.20), wird die Markup-Erweiterung Binding mit dem parameterlosen Konstruktor instanziiert. Die Parameterwerte für ElementName und Path werden den gleichnamigen Eigenschaften des Binding-Objekts übergeben.
Enthalten die Parameter hingegen kein =-Zeichen, werden sie an den Konstruktor der Markup-Erweiterung weitergeleitet. Selbstverständlich müssen dann Anzahl und Typ mit dem des Konstruktors übereinstimmen. Da die Klasse Binding einen Konstruktor beschreibt, der einen String für die Eigenschaft Path entgegennehmen kann, wäre auch die folgende Markup Extension zulässig:
<TextBox Name="txtUnten" Text="{Binding Text, ElementName=txtOben}">
Markup-Erweiterungen werden nicht nur durch den Typ Binding beschrieben. StaticResource, DynamicResource oder auch x: sind weitere wichtige Erweiterungen, die uns noch beschäftigen werden. Erwähnt werden sollte auch, dass das enorme Potenzial der WPF hinsichtlich der Datenbindung erst durch Markup-Erweiterungen voll ausgeschöpft wird.
Die Eigenschaft-Element-Schreibweise ist auch im Zusammenhang mit Markup-Erweiterungen möglich. Listing 18.21 zeigt das Beispiel aus Listing 18.20 noch einmal, ist aber in Eigenschaft-Element-Schreibweise formuliert.
<StackPanel>
<TextBox Name="txtOben"></TextBox>
<TextBox Name="txtUnten">
<TextBox.Text>
<Binding ElementName="txtOben" Path="Text" />
</TextBox.Text>
</TextBox>
</StackPanel>
Listing 18.21 Markup-Erweiterung mit der Eigenschaft-Element-Syntax
Zudem werden Sie noch sehen, dass auch mehrfach verschachtelte Markup-Erweiterungen möglich sind.
Markup-Erweiterungen durch C#-Code beschreiben
Markup-Erweiterungen lassen sich nicht nur deklarativ im XAML-Code festlegen, sondern auch durch Programmcode beschreiben. Wir wollen uns das am Beispiel der beiden Textboxen ansehen.
public MainWindow() {
InitializeComponent();
// StackPanel erstellen
StackPanel panel = new StackPanel();
this.AddChild(panel);
// TextBox oben erstellen
TextBox txtOben = new TextBox();
panel.Children.Add(txtOben);
// TextBox unten erstellen
TextBox txtUnten = new TextBox();
panel.Children.Add(txtUnten);
// Bindung erzeugen
Binding binding = new Binding("Text");
binding.Source = txtOben;
txtUnten.SetBinding(TextBox.TextProperty, binding);
}
Listing 18.22 Bindung mittels Programmcode
Der Code ist im Konstruktor des Fensters nach dem Aufruf der Methode InitializeComponent implementiert. Zuerst wird das StackPanel-Objekt erzeugt und dem Window durch Aufruf von AddChild als untergeordnetes Element übergeben. Die beiden Textboxen werden nach der Instanziierung zu untergeordneten Elementen des StackPanels. Dieser Container liefert durch Aufruf der Eigenschaft Children die Referenz auf ein UIElementCollection-Objekt, dem mit der Methode Add die Textboxen hinzugefügt werden.
Die Bindung der unteren an die obere TextBox erfordert ein Binding-Objekt, dessen Konstruktor Sie die Eigenschaft des Elements bekannt geben, an die gebunden werden soll. Das Element, an das gebunden wird, gibt man der Eigenschaft Source an.
Aktiviert wird die Bindung der unteren an die obere TextBox mit der Methode SetBinding. Während das zweite übergebene Argument keiner besonderen Erklärung bedarf, erscheint das erste ungewöhnlich. Aber genau so wird eine Abhängigkeitseigenschaft angesprochen.
18.2.8 XML-Namespaces![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
Bei den XML-Namespaces handelt es sich um eine Vorschrift, um Elemente im XAML-Code eindeutig zuordnen und somit interpretieren zu können. Beispielsweise könnte das Element <Button> in einem XAML-Dokument zwei unterschiedliche Schaltflächen, also Klassen, beschreiben. Erst die Zuordnung zu einem Namespace gestattet die eindeutige Identifizierbarkeit. Prinzipiell kommt den XAML-Namespaces somit die gleiche Bedeutung zu wie den CLR-Namespaces.
Im Wurzelelement Window einer XAML-Datei werden bereits beim Anlegen eines Fensters zwei Namespaces verfügbar gemacht:
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
Listing 18.23 Standardvorgabe der XML-Namespaces
Namespaces werden mit dem Attribut xmlns eingeleitet. Optional kann dahinter, durch einen Doppelpunkt getrennt, ein Namespace-Präfix angegeben werden. Dem wird dann oft ein URI zugeordnet.
Sehen wir uns die beiden Namespaces im Window an. Der erste weist kein Präfix auf und ist der sogenannte Standard-Namespace. Alle diesem Namespace zugeordneten Elemente können ohne Präfixangabe in XAML verwendet werden. Dazu gehören beispielsweise die Elemente <Grid>, <Button> oder <TextBox>.
Da nur ein XML-Namespace ohne Präfix als Standard-Namespace angegeben werden darf, müssen alle anderen Elemente, die nicht dem Standard-Namespace zugeordnet werden, ein Präfix aufweisen. Die zweite Namespace-Angabe im Element Window definiert ein solches mit x. Dieser Namespace dient den XAML-Spracherweiterungen. Verwenden Sie ein Element dieses Namespaces, müssen Sie vor dem Element das Namespace-Präfix, gefolgt von einem Doppelpunkt, angeben. Ein gutes Beispiel liefert bereits die XAML-Struktur eines Fensters mit
<Window x:Class="WpfApplication1.MainWindow" ...>
CLR-Namespaces verwenden
Viele WPF-Anwendungen benötigen über die Standardvorgaben hinaus noch weitere Namespaces. Dabei könnte es sich durchaus auch um CLR-Namespaces handeln.
Angenommen, wir möchten in einer WPF-Anwendung mit XAML auf die Klasse Circle der Assembly GeometricObjects.dll zugreifen. Nachdem Sie in der WPF-Anwendung einen Verweis auf die Klassenbibliothek gelegt haben, geben Sie den Namespace im Wurzelelement Window wie folgt an:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:geo="clr-namespace:GeometricObjects;assembly=GeometricObjects"
Title="Window1" Height="300" Width="300">
</Window>
Listing 18.24 Bekanntgabe eines CLR-Namespaces
Das Präfix, hier geo, ist frei wählbar, muss aber eindeutig in der aktuellen XAML-Datei sein. Danach folgen zwei Name-Wert-Paare, die durch ein Semikolon voneinander getrennt sind. Das erste Paar wird mit clr-namespace eingeleitet. Dahinter folgt ein Doppelpunkt und anschließend der CLR-Namespace, dem die Klasse Circle zugeordnet ist. Das zweite Name-Wert-Paar wird mit assembly eingeleitet. Nach dem »=«-Zeichen wird die Assembly angegeben, jedoch ohne die Dateiendung DLL.
Jetzt können Sie die Klasse Circle im XAML-Code verwenden und den Eigenschaften die gewünschten Werte übergeben. Der Elementangabe müssen Sie dabei das gewählte Präfix voranstellen, hier also geo.
<geo:Circle Radius="77" XCoordinate="100" YCoordinate="-250">
</geo:Circle>
Etwas einfacher wird die Bekanntgabe eines Namespaces, wenn dieser zum aktuellen Projekt gehört. Sie können in diesem Fall auf die Angabe von assembly verzichten, es reicht clr-namespace aus, z. B.:
xmlns:local="clr-namespace:WpfApplication1"
Mehrere CLR-Namespaces zusammenfassen
Entwickeln Sie Klassenbibliotheken, sollten Sie daran denken, die Klassen für den Einsatz in XAML passend vorzubereiten. Dazu gehört auch die Berücksichtigung der Namespaces. Das gilt insbesondere dann, wenn in einer Klassenbibliothek mehrere CLR-Namespaces definiert sind. Nehmen wir exemplarisch an, die beiden Klassen Rectangle und Circle seien unterschiedlichen Namespaces zugeordnet.
namespace Namespace1 {
public class Circle { [...] }
}
namespace Namespace2 {
public class Rectangle { [...] }
}
Ohne weitere Maßnahmen zu ergreifen, können Sie beide Typen nur dann im XAML-Code nutzen, wenn Sie beide Namespaces im Wurzelelement angeben, z. B.:
xmlns:geo1="clr-namespace:Namespace1;assembly=GeometricObjects"
xmlns:geo2="clr-namespace:Namespace2;assembly=GeometricObjects"
Beide Namespaces lassen sich aber auch auf einen gemeinsamen XML-Namespace abbilden. Dazu muss die entsprechende Vorkehrung bereits in der Klassenbibliothek erfolgen. Die notwendigen Angaben sind in der Datei AssemblyInfo.cs der Klassenbibliothek zu machen. Ergänzen Sie diese dazu, wie nachfolgend gezeigt, um zwei Attributangaben:
[assembly: XmlnsDefinition("http://www.tollsoft.de", "Namespace1")]
[assembly: XmlnsDefinition("http://www.tollsoft.de", "Namespace2")]
Um auf das Attribut XmlnsDefinition zugreifen zu können, müssen Sie zuerst einen Verweis auf die Bibliothek System.Xaml.dll legen. Darüber hinaus sollten Sie auch den Namespace System.Windows.Markup mit using bekannt geben.
Das Attribut beschreibt zwei Parameter. Dem ersten übergeben Sie den gewünschten XML-Namespace, dem zweiten Parameter teilen Sie mit, welcher CLR-Namespace auf diesen XML-Namespace abgebildet werden soll. Der XAML-Code reduziert sich daraufhin auf die folgende Angabe im Wurzelelement:
xmlns:geo="http://www.tollsoft.de"
Sie können anschließend mit dem Präfix geo Elemente sowohl vom Typ Circle als auch vom Typ Rectangle in Ihren XAML-Code einbetten.
<geo:Circle><geo:Circle>
18.2.9 XAML-Spracherweiterungen![Zur nächsten Überschrift](common/down.gif)
![Zur vorigen Überschrift](common/top.gif)
XAML definiert eine Reihe von Attributen, die besondere Aspekte bei der Entwicklung berücksichtigen und keine Entsprechungen in einer Klasse besitzen. Hiermit werden nur Zusatzinformationen geliefert, die einer besonderen Verarbeitung bedürfen. Tabelle 18.2 stellt Ihnen einige davon vor. In den folgenden Kapiteln werden Sie in den Beispielen auf einige dieser Schlüsselwörter stoßen.
Schlüsselwort | Bedeutung |
Dieses Attribut stellt die Beziehung zwischen dem Wurzelelement im XAML-Code und der Code-Behind-Datei her. |
|
Die Trennung von Code und Oberflächenbeschreibung ist keine strikte Vorgabe. Sie können auch Code innerhalb einer XAML-Datei implementieren. Mit x:Code wird ein Codebereich im XAML-Code definiert. |
|
Gibt den eindeutigen Namen eines Elements in einer Ressource an. |
|
Mit diesem Attribut können Sie einem Element einen Namen geben, wenn das Element selbst nicht über eine Eigenschaft Name verfügt. |
Markup-Erweiterungen
Es gibt mehrere Markup-Erweiterungen, die nicht spezifisch für die WPF-Anwendung sind, sondern Implementierungen für Funktionen von XAML als Sprache sind. Auch diese Markup-Erweiterungen sind durch das »x:«-Präfix in der Verwendung identifizierbar.
Erweiterung | Beschreibung |
Hiermit lassen sich Arrays in XAML definieren. Beispiel: <x:Array Type="clr:Int32"> Beachten Sie bitte, dass für die Verwendung des Integers der CLR-Namespace System bekannt gegeben werden muss. Im Beispiel wurde dazu das Präfix clr verwendet. |
|
Wird verwendet, um einem Element null zuzuweisen. Beispiel: <Button Background="{x:Null}" /> |
|
Referenziert eine statische Variable oder Eigenschaft eines Objekts oder eine Konstante oder einen Enumerationswert. Beispiel: <Button Background="{x:Static Brushes.Red}" /> |
|
Wird beispielsweise in Stildefinitionen benutzt, um einen Typ anzugeben. Beispiel: <Style TargetType="{x:Type TextBox}" /> |
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.