| Eine weitere Technik, die mit WinFX eingeführt wurde, ist XAML (ausgesprochen: [za:mɛ l]), die Extensible Application Markup Language. Im Zusammenhang mit WPF ist XAML eine Beschreibungssprache mit der sich elegant Benutzeroberflächen zusammenstellen lassen. Microsoft hat an dieser Stelle das aus ASP.NET bekannte „Code-behind” adaptiert. Im Falle von XAML heißt dies, man definiert den Aufbau und das Aussehen seiner Anwendung in einer getrennten Datei im XAML-Format und legt sämtlichen Code, der für die Funktionalität zuständig ist, in einer zugehörigen Datei ab. Die Programmanweisungen liegen hier also „hinter” („Code behind”) der Oberflächendefinition und verleihen der Anwendung ihr Leben. Microsoft betont an dieser Stelle immer wieder, dass diese Aufteilung zwischen der Oberfläche und dem Code zu einer besseren Zusammenarbeit zwischen Designern und Programmierern führt, was aber möglicherweise eher größere Entwicklerteams interessieren wird. Da eine komplette Einführung in XAML und die Funktionalitäten des WPFs mehrere Bücher füllen könnten, will ich hier lediglich die meiner Meinung nach wichtigsten Grundlagen der beiden neuen Techniken in Form von kleinen Beispielen vorstellen. Zum Schreiben und Testen von XAML-Code ist das mit dem Windows Plattform SDK mitgelieferte XamlPad eine große Hilfe. In Zukunft wird es übrigens nicht mehr nötig sein, den XAML-Quelltext per Hand zu schreiben. Microsoft wird ein speziell auf das Design von XAML basierten Benutzeroberflächen ausgerichtetes Programm unter dem Namen Sparkle veröffentlichen, auch das neue Visual Studio 2006 wird Entwurfsfähigkeiten besitzen. Layout WPF bringt nun endlich Behältnissteuerelemente mit, die einen Einfluss über die Position und Größe der in ihnen enthaltenen Steuerelemente ausüben. Auf diese Weise lassen sich Fenster mit Steuerelementen übersäen, welche sich beim Ändern der Größe der Form vollkommen korrekt verhalten, ohne dass dafür eine Zeile Programmcode per Hand geschrieben werden muss: <Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
Title="Eine Uhr!">
<DockPanel>
<TextBlock DockPanel.Dock="Top">
Dies ist eine Uhr!
</TextBlock>
<Button Margin="10,0,10,5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<MediaElement Source="C:\clock.avi" Grid.Row="0" />
<TextBlock Grid.Row="1">Klick mich!</TextBlock>
</Grid>
</Button>
</DockPanel>
</Window> Listing 1: Ein Beispiel über die Verwendung der Container-Steuerelemente und die Kompositionsfähigkeiten der WPF Dieses schon relativ komplizierte Beispiel zeigt gleich die Definition von zwei verschiedenen Layout-Containern: DockPanel und Grid. Bei dem DockPanel handelt es sich um das oberste Element in unserem Fenster. Es enthält zwei Steuerelemente: einen TextBlock und eine nicht ganz normale Schaltfläche, wie wir später noch sehen werden. Die beiden Controls sind, wie man erkennen kann, durch vollkommen normale XML-Elemente definiert. Eine Besonderheit ist hier die Eigenschaft „Dockpanel.Dock” des TextBlocks. Hierbei handelt es sich um eine geerbte Eigenschaft, die das Steuerelement erhalten hat, da es sich in einem DockPanel befindet. Die Schaltfläche wiederum zeigt sehr schön die Kompositionsfähigkeiten des grafischen Systems der WPF. Anstatt eines einfachen Textes haben wie hier nämlich ein weiteres Behältnissteuerelement, ein Grid, welches ein MediaElement und einen weiteren TextBlock enthält. Über die RowDefinitions wird festgelegt, wie genau der zur Verfügung stehende Platz zwischen den Elementen verteilt werden soll; die Steuerelemente werden wieder durch ererbte Eigenschaften in die verschiedenen Reihen sortiert. Auf diese Weise erhalten wir eine schöne Schaltfläche, auf dem ein Video abgespielt wird. Abbildung 2: Der Videobutton, erzeugt durch den Quelltext in Listing 1 Neben dem DockPanel und dem Grid existiert noch eine Reihe weiterer Layout-Steuerelemente. Darunter befindet sich das Canvas-Steuerelement, auf welchem sich Steuerelemente wie gewohnt durch die Angabe von Position und Größe anordnen lassen, das StackPanel, welches die in ihm enthaltenen Steuerelemente entweder horizontal oder vertikal nebeneinander anzeigt, oder das WrapPanel, welches die Steuerelemente automatisch umbricht. Selbstverständlich ist XAML nicht der einzige Weg eine Form zusammenzustellen, auch die Möglichkeit des Hinzufügens und des Editierens in Code ist gegeben. Ereignisse Selbstverständlich ist es in XAML sehr einfach, einen Ereignis-Handler für ein Ereignis hinzuzufügen: <StackPanel Orientation="Vertical">
<TextBlock Name="Ausgabe" Margin="5"></TextBlock>
<Button Click="SchaltflaecheWurdeGeklickt" Margin="5">
Klicken Sie hier!
</Button>
</StackPanel> Listing 2: Hinzufügen einer Ereignisprozedur in XAML Wir haben hier einen TextBlock mit dem Namen „Ausgabe” und eine Schaltfläche. Diese Schaltfläche besitzt neben vielen anderen ein „Click”-Ereignis. Um also eine Routine mit diesem Ereignis zu verknüpfen, müssen wir lediglich Click, wie einer einfachen Eigenschaft, den Namen einer Prozedur zuweisen. Diese Prozedur sollte sich nun in der zugehörigen Code-Datei befinden und könnte zum Beispiel so aussehen: Partial Public Class Window1
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
Public Sub SchaltflaecheWurdeGeklickt(ByVal sender As Object,
ByVal e As RoutedEventArgs)
Ausgabe.Text = "Vielen Dank für Ihre Mühe!"
End Sub
End Class Listing 3: Die zu Listing 2 passende Ereignisprozedur Hier haben wir eine einfache Ereignisprozedur, die lediglich den Text des TextBlockes verändert. Wie wir sehen können, kann man auf in XAML definierte Steuerelemente ohne Probleme einfach mit ihrem Namen zugreifen. Dies ist möglich, da die dazugehörige Klasse, Window1, als Partial deklariert ist, so dass ein XAML-Compiler eine dazu passende Teilklasse mit den benötigten Definitionen erstellen kann. Die bedeutet logischerweise auch, dass jedes Element in der XAML-Datei eine Gegenklasse im .NET Framework besitzt. Styles und Trigger Ebenso aus dem Bereich der Webentwicklung entnommen ist das Konzept der Stile, mit welchem Viele möglicherweise bereits aufgrund von CSS vertraut sind. Über Stile können wir einen Haufen von Eigenschaftswerten einen Namen verpassen und diesen Satz dann auf Controls anwenden. <Window.Resources>
<Style x:Key="Gefährlich" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Margin" Value="5,10,5,10" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical">
<Button Style="{StaticResource Gefährlich}">
Achtung: Ende der Welt!
</Button>
<Button Style="{StaticResource Gefährlich}">
Auf KEINEN Fall drücken: Ende der Welt!
</Button>
<Button Style="{StaticResource Gefährlich}">
Weltuntergang? Diese Richtung...
</Button>
</StackPanel> Listing 4: Verwendung von Styles und Trigger In den ersten zwölf Zeilen wird ein Stil für eine Schaltfläche festgelegt. Im Window.Resources-Block können Resourcen für ein Fenster festgelegt werden. Hierbei kann es sich um Objekte wie Pinsel (Brushes) handeln, aber eben auch um Stile für Steuerelemente. In Zeile 2 wird ein solcher definiert. Wir müssen an dieser Stelle immer mindestens zwei Eigenschaften setzen, einen Schlüssel, da es sich bei der .Resources-Erweiterung um ein Dictionary (Hash, Liste aus Schlüssel-Wert-Paaren) handelt und einen Steuerelemente-Typ, in diesem Falle Button. Darauf folgend werden über das Setter-Element einige Eigenschaften gesetzt. Dieser Stil wird nun unten auf alle drei Schaltflächen angewendet. In diesem Beispiel sehen wir ein neues Konstrukt, und zwar die geschwungenen Klammern in der Eigenschaftszuweisung. Um den Sinn derselben zu verstehen, muss man wissen, wie WPF Zuweisungen an Eigenschaften entgegennimmt. Nehmen wir als Beispiel einer normalen Eigenschaft hier die Margin-Eigenschaft. Diese kann verschiedene Arten von Werten annehmen, z.B. ” oder ,10,5,10”. An dieser Stellen tritt dann ein Typkonverter ein, der diese Zeichenfolge in eine Thickness-Struktur umwandelt. Diese Form der Umwandlung ist aber an der Stelle des Stils nicht erwünscht, wir möchten stattdessen auf eine Resource verweisen. Dies geht, in dem wir die StaticResource-Erweiterung dazu auffordern die Eigenschaft mit unserem Stil zu verknüpfen. Ähnliches geschieht auch im Falle der TargetType-Eigenschaft, nur das an dieser Stelle nicht auf eine Resource verknüpft werden soll, sondern an einen Typ. Abbildung 3: Die stylistisch veränderten Schaltflächen aus dem vorhergehenden Code-Beispiel Bei dem Teil der Stil-Deklaration den ich noch übersprungen habe, handelt es sich um einen sogenannten Trigger. Ein Trigger, wie oben definiert, wird immer ausgelöst, wenn eine bestimmte Eigenschaft einen bestimmten Wert annimmt. In diesem Fall handelt es sich dabei um die IsMouseOver-Eigeschaft, welche angibt, ob sich die Maus über dem Steuerelement befindet. Sobald diese den Wert „true” annimmt, gelten die in dem Trigger definierten Setter. Unsere Schaltflächen erhalten also einen blauen Hintergrund, solange sich die Maus über ihnen befindet. Datenbindung Datenbindung ist ein Vorgang, in dem Daten aus unterschiedlichsten Herkünften an ein Steuerelement zur Anzeige gebunden werden. Die Daten stammen im Falle der WPF aus einem DataSourceProvider, von denen WPF auch schon zwei mitbringt, einen für XML-Daten und eine für .NET-Objekte. Obwohl die Datenbindung der Windows Forms-Steuerelemente ständig verbessert wurde, war es häufig besser eine eigene Logik für diese zu implementieren. Damit dies nicht mehr nötig ist, spielte die uneingeschränkte Funktionalität der Datenbindung beim Entwurf von Avalon eine große Rolle. Im folgenden Beispiel soll gezeigt werden, wie einfach es ist, Daten aus .NET-Objekten in einer Listbox anzuzeigen: <?Mapping XmlNamespace="katzen" ClrNamespace="Katzen" ?>
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns:katzen="katzen"
Title="Katzen"
>
<StackPanel Orientation="Vertical">
<ListBox Margin="5" DisplayMemberPath="Name">
<katzen:Katze Name="Miezi" />
<katzen:Katze Name="Klausi" />
<katzen:Katze Name="Robbi" />
<katzen:Katze Name="Schorsch" />
</ListBox>
</StackPanel>
</Window> Listing 5: Ein Beispiel für die einfache Datenbindung von Objekten an ein Steuerelement Bei unserer gewünschten Datenklasse handelt es sich um die Klasse Katze, welche hier lediglich eine Name-Eigenschaft besitzt. Über das Mapping in der ersten Zeile und die xmlns-Anweisung im Window-Tag weisen wir den XAML-Compiler an, den .NET-Namespace „Katzen” unseres Projektes, als „katzen”-XML-Namespace einzubinden. Von nun an, können wir die Katze-Klasse in Form eines XML-Elements in eine ListBox einfügen. Im Grunde kann eine ListBox lediglich ListBoxItems enthalten, die Umwandlung erledigt die ListBox glücklicherweise für uns. Dabei ist die DisplayMemberPath-Eigenschaft der Listbox von Bedeutung, hiermit wird nämlich angegeben, welchen Wert die für uns erstellten ListBoxItems erhalten sollen. Im nächsten Beispiel wird über eine DataTemplate-Resource unseren „Katzen”-Listboxelementen ein eigenes Aussehen verliehen: <?Mapping XmlNamespace="katzen" ClrNamespace="Katzen" ?>
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns:katzen="katzen"
Title="Katzen"
>
<Window.Resources>
<DataTemplate x:Key="Katze" DataType="{x:Type katzen:Katze}">
<Grid Margin="15,2,15,2">
<Image Source="{Binding Picture}"
Width="80" Height="60"
Grid.Column="0" Grid.Row="0"
Grid.RowSpan="3">
<Image.Clip>
<RectangleGeometry Rect="0,0,80,60"
RadiusX="40"
RadiusY="30" />
</Image.Clip>
</Image>
<TextBlock
Text="{Binding Name}"
Margin="5,0,0,0"
Grid.Row="1" Grid.Column="1" />
<!-- Im Grunde müssten diese Definitionen in
ein RowDefinitions bzw. ColumnDefinitions-
Element. Leider ist dies aufgrund eines
Fehlers in dieser CTP nicht möglich -->
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid>
</DataTemplate>
</Window.Resources>
<StackPanel Orientation="Vertical">
<ListBox Margin="5" ItemTemplate="{StaticResource Katze}">
<katzen:Katze Name="Miezi"
PictureFilename="C:\katze1.bmp" />
<katzen:Katze Name="Klausi"
PictureFilename="C:\katze2.bmp" />
<katzen:Katze Name="Robbi"
PictureFilename="C:\katze3.bmp" />
<katzen:Katze Name="Schorsch"
PictureFilename="C:\katze4.bmp" />
</ListBox>
</StackPanel>
</Window> Listing 6: Die Verwendung von DataTemplates Unsere Klasse hat noch zwei weitere Eigenschaften hinzu erhalten, eine PictureFilename-Eigenschaft, mit der man der Katze ein Bild zuordnen kann und eine Picture-Eigenschaft, die dieses Bild in Form eines ImageSource-Objektes zurückgibt. Am Interessantesten ist jedoch der DataTemplate-Block. Hier wird für die Katzen-ListBoxItems ein neues Aussehen definiert. Zusätzlich finden wir noch eine weitere Erweiterung, Binding, mit der eine Eigenschaft eines Steuerelements an eine Eigenschaft eines Objektes gebunden werden kann. Abbildung 4: Die Katzen-Listbox mit den optisch veränderten ListBoxItems Effekte Eine nette Sache an der Compositing Engine der WPF ist, dass alles vor der Ausgabe grafisch verändert werden kann. So ist es möglich Steuerelementen einen BitmapEffect zuzuweisen, der die Ausgabe manipuliert. Derzeit existieren leider nur ein Verwischen-Effekt und ein Schatten-Effekt, welcher im zugehörigen Bild zu sehen ist. Die Steuerelemente bleiben allerdings selbst beim Verwischen-Effekt noch funktional, auch wenn ihr Inhalt bereits nicht mehr zu erkennen ist... <WrapPanel>
<WrapPanel.BitmapEffect>
<DropShadowBitmapEffect Softness="2" ShadowDepth="10" />
</WrapPanel.BitmapEffect>
<Button Margin="10">1</Button>
<Button Margin="10">2</Button>
<Button Margin="10">3</Button>
<Button Margin="10">4</Button>
<Button Margin="10">5</Button>
<Button Margin="10">6</Button>
<Button Margin="10">7</Button>
<Button Margin="10">8</Button>
<Button Margin="10">9</Button>
</WrapPanel> Listing 7: Ein Beispiel für einen Schatteneffekt Abbildung 5: Die mit einem Schatten unterlegten Schaltflächen Verbesserungen im Bereich von Texten und Dokumenten Doch die Windows Presentation Foundation bringt nicht nur Verbesserungen im Bereichen der Benutzbarkeit der Steuerelemente mit, sondern bietet auch im Bereich der Text- und Dokumentendarstellung einige Neuigkeiten. Mit den neuen Bibliotheken und Windows Vista wird eine neue ClearType-Version veröffentlicht. Neu sind hier zum einen das Sub-Pixel-Positioning, welches es einzelnen Zeichen erlaubt, rechnerisch zwischen zwei nebeneinanderliegenden Pixeln zu beginnen und zum Anderen, Kantenglättung in der Y-Richtung. Auf den meisten Flachbildschirmen dürfte dies die Darstellung von Text weiter verbessern. Zusätzlich unterstützt die Windows Presentation Foundation zum ersten Mal den vollen Umfang der OpenType-Schriftarten. Eine weitere, bisher in anderen Systemen nicht zu findende Neuerung, sind die Komponenten zur Darstellung von Dokumenten. Obwohl die Darstellung längerer Texte mit GDI und GDI+ kein Problem ist, sieht die Darstellung in vielen Fällen nicht besonders optimal aus, da das GDI nicht für solche Fälle ausgerüstet ist. Besonders die Anpassung der Darstellung an den zur Verfügung stehenden Platz lässt häufig zu wünschen übrigt, automatische Aufteilung auf Seiten und zoomen sind nur mit hohem Aufwand zu erreichen. Aus diesem Grund entschlossen sich die WPF-Entwickler dazu, dem Framework eigene Steuerelemente zur Darstellung und zum Editieren von Dokumenten zu verpassen. Hier gibt es eine Unterteilung in zwei Sorten von Dokumenten: FixedDocuments und FlowDocuments. FixedDocuments erlauben eine genaue Positionierung der Elemente auf den Seiten und sind deshalb für alle Programmierer geeignet, die ein Interesse daran haben, dass die Seiten unter verschiedenen Umständen (zum Beispiel sowohl auf dem Monitor als auch auf Papier) immer gleich aussehen. Die zweite Klasse, das FlowDocument, ist auf eine möglichst optimale Textwiedergabe zugeschnitten. Abbildung 6: Textdarstellung in einem FlowDocument Auf diesem Bildschirmphoto können wir einen Teil dieses Artikels mit einigen sinnlosen Einstreuungen erkennen. Die Schrift wird automatisch je nach dem zur Verfügung stehenden Platz auf mehrere Spalten aufgeteilt; der Zoomlevel lässt sich stufenlos anpassen. Weiterhin ist über die Block.IsHyphenationEnabled-Eigenschaft die automatische Silbentrennung eingeschaltet, da diese einen Text deutlich lesbarer macht. Auch das Aufteilen auf mehrere Seiten geschieht vollkommen automatisch. <FlowDocument Block.IsHyphenationEnabled="True">
<Paragraph>...</Paragraph>
<List>
<ListItem>
<Paragraph>
Alle Vögel sind schon da,<LineBreak />
Alle Vögel, alle!<LineBreak />
Welch ein Singen, Musiziern,<LineBreak />
Pfeifen, Zwitschern, Tierelier'n!<LineBreak />
Frühling will nun einmarschier'n,<LineBreak />
Kommt mit Sang und Schalle.<LineBreak />
</Paragraph>
</ListItem>
<ListItem>
<Paragraph>...</Paragraph>
</ListItem>
<ListItem>
<Paragraph>...</Paragraph>
</ListItem>
</List>
<Paragraph>
</Paragraph>
</FlowDocument> Listing 8: Die Verwendung von FlowDocuments Das FlowDocument vereinigt viele der verbesserten Darstellungsfunktionalitäten der WPF. Wer nicht allzu viele Funktionen benötigt, kommt auch häufig mit den TextBlock und dem TextFlow-Steuerelement gut zurecht. Das TextBlock-Steuerelement ist hierbei für einzeiligen Text ausgelegt, währenddessen TextFlow für die Anzeige von Text mit wenigen Absätzen gedacht ist. |