XAML
Rainer Stropek Karin Huber
XAML
schnell + kompakt
Rainer Stropek, Karin Huber XAML schnell + kompakt ISBN 978-3-939084-67-9
© 2007 entwickler.press, ein Imprint der Software & Support Verlag GmbH
1. Auflage, 2007
http://www.entwickler-press.de http://www.software-support.biz Ihr Kontakt zum Verlag und Lektorat:
[email protected] Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.
Korrektorat: Petra Kienle Satz: text & form GbR, Carsten Kienle Umschlaggestaltung: Caroline Butz Belichtung, Druck und Bindung: M.P. Media-Print Informationstechnologie GmbH, Paderborn. Alle Rechte, auch für Übersetzungen, sind vorbehalten. Reproduktion jeglicher Art (Fotokopie, Nachdruck, Mikrofilm, Erfassung auf elektronischen Datenträgern oder andere Verfahren) nur mit schriftlicher Genehmigung des Verlags. Jegliche Haftung für die Richtigkeit des gesamten Werks kann, trotz sorgfältiger Prüfung durch Autor und Verlag, nicht übernommen werden. Die im Buch genannten Produkte, Warenzeichen und Firmennamen sind in der Regel durch deren Inhaber geschützt.
Inhaltsverzeichnis Vorwort
7
Kapitel 1: Einleitung
9
Kapitel 2: Hinter den Kulissen 2.1 Einleitung 2.2 Voraussetzungen 2.3 Basiskonzepte von XAML 2.4 „Hello World“ in XAML 2.5 Kompilieren von XAML-Dateien XAML und MSBuild Die Build-Ergebnisse 2.6 Application Definition Files 2.7 Zusammenfassung
17 17 18 19 20 24 24 27 42 45
Kapitel 3: XAML Namespaces 3.1 Einleitung CLR Namespace XML Namespace 3.2 Namespaces in XAML-Dateien 3.3 Elemente im Namespace x: x:Array x:Class, x:ClassModifier, x:Subclass, x:TypeArguments x:Code x:Name, x:FieldModifier x:Key, x:Shared x:Null x:Static x:Type x:XData
47 47 48 51 53 55 55 57 59 60 61 62 63 64 65
schnell + kompakt
5
Inhaltsverzeichnis
3.4
3.5
XML-Attribute in XAML xml:space xml:lang mc:Ignorable, mc:ProcessContent Zusammenfassung
Kapitel 4: Die Sprache XAML 4.1 Einleitung XAML als deklarative Programmiersprache Das Beispiel 4.2 Die Basiselemente von XAML XAML-Elemente XAML-Attribute Attribute mit Typkonvertierung Markup Extensions Attached Properties 4.3 Zusammenfassung
67 67 68 68 69 71 71 71 72 74 74 77 81 86 94 98
Kapitel 5: XAML und WF 5.1 Einleitung 5.2 XAML oder C#? 5.3 XAML mit WF – ein Beispiel 5.4 Kompilieren von XAML Workflows 5.5 Zusammenfassung
99 99 100 101 107 107
Kapitel 6: XAML Tools 6.1 Einleitung 6.2 Systemvoraussetzungen 6.3 Tools Microsoft Visual Studio Microsoft Expression Blend XAMLPad
109 109 110 111 112 114 115
Stichwortverzeichnis
117
6
Vorwort Wir sind mit Leib und Seele Softwareentwickler! Wir hatten das große Glück, unser Hobby, unsere Leidenschaft zum Beruf machen zu können. Da Sie dieses Buch in Händen halten, ist es recht wahrscheinlich, dass Sie in der gleichen Branche arbeiten wie wir. Daher sind Sie ebenfalls mit der rasend schnellen Weiterentwicklung unserer Branche vertraut. In den letzten Jahren hat sich nach unserer Erfahrung das Tempo der Innovation noch deutlich beschleunigt. Der letzte Höhepunkt dieser Entwicklung ist die Veröffentlichung der neuen, in Windows Vista enthaltenen Komponenten Windows Presentation Foundation (WPF) und Workflow Foundation (WF). Die Marketingmaschinerie von Microsoft tut ihr Bestes, um insbesondere WPF und XAML in den schillerndsten Farben mit beeindruckenden Beispielen anzupreisen. Und tatsächlich – die neuen Möglichkeiten sind enorm. Für uns Softwareentwickler heißt das aber zurück auf die Schulbank! Wenig von unserem über die Jahre hinweg gesammelten Wissen über Programmierung von Windows-Anwendungen ist noch gültig. Als wäre der überwältigende Umfang der WPF-Klassenbibliotheken nicht schon genug, erscheint auch noch XAML als neue Sprache auf der Bildfläche. Auf den ersten Blick ist XAML nichts Neues, XML zur Definition von Benutzeroberflächen eben. Beschäftigt man sich jedoch genauer damit, wird ein viel mächtigeres und breiter einsetzbares Konzept sichtbar. Wir möchten Sie in diesem Buch mit der neuen Sprache XAML vertraut machen. Unser Ziel ist nicht die vollständige Beschreibung von WPF oder WF, das würde den Rahmen sprengen. Aufgrund ihrer Bedeutung beim Umgang mit XAML werden wir jedoch bei allen Sprachkonzepten immer konkrete Beispiele aus
schnell + kompakt
7
Vorwort
diesem Bereichen zeigen. Nichtsdestotrotz demonstrieren wir die Sprachelemente auch anhand von WPF- und WF-unabhängigen Beispielen, um vorzuführen, wie in eigenen Anwendungsfällen XAML ganz unabhängig von Benutzerschnittstellen oder Workflows verwendet werden kann. Als .NET-Programmiersprache verwenden wir in diesem Buch ausschließlich C#, auf eine Darstellung der Beispiele in VB.NET wird verzichtet. Das heißt aber auf keinen Fall, dass XAML nur in Verbindung mit C# zu verwenden ist! Falls Sie VB.NET-Entwickler sind, steht Ihnen die Funktionalität der neuen Sprache natürlich ebenfalls vollständig zur Verfügung. In diesem Fall hoffen wir, dass Sie von den in diesem Buch enthaltenen Beispielen ebenfalls profitieren und die Konzepte auf die von Ihnen gewählte Programmiersprache anwenden können. Rainer Stropek, Karin Huber
8
KAPITEL 1 Einleitung
Windows Vista wird gerne als Meilenstein in der Entwicklung von Betriebssystemen aus dem Hause Microsoft bezeichnet. Es werden Vergleiche mit dem Umstieg von Windows 3.x auf Windows 95 gezogen, mit Superlativen wird nicht gespart. Tatsächlich bringt das Betriebssystem viele nützliche Neuerungen für den täglichen Bedarf von typischen Windows-Benutzern. Hinter den Kulissen fand jedoch eine kleine Revolution statt. Diejenigen, die am meisten die Auswirkungen dieser Revolution zu spüren bekommen, sind die Softwareentwickler. Mit Windows Vista erblickte eine Reihe von Komponenten das Licht der Welt, die bisher von Programmierern mühsam selbst zu entwickeln waren oder die man für teures Geld von Drittanbietern zukaufen musste. Im Speziellen sprechen wir von Modulen wie Windows Presentation Foundation (WPF) oder Windows Workflow Foundation (WF). Mit WPF stellt Windows eine von Grund auf neue Komponente zur Umsetzung grafischer Benutzeroberflächen zur Verfügung. WF ist ein Modul zur Gestaltung und Steuerung von Geschäftsprozessen, die einfach und schnell mit eigener Logik verknüpft werden können.
schnell + kompakt
9
1 – Einleitung
Aus Sicht eines Programmierers von Windows-Anwendungen sind WPF und WF nichts anderes als Klassenbibliotheken, die zwar einen enormen Funktionsumfang mit sich bringen, auf den ersten Blick jedoch im Wesentlichen genauso zu verwenden sind, wie andere Klassenbibliotheken auch. Auf den zweiten Blick entdeckt man, dass eine komplett neue Sprache aufgetaucht ist: XAML (XML Application Markup Language). Sie wird sowohl von WPF als auch von WF genutzt. Keine Angst, liebe C#-Programmierer! XAML ist kein Ersatz für C#, es handelt sich um eine Ergänzung. Die Idee, die hinter der Einführung von XAML steckt, ist klar und nicht neu: Trennung von Layout und Logik. In WPF dient XAML zur Kapselung des Aussehens der Benutzerschnittstelle, in WF wird es verwendet, um die Separierung von Geschäftsprozessen und Programmlogik zu ermöglichen. Webentwickler fühlen sich sofort wie zu Hause, schließlich kennen sie dieses Konzept aus ASP.NET schon seit Jahren. Dort hat sich die Trennung von Layout (HTML) und Logik (C#) ohne Zweifel bewährt. Warum ist das so? Sind Sprachen wie C# weniger gut geeignet, um Steuerelemente in einer Windows Form zu platzieren? Hat sich vielleicht herausgestellt, dass Programmierer effizienter beim Schreiben von XML als beim Entwickeln in VB.NET sind? Nein, der Grund für den Erfolg dieses Programmierparadigmas ist nicht in der Technik zu finden. Er ergibt sich aus der immer stärker werdenden Arbeitsteilung bei Softwareentwicklungsprojekten. Lassen Sie uns einen Moment bei WPF bleiben. Windows bietet mittlerweile einen Funktionsumfang für die Entwicklung von Full-Client-Anwendungen, der enorm ist. Die Tage der altbekannten grauen Windows-Formulare sind spätestens seit Vista endgültig vorbei, die Welt wurde auch für normale WindowsAnwendungen wieder bunt. In WPF hat der Entwickler völlige Freiheit, was das Aussehen von Fenstern und Steuerelementen
10
Einleitung
betrifft. Er kann selbst die klassischen Elemente wie Buttons, Textboxen, Listboxen oder Menüs in ihre kleinsten Einzelteile zerlegen, grafisch völlig neu gestalten und wieder zusammensetzen. Obwohl das im ersten Moment bestechend klingt, stellt es Softwareentwicklungsteams vor eine völlig neue Herausforderung: Was tun mit dieser neu gewonnenen Freiheit? Teammitglieder mit Design- und Grafikkenntnissen sind gefragt! Die Gestaltung von Benutzeroberflächen wird viel mehr zu einem kreativen Prozess. In der Praxis zeigt sich jedoch, dass nicht in jedem Programmierer ein begnadeter Designer steckt. Umkehrt gilt auch, dass viele GUI-Designer oft nicht die besten Softwareentwickler sind. Es muss also ein Weg zur effizienten Aufgabenteilung gefunden werden. An dieser Stelle kommt das oben erwähnte Konzept der Trennung von Layout und Code ins Spiel. Lassen Sie uns dieses Prinzip der Trennung an einem Beispiel erklären. Zum Anzeigen eines Fensters mit einem Button sind in WPF nur wenige Zeilen C#-Code notwendig: using System; using System.Windows; using System.Windows.Controls; namespace Csharp_Xaml_Vergleich { class CsharpWindow : Window { private Button button1; public CsharpWindow() { Width = 300; Height = 130; Title = "Csharp_Xaml_Vergleich";
schnell + kompakt
11
1 – Einleitung
button1 = new Button(); button1.Margin = new Thickness(5,5,5,5); button1.Content = "Say Hello..."; button1.Click += OnButton1_Click; Content = button1; } public void OnButton1_Click( Object sender, RoutedEventArgs e) { MessageBox.Show("Hello!"); } } }
Diese C#-Datei ist ein klassisches Beispiel für die Vermischung von Layout und Logik. Durch Nutzung von XAML lassen sich diese beiden Aspekte des Programms gut trennen. Hier erst der Layoutteil unseres Beispiels in XAML: Say Hello...
Der Logikteil bleibt wie im vorigen Beispiel in der C#-Datei:
12
Einleitung
using System; using System.Windows; namespace Csharp_Xaml_Vergleich { public partial class XamlWindow : Window { public XamlWindow() { InitializeComponent(); } public void OnButton1_Click( Object sender, RoutedEventArgs e) { MessageBox.Show("Hello!"); } } }
Abbildung 1.1 stellt den Zusammenhang von XAML und C# grafisch dar:
Abb. 1.1: Zusammenhang XAML und C#
schnell + kompakt
13
1 – Einleitung
XAML wurde also insbesondere für Designer und die von ihnen verwendeten Werkzeuge entwickelt. Ist dieses Buch also ein Buch für Designer? Nur zum Teil. Technikorientierte Designer werden – wie das auch im Web mit HTML der Fall ist – sicherlich die Finger nicht von XAML lassen können. Schließlich ist die Sprache einfach zu lesen und da XAML auf XML basiert, ist eine unüberschaubare Vielzahl von Werkzeugen am Markt verfügbar, mit denen XAML-Quellcode bearbeitet werden kann. Der überwiegende Teil der GUI-Designer wird jedoch mit grafischen Werkzeugen arbeiten. Insbesondere für diese Werkzeuge wurde XAML geschaffen. Ein Grafiker entwirft die Benutzeroberfläche in einem Tool, das die Spezifikation der Benutzeroberfläche in Form von XAML-Code serialisiert. Der Programmierer auf der anderen Seite nutzt XAML zur Laufzeit, um den notwendigen Baum von Objekten im Speicher anzulegen (siehe dazu auch Abbildung 1.2), verknüpft ihn mit der von ihm entwickelten Programmlogik und fertig ist die Windows-Anwendung. Beide Benutzergruppen, Designer und Programmierer, können mehr oder weniger unabhängig (das bedeutet nicht unkoordiniert!) an ihrer Aufgabe arbeiten und die Ergebnisse anschließend wieder zusammenführen.
Abb. 1.2: Deserialisierung von XAML
14
Einleitung
Es stellt sich die Frage, warum mit XAML eine neue Sprache geschaffen werden musste. Der Windows Forms Designer von .NET 2.0 kam schließlich auch ohne aus, er erzeugte direkt, ohne Umwege C#- oder VB.NET-Code. Der Grund für XAML als Serialisierungs- und Deserialisierungsstandard ist die Einfachheit des Umgangs mit XML-basierenden Sprachen in Tools. Jeder .NET-Entwickler weiß, wie einfach und gut unterstützt das Schreiben und Lesen von XML-Dateien ist. Dazu kommt, dass XML ein plattformunabhängiger Standard ist. Reduziert man XAML auf das absolute Minimum, kann man es als XML-basierendes Serialisierungswerkzeug bezeichnen, das gegenüber den schon früher in .NET existierenden, XML-basierten Pendants um einige nützliche Neuerungen ergänzt wurde. Wir haben jetzt viel über die Anwendung von XAML in Verbindung mit Benutzerschnittstellen berichtet und damit den Sinn der neuen Sprache erklärt. Es ist jedoch nicht so, dass XAML ausschließlich für Benutzerschnittstellen verwendet wird. Oben wurde schon erwähnt, dass auch WF XAML einsetzt. Der Grund ist, dass sich auch im Bereich der Geschäftsprozesssteuerung gezeigt hat, dass eine Arbeitsteilung Sinn macht: Geschäftsprozessspezialisten gestalten die Prozesse mit entsprechenden grafischen Werkzeugen, die Prozessdefinition wird mithilfe von XAML als XML-basierte Datei gespeichert und anschließend mit der von Programmierern geschriebenen Programmlogik „gemischt“. Auch wenn im Moment XAML primär in Verbindung mit den Microsoft-Bibliotheken WPF und WF in Zusammenhang gebracht wird, besteht eine gute Chance, dass sich in der Zukunft noch andere Bereiche in der Softwareentwicklung finden, bei denen Spezialisten mit der Unterstützung spezifischer Tools gewisse Aspekte eines Softwaresystems modellieren. XAML kann in
schnell + kompakt
15
1 – Einleitung
all diesen Fällen als Schnittstelle zwischen diesen Werkzeugen und Programmlogik dienen. Durch die breite Anwendungsmöglichkeit von XAML ergeben sich Fragen, die Sie sich im Lauf des Lesens dieses Buchs auf jeden Fall stellen werden: Wenn XAML ein so allgemeines Konzept ist, warum ist dann der XAML-spezifische Namespace Markup im WPF Namespace System.Windows zu finden? Warum enthält eine Projektdatei einer Konsolenanwendung, die XML benutzt, Verweise auf WPF-spezifische Elemente? Die Gründe für diese Widersprüche liegen in der Vergangenheit von XAML. Die Sprache wurde im Zuge der Entwicklung von WPF erfunden und daher von Microsoft auch in diesem Bereich des .NET Framework implementiert. Eine klare Trennlinie zwischen XAML und WPF lässt sich im Augenblick nicht ziehen. Es bleibt abzuwarten ob Microsoft in der Zukunft daran etwas ändern wird. Im Moment muss man sich mit einer gewissen Trennunschärfe abfinden.
16
KAPITEL 2 Hinter den Kulissen 2.1 2.2 2.3 2.4 2.5 2.6 2.7
Einleitung Voraussetzungen Basiskonzepte von XAML „Hello World“ in XAML Kompilieren von XAML-Dateien Application Definition Files Zusammenfassung
17 18 19 20 24 42 45
2.1 Einleitung Bevor wir uns ansehen, was uns die Sprache XAML an Möglichkeiten bietet, sollten wir einen Blick hinter die Kulissen werfen. Während der normalen Arbeit findet man viel zu selten die Gelegenheit, hinter die „Zauberei“ von Visual Studio zu sehen. Schließlich werden die meisten Entwickler auf Produktivität getrimmt und nicht dafür bezahlt, die Konzepte hinter ihrer IDE zu erforschen. Spätestens wenn man den standardmäßig von Visual Studio vergebenen Klassennamen Window1 für das Startfenster seiner Applikation ändern möchte, bereut man diese Herangehensweise. Ein tiefergehendes Verständnis der grundlegenden Ideen ist in genau diesen Fällen gefragt. In diesem Kapitel tauchen wir in die dunklen Tiefen von XAML ab. Unser Ziel ist es, anhand des einfachsten möglichen XAMLProgramms die Elemente zu verstehen, aus denen es besteht, sowie den Build-Prozess zu analysieren.
schnell + kompakt
17
2 – Hinter den Kulissen
2.2 Voraussetzungen Ein großer Teil dieses Buchs besteht aus Beispielen. Wenn Sie diese Beispiele nachvollziehen oder selbst zu experimentieren beginnen wollen, brauchen Sie als Erstes die Laufzeitkomponenten des .NET Framework 3.0. Falls Sie schon mit Windows Vista arbeiten, braucht Sie diese Voraussetzung nicht zu kümmern, da Vista die neue Version des .NET Framework von Haus aus mitbringt. Wie der Name schon sagt, sind die Laufzeitkomponenten zur Laufzeit notwendig. Es reicht also nicht, wenn sie auf den Entwicklungsrechnern installiert sind. Sie müssen auf jedem Computer, auf dem die erstellte Software ausgeführt werden soll, installiert sein. Die zweite Komponente brauchen Sie zum Entwickeln von Software mit XAML: der Microsoft Windows SDK. Er enthält Beispiele, Bibliotheken, Tools u.v.m. Es reicht, ihn auf Entwicklungsarbeitsplätzen zu installieren. Zum Ausführen von Programmen, die auf Basis von .NET 3.0 entwickelt wurden, ist er nicht notwendig. Last but not least müssen Sie Ihr Visual Studio um die Erweiterungen für WPF und/oder WF ergänzen. Diese Komponente ist für WPF nicht in einer finalen Version erhältlich. Microsoft stellt ausschließlich CTPs (Community Technology Previews) zur Verfügung. Eine endgültige Unterstützung von WPF in Visual Studio wird erst mit der nächsten Version dieses Produkts verfügbar sein (Codename Orcas). In unseren Beispielen setzen wir die Erweiterungen für WPF voraus. Falls Sie auch die Beispiele aus Kapitel 5 nachvollziehen möchten, sind noch die Erweiterungen für WF erforderlich. Die genannten Komponenten können Sie von der MicrosoftWebsite kostenlos herunterladen. Alle Details zu Systemvoraussetzungen und Tools rund um XAML, WPF und WF haben wir für Sie in Kapitel 6 zusammengefasst.
18
Basiskonzepte von XAML
2.3 Basiskonzepte von XAML Als Entwickler oder Architekt stößt man zwangsläufig auf XAML, wenn man sich mit der Programmierung von Benutzeroberflächen mit Windows Presentation Foundation (WPF) zu beschäftigen beginnt. Wirft man einen Blick auf Artikel und Bücher zu diesem Thema, so fällt auf, dass sich so gut wie alle diese Publikationen unter anderem auch mit XAML befassen. Im Gegensatz zur landläufigen Meinung ist XAML jedoch nicht ein Konzept, das untrennbar mit WPF verknüpft ist. Es ist wahr, dass XAML ursprünglich im Rahmen der Entwicklung von „Avalon“ – das war der Codename für WPF – entstanden ist. Im Lauf der Zeit wurde jedoch klar, dass XAML ein viel allgemeineres Konzept darstellt: XAML wird genutzt, um einen Graphen von Objekten zu spezifizieren, der dann in einem .NET-Programm für verschiedenste Zwecke verwendet werden kann. Bei WPF beispielsweise legt der Objektgraph die Benutzerschnittstelle fest, bei Windows Workflow Foundation (WF) wiederum werden Geschäftsprozesse mit XAML definiert. XAML ist im Kern also nichts anderes als ein Serialisierungsmechanismus für Objektbäume. Jedes XAML-Element hat eine Entsprechung im Klassenmodell einer Anwendung. Eine Komponente, die als XamlReader bezeichnet wird, liest XAML-Code und instanziiert anhand von gewissen Regeln, auf die wir etwas später noch im Detail eingehen werden, Objekte der entsprechenden Klassen. Der Code, der hinter diesen Klassen steckt, ist für das Verhalten der Anwendung zur Laufzeit verantwortlich. Lesern, die mit der Entwicklung von ASP.NET-Anwendungen vertraut sind, wird dieses Konzept bekannt vorkommen. Auch dort findet man – zumindest in gut geschriebenen ASP.NETProgrammen – eine Trennung in Benutzerschnittstellendefinition (= Objektgraph, der beim Laden einer ASP.NET-Seite im Speicher aufgebaut wird) und Logik (= Code-behind-Dateien). Die
schnell + kompakt
19
2 – Hinter den Kulissen
Grundidee ist bei XAML die gleiche. Die Sprache XAML muss jedoch allgemeiner gesehen werden, sie ist nicht auf die Spezifikation von Benutzerschnittstellen beschränkt.
2.4 „Hello World“ in XAML Wie schon in .NET ist die einfachste Form eines Programms, das XAML verwendet, eine Konsolenapplikation. Unser Ziel ist es, die klassische „Hello World“-Applikation unter Verwendung von XAML zu schreiben. Wie bereits erwähnt, dient XAML zur Erstellung eines Objektgraphen. Jedes XAML-Objekt muss dabei eine Entsprechung in einer .NET-Klasse haben. Daher ist der erste Schritt unseres Einsteigerbeispiels die Entwicklung der Klasse TextContainer, die den auszugebenden Text „Hello World“ aufnehmen kann: namespace Samples { public class TextContainer { private string text; public string Text { set { text = value; } get { return text; } } } [...] };
Ohne XAML würde die oben dargestellte Klasse verwendet, indem mit new ein Objekt erstellt wird. Mit XAML können wir jedoch diesen Schritt vom C#-Code trennen. Wir erstellen eine
20
„Hello World“ in XAML
XAML-Datei, die TextContainer verwendet und die Eigenschaft Text auf „Hello World“ setzt:
Zerlegen wir diese XAML-Datei in ihre Bestandteile:
Wie Sie im oben dargestellten Codeabschnitt sehen, verwendet die XAML-Datei als Wurzelelement y:TextContainer, also die zuvor entwickelte Klasse. Das y: vor dem Klassennamen verweist auf den zugehörigen XML Namespace. Er enthält einen Verweis auf den CLR-Namensraum, in dem die Klasse zu finden ist. Sie werden in diesem Buch in Kapitel 3 noch mehr Details zum Thema Namespaces in XAML erfahren.
Der Eintrag x:Class ist für den XAML Compiler von besonderer Bedeutung. Wie wir in wenigen Momenten sehen werden, erzeugt er im Hintergrund C#-Code und der Eintrag x:Class legt den Namen der Klasse fest, die der Compiler erzeugen wird. Der
schnell + kompakt
21
2 – Hinter den Kulissen
Typ des Wurzelelements ist die Basisklasse für die generierte Klasse. Abbildung 2.1 stellt diesen Zusammenhang grafisch als UML-Klassendiagramm dar.
Abb. 2.1: Zusammenhang der Klassen aus der XAML-Datei
Die generierte Klasse ist partiell. Das ermöglicht es dem Entwickler, die Klasse um weitere Methoden und Eigenschaften in eigenen Dateien zu erweitern, ohne dass der Codegenerator des XAML Compiler sie verändert oder überschreibt. Solche Dateien werden als Code-behind-Dateien bezeichnet. Diese Bezeichnung soll verdeutlichen, dass in den eigenen Codedateien die Logik hinter der durch XAML definierten Struktur (z.B. Bildschirmmasken, Workflows etc.) festgelegt wird.
Der Namespace x verweist auf den Standard-Namespace von XAML http://schemas.microsoft.com/winfx/2006/xaml. Beachten Sie, dass dieser Namespace weder von WPF noch von WF oder anderen konkreten Implementierungen von XAML abhängt. Er ist Teil der Basis-XAML-Sprachspezifikation.
22
„Hello World“ in XAML
Zu guter Letzt wird der gewünschte Ausgabetext der Eigenschaft Text zugewiesen. Sie wurde zuvor in der oben gezeigten C#-Datei definiert. Ergänzen wir den C#-Code unserer Anwendung noch um die für jede Konsolenanwendung verpflichtende Main-Methode, ist unser erstes XAML-Beispiel fertig: namespace Samples { public class TextContainer [...] public static class Startup { private static HelloWorldText text = new HelloWorldText(); public static void Main() { text.InitializeComponent(); System.Console.WriteLine(text.Text); } } };
Bei der Analyse dieses Quellcodes fällt auf, dass hier eine Klasse HelloWorldText verwendet wird, die jedoch nirgends im C#-Code definiert ist. Des Rätsels Lösung werden wir finden, sobald wir
schnell + kompakt
23
2 – Hinter den Kulissen
im nächsten Schritt unser Beispiel übersetzen und uns ansehen, was der XAML-Compiler an Code für uns produziert.
2.5 Kompilieren von XAML-Dateien Viele Entwickler stellen die Frage, ob XAML interpretiert oder ob – ähnlich wie beim Windows Forms Designer – im Hintergrund Programmcode erzeugt wird. Die Antwort lautet: weder noch. Der XAML-Code selbst wird zur Laufzeit nicht interpretiert (obwohl das auch möglich ist; Details dazu siehe „XAML zur Laufzeit behandeln“ in diesem Kapitel). Es wird jedoch XAML auch nur bedingt in Programmcode umgewandelt. Stattdessen wird XAML in eine schnell zu ladende, binäre Form namens BAML umgewandelt. Der generierte Code ist nur dafür zuständig, die entsprechende Methode zum Laden von BAML aufzurufen. In diesem Kapitel werden wir den Prozess des Kompilierens und Ladens von XAML genauer durchleuchten. Der Prozess des Kompilierens unterscheidet sich je nach Anwendungsgebiet von XAML. Windows Workflow Foundation (WF) kann beispielsweise ebenfalls XAML als Spezifikationssprache für Workflows verwenden. In WF sieht der Kompilierprozess für solche Dateien jedoch grundlegend anders aus als in WPF. In diesem Kapitel beschränken wir uns für den Moment auf die Beschreibung des Übersetzungsvorgangs in der „Geburtsstätte“ von XAML, also in WPF. Auf die Besonderheiten von XAML in Verbindung mit WF gehen wir später ein.
XAML und MSBuild Zum Kompilieren von Projekten, die XAML-Dateien enthalten, muss man auf MSBuild zurückgreifen. Es steht kein anderes Kommandozeilen-Werkzeug zur Verfügung, das auf die Übersetzung von XAML spezialisiert wäre. MSBuild nutzt die Klas-
24
Kompilieren von XAML-Dateien
sen des Assembly presentationbuildtasks.dll (Namespace Microsoft.Build.Tasks.Windows) zum Übersetzen von XAML-Dateien. In der Praxis ist die Beschränkung auf MSBuild jedoch kein Nachteil, da dieses Werkzeug ideal geeignet ist, um mit den komplexen Projektstrukturen, die typischerweise hinter WPF-Projekten stecken, umzugehen. Dazu kommt, dass es nur wenige Entwickler gibt, für die die Notwendigkeit besteht, MSBuildProjektdateien selbst zu schreiben, da Visual Studio 2005 mit den Erweiterungen für WPF diese Arbeit vollständig übernimmt. Unser Kapitel heißt jedoch „Hinter den Kulissen“ und daher wollen wir wissen, wie die Projektdatei für das „Hello World“Beispiel aufgebaut ist. An dieser Stelle gehen wir nicht auf alle Feinheiten von MSBuild ein, sondern erklären nur die Besonderheiten, die MSBuild für den Umgang mit XAML-Dateien bereithält. Beim Anlegen eines Projekts im Visual Studio wird eine Projektdatei mit der Endung .csproj erstellt. In dieser sind alle Informationen enthalten, die wir zum Kompilieren benötigen: HelloWorld Exe
schnell + kompakt
25
2 – Hinter den Kulissen
Die erste Besonderheit der Projektdatei sind die Verweise auf die Referenzen WindowsBase, PresentationCore und PresentationFramework. Ein paar Seiten zuvor wurde hervorgehoben, dass XAML kein Konzept ist, das untrennbar mit WPF verbunden ist, und jetzt sind schon im einfachsten Beispiel Verweise auf WPF enthalten? Der Grund ist, dass XAML als Konzept zwar eigenständig ist, zur Verwendung jedoch immer eine konkrete Implementierung vorausgesetzt wird. In unserem Fall greifen wir auf die von WPF zurück und daher verlangt MSBuild als Voraussetzung für die konkrete Kompilierung des Programms diese Referenzen. Der zweite Punkt, der hervorgehoben werden soll, sind die WPFspezifischen Build Items. Tabelle 1.1 zeigt eine komplette Übersicht. In unserem Fall wird nur das Item Page verwendet, um die XAML-Datei zu übersetzen. Schlussendlich ist es noch erforderlich, das MSBuild-Projekt "$(MSBuildBinPath)\Microsoft.WinFX.targets zu importieren. Es
enthält die Definition der notwendigen Schritte zum Kompilieren von WPF-Anwendungen.
26
Kompilieren von XAML-Dateien Tabelle 2.1: WPF-spezifische Build Items
Build Item
Beschreibung
ApplicationDefinition
Dieses Build Item verweist auf die XAML-Datei, in der anwendungsweise Definitionen enthalten sind. In WPF ist das typischerweise die Datei App.xaml.
Page
Mit diesem Build Item werden alle XAMLDateien angegeben, die kompiliert und dem Assembly hinzugefügt werden sollen.
Resource
Mit Resource können Dateien dem Assembly hinzugefügt werden, auf die aus dem Programm über Pack URIs zugegriffen werden kann. Resource-Dateien lassen sich lokalisieren (UICulture-Eigenschaft).
Content
Dieses Build Item dient dazu, zusätzliche Dateien zu spezifizieren, die gemeinsam mit dem Assembly verteilt werden und auf die über Pack URIs zugegriffen werden kann. Die Dateien werden nicht dem Assembly hinzugefügt, lediglich die entsprechenden Metadaten.
Die Build-Ergebnisse Beim Kompilieren einer Anwendung werden im obj-Verzeichnis je XAML-Datei eine Reihe von Hilfsdateien generiert. Zwei davon sind von besonderem Interesse: die BAML- und die generierte C#-Datei. Beide tragen vor der Dateierweiterung den Namen der XAML-Datei, die BAML-Datei mit der Dateierweiterung .baml, die C#-Datei mit .g.cs. In unserem Fall finden wir also HelloWorld.baml und HelloWorld.g.cs.
schnell + kompakt
27
2 – Hinter den Kulissen
Abbildung 2.2 stellt den Build-Prozess einer XAML-Datei vereinfacht grafisch darf. Auf die einzelnen Ergebnisse werden wir in Folge genauer eingehen.
Abb. 2.2: Build-Prozess einer XAML-Datei
BAML – Binary XML Die Abkürzung BAML steht für „Binary XAML“ – bei dieser Datei handelt es sich also um die binäre Repräsentation der XAMLDatei. Der Grund, warum der Compiler nicht die XAML-Datei selbst verwendet, ist schlicht und einfach die Performance. Das Lesen der binären XAML-Datei kann zur Laufzeit um ein Vielfaches schneller erfolgen als das der Ausgangsdatei im XML-Format. Die BAML-Datei wird dem zu erstellenden Assembly als Ressource hinzugefügt. Dementsprechend findet man ihren Inhalt auch in der .g.resources-Datei wieder. Der Mechanismus, BAMLDateien als Ressourcen den Assemblies hinzuzufügen, ermöglicht auch die Verwendung unterschiedlicher XAML-Dateien abhängig von der zu verwendenden Sprache (Lokalisierung). Wir werden an dieser Stelle jedoch nicht näher auf den Prozess der Lokalisierung von WPF-Ressourcen eingehen.
28
Kompilieren von XAML-Dateien
Abbildung 2.3 (Bildschirmkopie von ILDASM, dem Disassembler von .NET) zeigt, wie die BAML-Datei als Ressource dem Assembly HelloWorld.exe hinzugefügt wurde.
Abb. 2.3: BAML als Ressource im Assembly
Der generierte C#-Code Beachten Sie, dass für jede XAML-Datei im Hintergrund eine Klasse generiert wird. Erinnern Sie sich noch an die XAML-Datei unseres Beispiels? Dort wurde mit x:Code angegeben, dass die Klasse den Namen HelloWorldText haben soll:
Wir finden HelloWorldText in der von MSBuild generierten .g.csDatei. Das Kürzel .g steht dabei für Generated. Werfen wir einen Blick in diese Datei: [...] namespace Samples { [...] public partial class HelloWorldText : Samples.TextContainer, System.Windows.Markup.IComponentConnector
schnell + kompakt
29
2 – Hinter den Kulissen
{ [...] public void InitializeComponent() { [...] } [...] } }
An dieser Stelle soll nicht auf alle Feinheiten des generierten Codes eingegangen werden. Einige Besonderheiten sind jedoch für das Verständnis von XAML wesentlich: 쐌 Die generierte Klasse ist mit dem Schlüsselwort partial versehen. Das bedeutet, dass sie in anderen Dateien noch erweitert werden kann. Diese Tatsache ist wichtig, da bei der konkreten Anwendung von XAML in WPF ähnlich wie bei ASP.NET das Konzept der Code-behind-Dateien verwendet wird. Der Anwender erweitert dabei die vom XAML-Compiler generierte Klasse in eigenen Dateien, indem er dort den gleichen Klassennamen – wiederum mit dem Schlüsselwort partial versehen – verwendet. 쐌 Die erzeugte Klasse leitet von Samples.TextContainer ab. Wenn XAML in Verbindung mit WPF zum Einsatz kommt, führt beispielsweise die Verwendung des XAML-Elements Window dazu, dass eine Nachfahrenklasse der Klasse System.Windows.Window erzeugt wird. 쐌 Die Implementierung des Interface System.Windows.Markup.IComponentConnector ist notwendig, damit Objekte der Klasse Event-Handler hinzugefügt werden können. 쐌 Die Methode InitializeComponent schließlich wird verwendet, um den Objektgraphen aus dem BAML zu laden. XAML bietet grundsätzlich auch die Möglichkeit, Code einzubetten. Für diesen Zweck steht das Element x:Code zur Verfügung. In diesem Element kann Programmcode innerhalb eines
30
Kompilieren von XAML-Dateien
CDATA-Bereichs geschrieben werden, der vom XAML Compiler in die generierte partielle Klasse in der .g.cs-Datei eingebunden wird. In unserem HelloWorld-Beispiel kann in der XAML-Datei zum Beispiel die zusätzliche Eigenschaft TextUpper definiert werden:
Von der Vorgehensweise, den Programmcode in XAML einzubetten, ist unbedingt abzuraten. Wie oben erwähnt, ist der einzige Sinn der partiellen Klasse, dass der aus XAML kommende Objektgraph über eigene Programmdateien um Logik erweitert werden kann. Dadurch soll eine Trennung zwischen Strukturen (d. h. Benutzeroberflächenelemente in WPF, Workflow-Definitionen in WF) und Logik geschaffen werden. Durch Vermischung von Struktur und Code in der XAML-Datei wird diese Trennung zunichte gemacht. Insofern sollte vom x:Code-Element nur Gebrauch gemacht werden, wenn keine andere Möglichkeit in Frage kommt! Eine Besonderheit stellt das x:Name-Attribut dar. Es wird später in diesem Buch noch im Detail erklärt. An dieser Stelle wollen wir
schnell + kompakt
31
2 – Hinter den Kulissen
es jedoch vorab erwähnen, da es eine besondere Rolle in Bezug auf den generierten C#-Code einnimmt. Mit x:Name können Elemente mit einem innerhalb der XAML-Datei eindeutigen Namen versehen werden. Hier ein Code-Beispiel einer WPF-Maske mit drei Eingabefeldern und einer Schaltfläche.
Abb. 2.4: WPF-Maske
Die in den ersten beiden Eingabefeldern eingegebenen Zahlen sollen bei Klick auf die Schaltfläche miteinander multipliziert werden. Das Ergebnis wird im dritten Eingabefeld dargestellt. Die Eingabefelder wurden mit x:Name benannt: *
32
Kompilieren von XAML-Dateien
= Calculate
Der XAML-Compiler generiert pro Element, das mit x:Name benannt wurde, ein Attribut in die erzeugte, partielle Klasse. Außerdem wird die Methode Connect von IComponentConnector implementiert. Sie wird im Zuge des Ladeprozesses pro benanntem Element aufgerufen und ist dafür zuständig, dass die Attribute mit den entsprechenden, durch das Laden der XAML-Datei im Speicher erzeugten Objekten „verdrahtet“ werden. Ihre zweite Aufgabe ist die Zuweisung der entsprechenden Methoden zu den Event-Handlern, die in der XAML-Datei definiert wurden. In unserem Fall soll die Methode OnCalculate_Click aufgerufen werden, wenn ein Benutzer auf die Schaltfläche Calculate klickt. Der folgende Codeausschnitt zeigt den vom XAML-Compiler generierten Code aus der .g.cs-Datei. Die oben beschriebenen Besonderheiten sind hervorgehoben: [...] public partial class ElementNamingForm : System.Windows.Window, System.Windows.Markup.IComponentConnector { internal System.Windows.Controls.TextBox Variable_X;
schnell + kompakt
33
2 – Hinter den Kulissen
internal System.Windows.Controls.TextBox Variable_Y; internal System.Windows.Controls.TextBox Result; internal System.Windows.Controls.Button Calculate; [...] void IComponentConnector.Connect( int connectionId, object target) { switch (connectionId) { case 1: this.Variable_X = ((System.Windows.Controls.TextBox) (target)); return; case 2: this.Variable_Y = ((System.Windows.Controls.TextBox) (target)); return; case 3: this.Result = ((System.Windows.Controls.TextBox) (target)); return; case 4: this.Calculate = ((System.Windows.Controls.Button) (target)); this.Calculate.Click += new System.Windows.RoutedEventHandler( this.OnCalculate_Click);
34
Kompilieren von XAML-Dateien
[...] return; } this._contentLoaded = true; } }
In der Code-behind-Datei kann durch diesen generierten Code bequem auf die einzelnen benannten Steuerelemente zugegriffen werden: [...] public partial class ElementNamingForm : System.Windows.Window { public ElementNamingForm() { InitializeComponent(); } private void OnCalculate_Click( Object sender, EventArgs e) { Result.Text = ( Convert.ToInt32(Variable_X.Text) * Convert.ToInt32(Variable_Y.Text) ).ToString(); } }
schnell + kompakt
35
2 – Hinter den Kulissen
XAML zur Laufzeit behandeln Die Alternative zum Einbinden der XAML-Datei zur Kompilierzeit wäre das Laden zur Laufzeit. Dieses Vorgehen sollte jedoch nur gewählt werden, wenn es triftige Gründe dafür gibt, da die Zeit zum Laden der XAML-Datei wegen der fehlenden binären Version deutlich länger ist. Nichtsdestotrotz gibt es Situationen, in denen das XAML zur Kompilierzeit nicht vorliegt. Für diese Fälle gibt es die Klasse System.Windows.Markup.XamlReader. Unser zuvor gezeigtes HelloWorld-Beispiel lässt sich mit ganz wenigen Änderungen so umstellen, dass die XAML-Datei erst zur Laufzeit geladen wird:
쐌 Die erste Änderung betrifft die XAML-Datei selbst. Das x:Class-Attribut entfällt, da keine Klasse mehr erzeugt werden soll. Generell dürfen alle XAML-Strukturen, die eine Kompilierung voraussetzen, beim Laden zur Laufzeit nicht in der XAML-Datei vorkommen (d.h. auch zum Beispiel eingebetteter Programmcode), da kein Compiler mehr involviert ist. 쐌 Als Zweites ist darauf zu achten, dass beim Verweis auf den Namespace das Assembly mit angegeben werden muss. Im Programmcode ist die Main-Methode so umzustellen, dass mithilfe der erwähnten XamlReader-Klasse die XAML-Datei gelesen wird:
36
Kompilieren von XAML-Dateien
public static void Main() { FileStream fs = new FileStream( @"c:\temp \HelloWorldRuntime.xaml", FileMode.Open ); TextContainer text = (TextContainer)XamlReader.Load( fs ); System.Console.WriteLine( text.Text ); fs.Close(); }
Die MSBuild-Projektdatei wird beim Laden von XAML zur Laufzeit natürlich einfacher, weil weniger Referenzen notwendig sind: HelloWorldRuntime Exe
schnell + kompakt
37
2 – Hinter den Kulissen
Etwas komplizierter ist das Laden von XAML-Dateien zur Laufzeit, bei denen man auf einzelne, mit x:Name benannte Elemente zugreifen will. Im Kapitel „Der generierte C#-Code“ wurde bereits erklärt, welche Auswirkungen x:Name auf den generierten C#-Code hat. Der XAML-Compiler macht in diesem Fall die ganze Arbeit, indem er pro benanntem Element ein Attribut in die partielle Klasse einfügt. Im eigenen Code kann man damit bequem auf die mit x:Name versehenen Objekte zugreifen. Lädt man die XAML-Dateien jedoch zur Laufzeit, gibt es keinen generierten C#-Code. Man muss die Arbeit, die in der kompilierten Version der XAML-Compiler übernimmt, selbst erledigen. Ein typisches Szenario, wo diese Vorgangsweise sinnvoll wäre, ist eine Anwendung, in der die Gestaltung der WPF-Masken zur Kompilierzeit noch nicht fixiert ist. Es wäre denkbar, dass Anwender das Front-End an ihre Bedürfnisse anpassen können, indem die XAML-Dateien verändert werden. Das Programm müsste somit die WPF-Masken zur Laufzeit laden und die Eingabeelemente mit den programminternen Variablen verbinden. Wir möchten dieses Prinzip demonstrieren, indem wir das letzte Beispiel des Kapitels „Der generierte C#-Code“ so umbauen, dass die WPF-Maske nicht mehr kompiliert, sondern zur Laufzeit geladen wird. Die XAML-Datei unterscheidet sich zwischen der kompilierten Version und der Version zum Laden zur Laufzeit nur wenig: 쐌 Wie schon im HelloWorld-Beispiel muss das x:Class-Attribut entfernt werden, da kein Compiler mehr im Spiel ist, der eine Klasse generieren könnte. 쐌 Die zweite Änderung betrifft die Definition des Buttons. Dort war in der kompilierten Version ein Event-Handler dem Event Click zugeordnet. Das hatte zur Folge, dass der Compiler Code erzeugte, um den Event-Handler dem entsprechenden Objekt zuzuweisen. Beim Laden zur Laufzeit wird kein Code
38
Kompilieren von XAML-Dateien
erzeugt, insofern müssen solche Zuweisungen eines EventHandler aus dem XAML-Code entfernt werden. * = Calculate
Das Laden der XAML-Datei ElementNamingForm.xaml zur Laufzeit erfolgt wie im HelloWorld-Beispiel mithilfe der Klasse System.Windows.Markup.XamlReader. Was hinzukommt, ist jedoch der Code zum „Verdrahten“ der Attribute mit den entsprechenden durch das Laden der XAML-Datei erzeugten Objekten. Dafür steht die Klasse System.Windows.LogicalTreeHelper zur Verfügung. Sie enthält die Methode FindLogicalNode, die einen Baum
schnell + kompakt
39
2 – Hinter den Kulissen
von FrameworkElement- oder FrameworkContentElement-Objekten nach einem Objekt mit einem angegebenen Namen durchsucht. using using using using using
System; System.IO; System.Windows; System.Windows.Controls; System.Windows.Markup;
namespace ElementNaming { class ElementNaming : Application { [STAThread] private static void Main() { ElementNaming app = new ElementNaming(); app.mainForm.Show(); app.Run(); } private private private private private
Window mainForm; TextBox Variable_X; TextBox Variable_Y; TextBox Result; Button Calculate;
public ElementNaming() { FileStream fs = new FileStream( @"[...]\ElementNamingForm.xaml", FileMode.Open); mainForm = (Window)XamlReader.Load(fs); fs.Close(); DoWiring(mainForm); }
40
Kompilieren von XAML-Dateien
private void DoWiring( DependencyObject formContent ) { Variable_X = (TextBox) LogicalTreeHelper.FindLogicalNode( formContent, "Variable_X"); Variable_Y = (TextBox) LogicalTreeHelper.FindLogicalNode( formContent, "Variable_Y"); Result = (TextBox) LogicalTreeHelper.FindLogicalNode( formContent, "Result"); Calculate = (Button) LogicalTreeHelper.FindLogicalNode( formContent, "Calculate"); Calculate.Click += new RoutedEventHandler(OnCalculate_Click); } private void OnCalculate_Click( Object sender, EventArgs e) { Result.Text = ( Convert.ToInt32(Variable_X.Text) * Convert.ToInt32(Variable_Y.Text) ).ToString(); } } }
schnell + kompakt
41
2 – Hinter den Kulissen
2.6 Application Definition Files Unsere bisherigen Beispiele enthielten alle die für Konsolenanwendungen typische Main-Methode. Die WPF-Implementierung von XAML bietet als Ersatz dafür Application Definition Files an. Im Wesentlichen sind diese Dateien ganz normale XAML-Dateien. Das Besondere an ihnen ist, dass als Wurzelelement Application verwendet wird. Diesem XAML-Element entspricht die Klasse System.Windows.Application im .NET Framework. Den Application Definition Files bzw. der Klasse Application kommen folgende Aufgaben zu: 쐌 Implementierung des Einsprungpunkts für den Programmstart (Main-Methode) 쐌 Management der Anwendungsausführung (z.B. Starten, Beenden) 쐌 Management applikationsweit verwendeter, globaler Objekte (z.B. Stile, Ressourcen) 쐌 Verarbeiten von Kommandozeilenparametern 쐌 Anbieten von Funktionen und Events für die Navigation zwischen den Fenstern und Seiten einer WPF-Applikation Application implementiert das Singleton-Pattern. Es ist also sichergestellt, dass pro AppDomain genau eine Instanz dieser Klasse existiert. Das hat den Vorteil, dass auf die von ihr zur Verfügung gestellten Eigenschaften und Methoden von überall innerhalb einer Applikation zugegriffen werden kann. Dafür steht die statische Eigenschaft Current zur Verfügung, die das Application-Objekt der aktuellen AppDomain zurückgibt.
Der folgende XAML-Ausschnitt zeigt, wie das schon bekannte Hello-World-Beispiel mit einem Application Definition File umgesetzt werden könnte:
42
Application Definition Files
In diesem Codebeispiel wird das Application Definition File zum Ablegen einer globalen Ressource, also eines globalen Objekts der bereits bekannten Klasse TextContainer, verwendet (Details zum Verwenden der Resources-Collection finden sich später in Kapitel 4). Für die C#-Code-behind-Klasse bleibt nur die Aufgabe, den Text auszugeben. Sie kann dabei mithilfe der Methode FindResource auf die Ressource aus dem Application Definition File zugreifen: using System.Windows; namespace Samples { public class TextContainer { private string text; public string Text { set { text = value; } get { return text; } }
schnell + kompakt
43
2 – Hinter den Kulissen
public override string ToString() { return Text; } } public partial class SampleApp : Application { override protected void OnStartup( StartupEventArgs e) { System.Console.WriteLine( FindResource("OutputText")); Shutdown(); } } };
Da unser Beispiel eine Konsolenanwendung ist, wird nach dem Ausgeben des Textes „Hello World!“ die Anwendung sofort wieder geschlossen. Bei der Umsetzung von WPF-Applikationen ist das natürlich anders. Dort ist es – zumindest bei einfacheren Programmen – oft der Fall, dass die Code-behind-Datei der Application-Klasse leer ist. Es ist nicht einmal notwendig, Code zu schreiben, der das Hauptfenster einer Anwendung anzeigt. Dafür bietet Application die Eigenschaft StartupUri an, die bereits in der XAML-Datei entsprechend gesetzt werden kann. Beim Programmmstart kümmert sich das Applikationsobjekt automatisch darum, die Maske, auf die StartupUri verweist, anzuzeigen. Generiert man eine WPF-Anwendung mit Visual Studio, wird genau dieser Weg gewählt:
44
Zusammenfassung
Weder WPF- noch Konsolenanwendungen verlangen zwingend die Nutzung von Application Definition Files oder der Klasse Application im Code. In der Praxis raten wir jedoch unbedingt, sie zu verwenden. In Application Definition Files sollten insbesondere zentral definierte Layoutvorschriften in Form von Stilen abgelegt werden, die im Rest der Anwendung durchgängig verwendet werden. Nur dadurch kann man in Programmen, die eine gewisse Größe haben, ein einheitliches Aussehen sicherstellen und den Wartungsaufwand bei Layoutänderungen in Grenzen halten.
2.7 Zusammenfassung 쐌 Obwohl XAML im Zuge der Entwicklung von WPF entstanden ist, handelt es sich um eine allgemein verwendbare Sprache zum Spezifizieren von Objektgraphen. 쐌 XAML-Dateien werden mit MSBuild in ihre binäre Repräsentation BAML übersetzt und als Ressourcen dem Assembly hinzugefügt. 쐌 Pro XAML-Datei wird eine partielle Klasse generiert. Sie enthält unter anderem den Code zum Erzeugen des Objektgraphen aus BAML. 쐌 XAML kann auch zur Laufzeit interpretiert werden. Davon ist jedoch aus Laufzeitgründen abzuraten! Dieser Weg sollte nur
schnell + kompakt
45
2 – Hinter den Kulissen
beschritten werden, wenn es gute Gründe dafür gibt, dass das XAML nicht zur Kompilierzeit in das Assembly eingebunden werden kann. 쐌 In Application Definition Files sollten zentrale Ressourcen wie Stile abgelegt werden. Auf sie kann von überall in der Anwendung zugegriffen werden.
46
KAPITEL 3 XAML Namespaces 3.1
Einleitung
47
3.2
Namespaces in XAML-Dateien
53
3.3
Elemente im Namespace x:
55
3.4
XML-Attribute in XAML
67
3.5
Zusammenfassung
69
3.1 Einleitung Was ist der Kern von XAML? Rein technisch gesehen handelt es sich um ein Werkzeug zur Serialisierung und Deserialisierung von Objektstrukturen. Vom Standpunkt der Anwendungsbereiche gesehen dient die Sprache zum Definieren von Benutzerschnittstellen oder Workflows. Da es dabei im Endeffekt aber ebenfalls nur um das Anlegen von Objekten im Speicher geht, widerspricht sich das nicht. Die Sprache XAML hat keine spezifische Klassenbibliothek „eingebaut“. Sie kann ihre Aufgabe mit jeder beliebigen .NET-Klasse erfüllen und beherrscht nicht nur den Umgang mit gewissen Klassen. Aus diesem Grund ist ein Mechanismus notwendig, der XAML mitteilt, wo die Klassen zu finden sind, auf die in einer XAML-Datei verwiesen wird. Konsequenterweise hat Microsoft dafür nicht ein neues Konzept erfunden, sondern auf Altbekanntes und Bewährtes aus XML zurückgegriffen: Namespaces und Namespace Mappings. Namespace Mappings stehen nicht nur sprichwörtlich am Anfang von XAML, sie sind auch ganz am Beginn der XAML-Datei,
schnell + kompakt
47
3 – XAML Namespaces
also im Wurzelelement, anzugeben. Zwei Typen von Namespace Mappings sind zu unterscheiden: 쐌 CLR (Common Language Runtime) Namespace Mappings 쐌 XML Namespace Mappings CLR und XML Namespace Mappings stellen die erwähnte Verbindung zwischen XAML und den .NET-Klassen dar. Ein CLR Mapping ist ein Verweis auf einen CLR Namespace, der alle Klassen dieses Namespace in der XAML-Datei verfügbar macht. XML Namespaces sind eine Zusammenfassung mehrerer CLR Namespaces. Der erste Teil dieses Kapitels widmet sich der Erklärung dieser beiden Strukturen. Im zweiten Teil des Kapitels gehen wir auf den XAML Standard Namespace ein. Für ihn wird typischerweise die Abkürzung x: verwendet. Er beinhaltet alle Klassen und Compiler-Anweisungen, die XAML ausmachen. Bevor wir darauf eingehen, wie wir CLR und XML Namespaces in XAML-Dateien verwenden können, sehen wir uns zuerst an, wie diese in .NET überhaupt zu definieren sind.
CLR Namespace CLR (Common Language Runtime) Namespaces sind Ihnen wahrscheinlich bekannt. Sie werden in .NET verwendet, um mehrere Klassen zu größeren Einheiten zusammenzufassen. CLR Namespaces werden im C#-Quellcode mit dem Schlüsselwort namespace definiert und mithilfe von using verwendet: using System; using System.Text; namespace Samples { class MyClass1
48
Einleitung
{ [...] }; class MyClass2 { [...] }; }
Verwendet man einen CLR Namespace in einer XAML-Datei, spricht man von einem CLR Namespace Mapping. Enthält eine XAML-Datei ein CLR Namespace Mapping auf einen bestimmten CLR Namespace, dann können alle Klassen dieses Namespace in der XAML-Datei verwendet werden. Um einen CLR Namespace zu inkludieren, muss man im xmlns:Attribut die Namen des Assembly und des Namespace angeben. Befindet sich der Namespace im selben Assembly wie die Applikation, kann der Name des Assembly weggelassen werden. xmlns:[prefix]="clr-namespace:Namespace Name;[assembly=Assembly Name]"
Im folgenden Codeausschnitt möchten wir zeigen, wie eine selbst geschriebene .NET-Klasse in einer XAML-Datei verwendet werden kann, indem ein CLR Namespace Mapping eingefügt wird:
schnell + kompakt
49
3 – XAML Namespaces
Im Beispiel wird der Namespace CustomClasses.Listbox aus dem Assembly CustomClasses eingebunden. Als Präfix wird txt: angegeben. Dieses Präfix gibt dem Namespace Mapping einen eindeutigen Namen. Bei jeder Verwendung einer Klasse aus diesem Namespace muss auch das Präfix mit angegeben werden. Der angegebene Namespace enthält die Klasse ListBoxText, welche im XAML verwendet wird, um für eine ListBox Listbox-Elemente zu erstellen. Sie stellt die Eigenschaften Text und Repeat zur Verfügung. Die ToString-Methode liefert den angegebenen Text und wiederholt ihn mit Komma getrennt Repeat mal. namespace CustomClasses.ListBox { public class ListBoxText { private int repeat = 1; private string text = ""; public int Repeat {get{...} set{...}} public string Text {get{...} set{...}} public override string ToString() { StringBuilder buttonText = new StringBuilder(); for (int i = 0; i < this.Repeat; i++) { if (i > 0) { buttonText.Append(", ");
50
Einleitung
} buttonText.Append(this.Text); } return buttonText.ToString(); } } }
Für jedes Objekt in der ListBox wird die Methode ToString aufgerufen und der zurückgegebene Text wird in der Liste angezeigt:
Abb. 3.1: ListBox mit eigenen Objekten
XML Namespace XML Namespaces können mehrere CLR Namespaces zu noch größeren Einheiten gruppieren. In Abbildung 3.2 werden zwei Assemblys dargestellt. Jedes beinhaltet drei CLR Namespaces. Die XML Namespaces 1 und 2 fassen mehrere CRL Namespaces aus den beiden Assemblys zusammen.
Abb. 3.2: Assemblys, CLR und XML Namepaces
schnell + kompakt
51
3 – XAML Namespaces
Der Vorteil dieser Variante besteht darin, dass CLR Namespaces, die üblicherweise zusammen verwendet werden, unter einem einzigen Namen ansprechbar sind und nicht alle CLR Namespaces einzeln eingebunden werden müssen. Die Definition der XML Namespaces erfolgt auf Assembly-Ebene mit dem Attribut XmlnsDefinitionAttribute und wird üblicherweise im File AssemblyInfo.cs angegeben. Um die XML Namespaces aus Abbildung 3.2 zu definieren, sind in der Datei AssemblyInfo.cs von Assembly 1 folgende Angaben erforderlich: [assembly: XmlnsDefinition("http://schemas.karin.com/ namespace1", "a")] [assembly: XmlnsDefinition("http://schemas.karin.com/ namespace1", "a.x")] [assembly: XmlnsDefinition("http://schemas.karin.com/ namespace2", "b")]
In der Datei AssemblyInfo.cs von Assembly 2 müssen folgende XmlnsDefinition-Attribute angegeben werden: [assembly: XmlnsDefinition("http://schemas.karin.com/ namespace1", "c")] [assembly: XmlnsDefinition("http://schemas.karin.com/ namespace2", "c")]
In der Dokumentation zum .NET Framework findet man in der Übersicht zu jeder Klasse zusätzlich zu Namespace und Assembly auch den XML Namespace, falls vorhanden. WPF Controls wie Button oder ListBox wurden zum Beispiel im XML
52
Namespaces in XAML-Dateien
Namespace http://schemas.microsoft.com/winfx/2006/xaml/pre sentation zusammengefasst. Üblicherweise werden als Namen für XML Namespaces URIs verwendet. Dies ist aber nicht zwingend nötig. Voraussetzung ist nur, dass der angegebene Name eindeutig ist. Wir können den definierten XML Namespace nun in eine XAMLDatei einbinden, indem wir dessen Namen im Attribut xmlns angeben. Auch hier ist ein Präfix erforderlich, um den Namespace in der XAML-Datei eindeutig zu identifizieren: xmlns:[prefix]="XML Namespace Name"
Das folgende Codebeispiel zeigt die Verwendung der XML Namespaces aus dem oben gezeigten Beispiel. Wie man sieht, ist die Referenzierung in der XAML-Datei wesentlich einfacher und kürzer, als wenn man alle CLR Namespaces einzeln referenzieren würde.
3.2 Namespaces in XAML-Dateien Wenn man in Visual Studio in einer WFP-Applikation ein neues Benutzerschnittstellenelement wie Window, Page etc. anlegt, werden dem Wurzelelement zwei XML Namespace Mappings automatisch hinzugefügt:
Das Mapping xmlns verweist auf den XML Namespace für WPF. Alle Klassen in diesem Namespace können direkt ohne Präfix im XAML verwendet werden (z.B. ). Dem Namespace Mapping wird kein Präfix zugewiesen, da die WPF-Klassen am häufigsten in der Datei verwendet werden und nur ein einziger Namespace ohne Präfix angegeben werden darf. Alle weiteren Namespace Mappings müssen ein innerhalb der XAML-Datei eindeutiges Präfix erhalten. Analog dazu bindet die Workflow Foundation Folgendes in das Wurzelelement des XML Namespace für WF ein:
Das Mapping xmlns:x ist in beiden Varianten vorhanden. Es verweist auf den XML Namespace für XAML. Objekte in diesem Namespace müssen mit dem Präfix „x:“ verwendet werden (z.B. das Element , das Attribut ). Es wurde bereits betont, dass XAML nicht auf WPF oder WF beschränkt ist. Man kann weitere Mappings auf eigene CLR oder XML Namespaces angeben. Ein Anwendungsbeispiel dafür wären eigene WPF Control Libraries.
54
Elemente im Namespace x:
3.3 Elemente im Namespace x: Ein XML Namespace spielt in Zusammenhang mit XAML eine besondere Rolle: http://schemas.microsoft.com /winfx/2006/ xaml. Für ihn wird typischerweise das Präfix x: verwendet. Es wäre zwar theoretisch möglich, von dieser Vorgabe abzuweichen; wir raten aber unbedingt davon ab, da alle Tools zum Bearbeiten von XAML von dieser Regel ausgehen. Im Gegensatz zu allen anderen XML-Namespaces, die mit XAML verwendet werden können, dient x: nicht nur zum Referenzieren von Klassen aus .NET Assemblys. Es gibt zwar solche Elemente, beim überwiegenden Teil handelt es sich jedoch um Anweisungen, die der XAML Loader und der XAML Compiler beachten. Sie steuern einerseits die Eigenschaften des generierten Codes bzw. der generierten Assemblys und beeinflussen andererseits den Ladeprozess der XAML-Dateien. Auf den nächsten Seiten werden wir die von x: angebotenen Funktionen im Detail erklären und anhand von Beispielen demonstrieren.
x:Array ...
Mit x:Array können Felder in XAML definiert werden. Das Attribut Type gibt den Typ der Array-Elemente an. Um ein Feld von Strings anzulegen, muss im Wurzelelement der XAML-Datei der Namespace System inkludiert werden, da dort die Klasse String enthalten ist. Im Feld kann als Typ dann sys:String angegeben
schnell + kompakt
55
3 – XAML Namespaces
werden. Das Feld kann zum Beispiel als Datenquelle für eine ListBox dienen. Im folgenden Beispiel wird in der Page ein Array als Ressource definiert. In der ListBox wird diese Ressource dann als Datenquelle verwendet. Array Sample
Abb. 3.3: x:Array als Datenquelle einer ListBox
Das in XAML definierte Feld wird vom XAML Loader in ein normales C#-Array umgewandelt. Man kann dieses Verhalten im Debugger von Visual Studio beobachten:
Abb. 3.4: x:Array im Debugger
56
Elemente im Namespace x:
Am Beispiel fällt wahrscheinlich die besondere Schreibweise des Attributwerts für ItemsSource auf. Dort wird auf ein Objekt aus den Ressourcen der WPF Page verwiesen. Man bezeichnet einen Ausdruck, der in geschwungenen Klammern enthalten ist (Attribut=“{Ausdruck}“), als Markup Extension. Dieses Konstrukt wird im Detail in Kapitel 4 erklärt.
x:Class, x:ClassModifier, x:Subclass, x:TypeArguments ... x:Class wird im Wurzelknoten einer XAML-Datei angegeben, um den Namen der vom XAML Compiler generierten Klasse zu spezifizieren. Diese Klasse kann optional auch mit einer Codebehind-Datei verknüpft werden. Der Mechanismus, der dabei zur Anwendung kommt, wurde bereits in Kapitel 2 an einem Beispiel erklärt.
Wenn die Klasse im Code nicht als public deklariert werden soll, dann muss im Objekt in XAML die Sichtbarkeit mit x:ClassModifier explizit angegeben werden. Beachten Sie, dass die Sichtbarkeitseinstellung in der XAML-Datei und die in der Code-behindDatei übereinstimmen müssen. Je nach Programmiersprache sind unterschiedliche Werte erlaubt. In C# muss für Non-Public internal eingesetzt werden, während in VB.NET Friend verwendet werden muss. Die Sichtbarkeit Public ist das Standardverhal-
schnell + kompakt
57
3 – XAML Namespaces
ten, es kann aber auch explizit angegeben werden (public für C# und Public für VB.NET). x:Subclass kann verwendet werden, um auch in Sprachen, die
keine partiellen Klassen erlauben, einer XAML-Datei eine Codebehind-Datei zuzuweisen. Wir gehen auf diesen Sonderfall an dieser Stelle nicht näher ein. x:TypeArguments geben den oder die Datentypen für Generic-Objekte in Wurzelknoten an. In WPF wird dieses Attribut für Page Functions verwendet. Sie werden mithilfe der Klasse System.Windows.Navigation.PageFunction implementiert. Diese von System.Windows.Controls.Page abgeleitete Klasse ist, wie auch Window, eine Basisklasse für Wurzelknoten in XAML-Dateien. Im Gegensatz zu Page kann sie allerdings einen Wert vom Typ T an die aufrufende Klasse zurückgeben: ...
In der Code-behind-Datei der PageFunction wird die Methode OnReturn aufgerufen, um einen Wert vom Typ T an die aufrufende Seite zurückzugeben und zu dieser zu navigieren: void ButtonFertig_Click(object sender, RoutedEventArgs e) { OnReturn( new ReturnEventArgs("Fertig")); }
58
Elemente im Namespace x:
In der aufrufenden Seite kann auf das Return-Event der PageFunction reagiert werden: private void BtnNavigate_Click( object sender, RoutedEventArgs e) { PageFunction pageFunction = new MyPageFunc(); pageFunction.Return += new ReturnEventHandler( PageFunction_Return); this.NavigationService.Navigate( pageFunction); } private void PageFunction_Return( object sender, ReturnEventArgs e) { MessageBox.Show(e.Result); }
x:Code
Mit x:Code kann Code direkt in XAML eingebettet werden. Es gibt jedoch eine Reihe von Einschränkungen und die Trennung von Benutzeroberfläche und Logik wird aufgehoben. Daher sollten Sie x:Code wirklich nur verwenden, wenn keine andere Lösung möglich ist.
schnell + kompakt
59
3 – XAML Namespaces
Im folgenden Beispiel wird die Methode OnInitialized direkt in der XAML-Datei angegeben, um dort den Text eines TextBlock zu setzen:
x:Name, x:FieldModifier x:Name muss ein Objekt eindeutig identifizieren. Der XAML Compiler erzeugt in der von ihm generierten Klasse eine Variable mit dem angegebenen Namen. Dieses Prinzip wurde im Detail bereits in Kapitel 2 erläutert. Dort findet man auch ein praktisches Beispiel für die Anwendung von x:Name.
Standardmäßig ist die Sichtbarkeit dieser Variable internal. Mit x:FieldModifier können Sie die Sichtbarkeit der Variable verändern. Für Public kann in C# public und in VB.NET Public angegeben werden. Non-Public ist das Standardverhalten und muss daher nicht angegeben werden (internal in C# und Friend in VB.NET).
60
Elemente im Namespace x:
In WPF bieten alle von System.Windows.FrameworkElement abgeleiteten Klassen die Eigenschaft Name an. Das Setzen dieser Eigenschaft setzt auch automatisch den Wert von x:Name. Der Grund dafür ist, dass Name als RuntimeNameProperty in FrameworkElement eingetragen ist: [RuntimeNamePropertyAttribute("Name")] ... public class FrameworkElement : ...
x:Key, x:Shared
Ressourcen können für viele Objekte in WFP angegeben werden (Nachfahrenklassen von System.Windows.FrameworkElement oder FrameworkContentElement), um Objekte, die mehrfach verwendet werden, nur einmal definieren zu müssen. Die Elemente in Ressourcen werden in einem Objekt vom Typ System.Windows.ResourceDictionary gespeichert. x:Key identifiziert die Elemente in Ressourcen. Als Key kann ein String, aber auch ein Objekt angegeben werden. In WPF können Sie die Ressourcen dann mit den Markup Extensions StaticResource oder DynamicResource ansprechen (für Details siehe Kapitel 4).
Im zuvor bereits erwähnten Beispiel für x:Array wird für das Array mit x:Key ein Name vergeben. In der Listbox wird das Array dann über den Namen als Datenquelle angegeben:
schnell + kompakt
61
3 – XAML Namespaces
... x:Shared gibt an, ob für jede Anfrage eine neue Instanz des Objekts erstellt oder ob jedes Mal dieselbe Instanz verwendet wird. Der Standardwert für x:Shared ist true (nur eine Instanz für alle Anfragen). Nur wenn false verwendet werden soll, muss das Attribut angegeben werden.
Ein Anwendungsfall für x:Shared="false" wäre zum Beispiel ein WFP-FrameworkElement (Button, ListBox, …), das als Ressource definiert wird, um dann mehrmals auf einer Seite verwendet zu werden.
x:Null
oder
62
Elemente im Namespace x:
x:Null verwenden Sie, um Eigenschaften den Wert null zuweisen zu können, da Eigenschaften nicht automatisch den Wert null enthalten, wenn kein Wert im XAML angegeben ist.
Ein Button in WFP hat beispielsweise standardmäßig einen grauen Hintergrund. Um ihn zu entfernen, geben Sie im XAML x:Null an (eine andere Möglichkeit wäre, die Farbe auf Transparent zu setzen):
Abb. 3.5: Button ohne Hintergrundfarbe
x:Static
oder
Mit x:Static können Sie auf statische Variablen oder Eigenschaften beliebiger Klassen zugreifen. Befindet sich die Klasse nicht
schnell + kompakt
63
3 – XAML Namespaces
im Haupt-Namespace, müssen Sie das Präfix des Namespace angeben. Im folgenden Beispiel wird als Inhalt eines Button das aktuelle Datum über die Eigenschaft Today der Klasse DateTime im System Namespace gesetzt:
x:Type
oder x:Type wird benötigt, um in XAML-Eigenschaften, die einen Wert vom Typ Type erwarten, einen Wert zu übergeben. Im zuvor schon gezeigten Beispiel für x:Array verwenden wir x:Type, um den Typ der Array-Elemente zu definieren: Array Sample
64
Elemente im Namespace x:
x:XData [elementData] ...
Wenn Sie XML-Daten in XAML-Dateien einbinden wollen, verwenden Sie dafür x:XData. Dieses Element wird meistens dazu verwendet, den Inhalt eines XmlDataProvider zu setzen. Im folgenden Beispiel legen wir einen XmlDataProvider als Ressource an. Eine ListBox verwendet ihn dann als Datenquelle. Zusätzlich wird in der ListBox ein XPath-Ausdruck angegeben, der es erlaubt, nur einen Teil des XML in der ListBox anzuzeigen. Im Beispiel werden Elemente vom Typ Book angegeben, deren Attribut Stock den Wert „in“ enthält. Für die weitere Verwendung des XML-Codes ist es am einfachsten, wenn der Namespace für das XML (xmlns im Wurzelknoten) auf einen leeren String gesetzt wird. In XPath-Abfragen ist es dann nicht nötig, ein Präfix anzugeben. Für komplexeres XML ist es natürlich möglich, einen oder mehrere Namespaces zu vergeben. Visual C# Programmierhandbuch
schnell + kompakt
65
3 – XAML Namespaces
XML Standards SQL Server 2005
Abb. 3.6: ListBox mit XML als Datenquelle
66
XML-Attribute in XAML
3.4 XML-Attribute in XAML xml:space xml:space gehört nicht mehr zu den XAML-Namespace-Elementen, sondern zu den XML-Attributen. Es wird verwendet, um Leerzeichen, Zeilenumbrüche und Ähnliches zu erhalten, da in XML diese Zeichen normalerweise entfernt werden. Gültige Werte sind preserve und default. Im folgenden Beispiel stellen wir eine Textbox einmal ohne und einmal mit xml:space="preserve" dar: New TextBox! New TextBox!
Abb. 3.7: TextBox ohne und mit xml:space="preserve"
schnell + kompakt
67
3 – XAML Namespaces
xml:lang xml:lang gehört wie xml:space zu den XML-Attributen. Mit seiner Hilfe können Sprache und Culture-Informationen für ein Objekt entsprechend dem RFC-3066-Standard (Tags for the Identification of Languages) definiert werden.
Objekte, die von FrameworkElement oder FrameworkContentElement im WPF Namespace abgeleitet sind, können äquivalent dazu die Eigenschaft Language verwenden.
mc:Ignorable, mc:ProcessContent mc:Ignorable und mc:ProcessContent gehören ebenfalls zu den XML-Attributen. mc:Ignorable gibt einen oder mehrere Namespaces an, die vom XAML Reader ignoriert werden sollen.
Verwendet wird dieses Attribut zum Beispiel vom Programm Microsoft Expression Blend zum Erstellen von Benutzeroberflächen für .NET 3.0-Applikationen (weitere Informationen zu die-
68
Zusammenfassung
sem Tool finden Sie auch in Kapitel 6). Es wird ein eigener Namespace d: eingebunden, der bei der Ausführung des Programms aber ignoriert werden soll:
Verwendet wird der Namespace, um Eigenschaften in XAML einzufügen, die nur für das Tool zur Designzeit relevant sind:
Wird ein Element nicht geladen, dann werden standardmäßig auch dessen Unterelemente ignoriert. Mit mc:ProcessContent kann man definieren, für welche Elemente die Unterelemente dennoch geladen werden sollen, obwohl das Element selbst vom Reader ignoriert wurde.
3.5 Zusammenfassung 쐌 XAML kann grundsätzlich mit beliebigen .NET-Klassen umgehen. Über Namespace Mappings geben Sie XAML bekannt, welche Namespaces in einer Datei zur Verfügung stehen sollen. 쐌 In XAML kann sowohl auf CLR als auch auf XML Namespaces verwiesen werden. Ein XML Namespace ist eine Gruppierung von mehreren CLR Namespaces.
schnell + kompakt
69
3 – XAML Namespaces
쐌 In Visual Studio 2005 werden für .NET 3.0-Projekte automatisch die beiden Namespaces für XAML und für WPF in jede XAML-Datei eingefügt. Für den WPF Namespace wird kein Präfix vergeben, während der XAML Namespace das Präfix x: erhält. 쐌 Der XAML Namespace x: stellt eine Sammlung von Markup Extensions und Compiler-Anweisungen dar, die XAML beim Kompilieren und Laden berücksichtigt.
70
KAPITEL 4 Die Sprache XAML 4.1
Einleitung
71
4.2
Die Basiselemente von XAML
74
4.3
Zusammenfassung
98
4.1 Einleitung XAML als deklarative Programmiersprache XAML gehört zur Kategorie der deklarativen Programmiersprachen. Diesen Typ von Sprachen kennzeichnet, dass sie definieren, was gemacht werden soll, jedoch nicht wie. Führt man sich vor Augen, wie XAML entstanden ist, wird diese Aussage klarer: XAML erblickte bei Microsoft als „eXtensible Avalon Markup Language“ das Licht der Welt. Man sieht, dass die Ausgangsidee war, XAML als reine Definitionssprache für die Benutzerschnittstelle in WPF (alias Avalon) zu verwenden; XAML sollte das Aussehen der GUI deklarieren, ohne darauf Rücksicht zu nehmen, wie die eigentliche Darstellung auf dem Bildschirm erfolgt. Heute kann man die Aufgabe von XAML allgemeiner, nicht mehr nur abhängig von Benutzerschnittstellen definieren: XAML ist eine Sprache, mit der der Aufbau eines Objektgraphen definiert wird. Durch Ergänzung mit Managed Code wird daraus eine anwendbare Applikation.
schnell + kompakt
71
4 – Die Sprache XAML
Obwohl XAML-Dateien immer well-formed XML-Dateien sind, beinhaltet die Sprache viele Elemente, die über pures XML hinaus gehen. In diesem Kapitel sollen genau diese Teile erklärt werden. Wir betrachten dabei jedes Sprachelemente aus drei Perspektiven: 쐌 Wie funktioniert die Verwendung in XAML? 쐌 Wo findet man eine entsprechende Anwendung des jeweiligen XAML-Konzepts in WPF? 쐌 Wie muss eine Klasse in C# entwickelt sein, damit sie das entsprechende Sprachelement von XAML unterstützt?
Das Beispiel Zur Erklärung der Umsetzung von eigenen C#-Klassen, die in XAML verwendet werden können, geht dieses Kapitel von einem durchgängigen Beispiel aus. Gefordert ist eine .NET-Anwendung, die Feiertage ermitteln muss. Die Definition, welche Tage als gesetzliche Feiertage gelten, ändert sich jedoch immer wieder. Sie ist auch von Land zu Land verschieden. Insofern möchten wir XAML verwenden, um die Definition der Feiertage vom Programmcode zu trennen, und diese Logik damit leichter warten zu können. Abbildung 4.1 zeigt ein UML-Klassendiagramm des Beispiels. Die Klasse HolidayCalendar soll in XAML als Wurzelelement verwendet werden können. Ihre Spezialisierung CountryHolidayCalender wird als partielle Klasse vom XAML Compiler generiert. Der Feiertagskalender besteht aus einer Sammlung von HolidayObjekten. Jeder dieser Feiertage hat einen Namen (z. B. „Neujahrstag“) sowie ein Datum. Das Datum kann in unserem Beispiel auf drei verschiedene Arten definiert werden:
72
Einleitung
Abb. 4.1: UML-Klassendiagramm des Holiday-Beispiels
쐌 Als fixer Tag eines Monats (z.B. Neujahrstag, ist immer am 1. Januar). Solche fixen Feiertage werden über die Klasse FixedHolidayDate abgebildet. 쐌 Als Feiertag relativ zum Ostersonntag (z.B. Pfingstmontag, ist immer 50 Tage nach dem Ostersonntag). Zur Abbildung dieser Feiertage steht die Klasse Easter zur Verfügung. Wir verwenden in diesem Beispiel zur Berechnung des Osterdatums eine modifizierte Variante der Gaußschen Osterformel. 쐌 Als n-ter Wochentag eines Monats (z.B. Muttertag, ist immer der zweite Sonntag im Mai). Diese Logik wird von VariableHolidayDate implementiert. Die beiden Klassen CreateVariableHolidayDate und HolidayDateConverter werden für die Umsetzung von XAML Markup Extensions und XAML-Typkonvertierungen benötigt. Auf Details dazu wird in diesem Kapitel an der entsprechenden Stelle eingegangen.
schnell + kompakt
73
4 – Die Sprache XAML
Abb. 4.2: Der HolidayViewer WPF Client
Die WPF Client-Anwendung für unser Beispiel ist denkbar einfach. Sie besteht im Wesentlichen nur aus einer einzigen XAMLDatei, die den Benutzer in einer Auswahlliste ein Jahr wählen lässt, und mit Hilfe der oben beschriebenen Feiertagsklassen das jeweilige Datum der definierten Feiertage anzeigt. Abbildung 4.2 zeigt die Benutzeroberfläche des WPF-Clients.
4.2 Die Basiselemente von XAML XAML-Elemente Einleitung XAML basiert auf XML. Insofern muss jede XAML-Datei bei der Definition von Elementen alle Regeln von XML erfüllen. Der XAML Reader sucht für jedes Elemente anhand seines Namens die entsprechende Klasse in einem Assembly und instanziiert beim Laden ein Objekt dieser Klasse. Ein Objekt der Klasse System.Windows.Controls.TextBlock wird beispielsweise mit folgendem Element angelegt:
74
Die Basiselemente von XAML
Wie man sieht, wird dieser TextBlock vollständig durch seine Attribute definiert. Es gibt jedoch auch Elemente, die den XML Content verwenden können. Man spricht in diesem Fall von der XAML Content-Syntax: 2005 2006 2007 2008 2009 2010
Wie man im oben dargestellten Codeausschnitt sieht, wird der Inhalt der Objekte vom Typ System.Windows.Controls.ComboBoxItem und ComboBox nicht als Attribut, sondern als Elementwert angegeben. Implementierung in .NET Wie erwähnt, wird für jedes Element von XAML ein Objekt der entsprechenden Klasse angelegt. Insofern kann grundsätzlich jede Klasse, die in einem Assembly vorliegt, durch XAML instanziiert werden. Die einzige Voraussetzung ist, dass sie einen public-Konstruktor anbietet, der keine Parameter verlangt. Damit eine Klasse die Verwendung der oben beschriebenen XAML Content Syntax erlaubt, sind spezielle Vorkehrungen notwendig. Der Elementinhalt wird beim Laden in ein Attribut der Klasse eingetragen. Damit der XAML Reader jedoch weiß, welches Attribut dafür zu verwenden ist, muss der Attributname bei der Klasse im ContentProperty-Attribut angegeben werden (Klasse System.Windows.Markup.ContentPropertyAttribute).
schnell + kompakt
75
4 – Die Sprache XAML
In unserem Beispiel soll die Klasse HolidayCalender die Verwendung der XAML Content-Syntax erlauben. Unser Ziel ist es, dass einzelne Feiertage, aus denen unser Feiertagskalender besteht, als Unterelemente (= Content) des Elements HolidayCalender angegeben werden können: [...]
Um das zu erreichen, muss die Klasse mit dem ContentPropertyAttribut ausgestattet werden: namespace Samples { [ContentProperty("Holidays")] public class HolidayCalendar { [...] } }
Der Inhalt des Elements wird dadurch automatisch der Eigenschaft Holidays zugewiesen.
76
Die Basiselemente von XAML
XAML-Attribute Einleitung Bei Attributen gilt grundsätzlich das Gleiche wie bei Elementen: Auch sie können in XAML-Dateien in der aus XML bekannten Syntax angegeben werden. Jedes Attribut entspricht dabei einer Eigenschaft der entsprechenden Klasse. In unserem Beispiel können wir einem Feiertag über das Attribut Name einen Namen zuweisen.
Damit dieses Attribut in XAML zur Verfügung steht, muss in der Klasse Holiday die Eigenschaft Name implementiert werden: public class Holiday { private string name; public string Name { get {return name;} set {name = value;} } [...] }
Da die Eigenschaften einer Klasse jedoch in vielen Fällen auch komplexere Datentypen – zum Beispiel Klassen, Sammlungen von anderen Objekten – darstellen, ist eine zusätzliche Möglichkeit zur Angabe von Attributwerten notwendig, die ein wenig von aus XML Benanntem abweicht: die Property-Element-Syntax. Ihre Syntax lautet . Das folgende Beispiel zeigt einen für WPF typischen Fall für die Verwendung der Property-Element-Syntax:
schnell + kompakt
77
4 – Die Sprache XAML
[...]
Es soll ein Wert für die Eigenschaft ItemTemplate von System.Windows.Controls.ListBox angegeben werden. Der Wert dieser Eigenschaft lässt sich nicht als einfache Zeichenkette darstellen, sondern verlangt ein Objekt vom Typ DataTemplate, das selbst wiederum aus vielen, teilweise komplexen Unterelementen besteht. Die Angabe des Attributwerts ist daher nicht direkt im ListBox-Element möglich; sie wird in ein eigenes Property-Element ausgegliedert. Eine weitere Anwendung der Property-Element-Syntax in WPF findet man bei Objekten, die Attribute anbieten, in die eine Sammlung anderer Objekte eingebunden werden können. Ein prominenter Vertreter von Klassen mit einer solchen Struktur ist System.Windows.Controls.Grid: [...]
78
Die Basiselemente von XAML
In Tabelle 4.1 werden die beiden in XAML unterstützten Arten, Attributen einen Wert zuzuweisen, nochmals zusammenfassend gegenübergestellt. Tabelle 4.1: Vergleich Attribut- und Property-Element-Syntax
Attributsyntax
Property-Element-Syntax
Wert
Wert kann nur ein Basisdatentyp sein.
Wert kann ein Basisdatentyp oder ein eigenes Objekt sein.
Zeilenumbrüche im Wert bleiben nicht erhalten.
Wenn Wert eine Zeichenkette ist, können Zeilenumbrüche erhalten bleiben.
Implementierung in .NET Attribute in XAML entsprechen Eigenschaften in .NET-Klassen. Eigenschaften müssen keine besonderen Kriterien erfüllen, damit sie in XAML ansprechbar sind. Eine Besonderheit stellen jedoch Attribute dar, die eine Sammlung an Objekten enthalten sollen. Der Typ der Eigenschaft muss eines der folgenden Kriterien erfüllen: 쐌 Implementiert System.Collections.IList oder System.Collections.Generic.IList
쐌 Implementiert System.Collections.IDictionary oder System. Collections.Generic. IDictionary
쐌 Ist abgeleitet von System.Array (siehe dazu auch x:Array in Kapitel 3)
schnell + kompakt
79
4 – Die Sprache XAML
쐌 Implementiert die von WPF definierte Schnittstelle System. Windows.Markup.IAddChild
In unserem Beispiel enthält die Klasse HolidayCalendar eine Eigenschaft Holidays, die eine Liste von Holiday-Objekten verwalten kann. Zum Speichern der Objekte verwenden wir die generische Variante einer Liste, also System.Collections.Generic.IList: using System; using System.Windows.Markup; using System.Collections.Generic; namespace Samples { [ContentProperty("Holidays")] public class HolidayCalendar { private List holidays = new List(); public List Holidays { get { return holidays; } } private string country; public string Country { get { return country; } set { country = value; } } } }
80
Die Basiselemente von XAML
Attribute mit Typkonvertierung Einleitung Attribute können optional Möglichkeiten zur Typkonvertierung anbieten. Dieses Hilfsmittel ist praktisch, um XAML-Code kurz und gut lesbar zu halten. Ein Beispiel für die Verwendung von Typkonvertierung findet man in unserem WPF Client. Die Eigenschaft Margin von Grid verlangt ein Objekt vom Typ Thickness. Ohne Typkonvertierung müsste man auf die Property-Element-Syntax zurückgreifen, die bereits beschrieben wurde: [...] Margin wurde von Microsoft jedoch mit der Möglichkeit der automatischen Typkonvertierung ausgestattet. Das bedeutet, dass der Wert in einem vom Typkonvertierer interpretierbaren Format als Zeichenkette angegeben werden kann. Aufgabe des Typkonvertierers ist es, den String in ein Objekt umzuwandeln. Die Angabe des Werts für Margin wird dadurch wesentlich kompakter: [...]
schnell + kompakt
81
4 – Die Sprache XAML
Typkonvertierer werden in der Entwicklergemeinde sehr kontrovers diskutiert. Einerseits ermöglichen sie in vielen Fällen wesentlich kürzere und kompaktere Schreibweisen von XAML-Code. Im Extremfall entstehen durch Typkonvertierer jedoch eigene kleine „Mini-Sprachen“, die in XAML eingebettet werden. Was hier gemeint ist, soll am Beispiel der PathGeometry-Markup-Syntax erklärt werden: Ähnlich wie in SVG (Scaleable Vector Graphics; siehe http:// www.w3.org/Graphics/SVG/) ist es auch in XAML möglich, das Aussehen grafischer Objekte durch Angabe komplexer Pfade zu spezifizieren. Dafür dient das Objekt Path. Die Teilpfade eines Path-Elements können in XAML auf zwei Arten angegeben werden: Entweder man verwendet die Elementsyntax (besteht aus XML-Unterelementen wie PathGeometry, PathSegmentCollection, LineSegment etc.) oder die PathGeometry-Markup-Syntax. Letztere entspricht fast exakt der Path-Syntax aus dem SVG-Standard:
Wie man sieht, ist die Angabe von Pfaden in der PathGeometryMarkup-Syntax sehr kompakt. Man könnte sie jedoch durchaus als eine in XAML eingebettete Minisprache bezeichnen. Der Nachteil solcher Erweiterungen ist, dass sie in vielen Fällen die Weiterverarbeitung der XAML-Dateien in Werkzeugen verhindern. Beim Spezifizieren von Pfaden besteht noch eine gute Chance, dass XAML-Tools sie beherrschen, da die Syntax von
82
Die Basiselemente von XAML
Microsoft vorgegeben ist und in Anlehnung an SVG definiert wurde. Generell lässt sich jedoch eine Abkehr von solchen Konstrukten erkennen. Die ersten Beta-Versionen von .NET 3.0 enthielten sie noch in vielen Bereichen, in der finalen Version waren sie verschwunden.
Abb. 4.3: Darstellung des Pfadausdrucks in XAMLPad
Im Zweifelsfall ist man als Entwickler gut beraten, die Elementsyntax zu verwenden, auch wenn der Code dadurch etwas länger wird. Implementierung in .NET Eigene Attribute können recht einfach mit der Möglichkeit zur Typkonvertierung versehen werden. Als Programmierer einer .NET-Klasse kann man dadurch den XAML-Entwicklern das Leben erleichtern und deren XAML-Code schlanker halten.
schnell + kompakt
83
4 – Die Sprache XAML
In unserem Beispiel möchten wir das anhand der Eigenschaft Date der Klasse Holiday erläutern. Die Eigenschaft verlangt ein Objekt vom Typ HolidayDate. Der XAML-Entwickler kann daher aus den drei Nachfahrenklassen dieser Klasse den passenden Typ auswählen: FixedHolidayDate, VariableHolidayDate oder Easter. Natürlich können alle diese Objekte auch ohne Typkonvertierung über Property-Element-Syntax verwendet werden:
Wir streben jedoch die Implementierung eines Typkonvertierers an, der anhand einer übergebenen Zeichenkette automatisch erkennt, welche der Nachfahrenklassen von HolidayDate instanziert werden muss, und der das angelegte Objekt auch gleich initialisiert. Der XAML-Code, den wir ermöglichen wollen, sieht wie folgt aus:
Typkonvertierer werden entwickelt, indem eine von System.ComponentModel.TypeConverter abgeleitete Klasse entwickelt wird. Man muss darin zwei Methoden überschreiben: 쐌 CanConvertFrom wird von XAML aufgerufen, um herauszufinden, ob der Typkonvertierer den Zieldatentyp auf Basis eines bestimmten Datentyps (z.B. String) erstellen kann. 쐌 ConvertFrom nimmt schließlich die eigentliche Konvertierung vor. In unserem Fall wird der Konvertierer wie folgt entwickelt:
84
Die Basiselemente von XAML
using System; using System.Globalization; using System.ComponentModel; namespace Samples { public class HolidayDateConverter : TypeConverter { public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType ) { if (sourceType == typeof(string)) return true; else return base.CanConvertFrom( context, sourceType); } public override object ConvertFrom( ITypeDescriptorContext context, CultureInfo culture, object value ) { string dateString = (string)value; if (dateString == "Easter") return new Easter(); else { string[] dateParts = dateString.Split('.'); return new FixedHolidayDate( Convert.ToInt32(dateParts[0]), Convert.ToInt32(dateParts[1]));
schnell + kompakt
85
4 – Die Sprache XAML
} } } }
Um diesen Typkonvertierer der Eigenschaft Date der Klasse Holiday zuzuordnen, muss sie mit dem Attribut TypeConverter (Klasse System.ComponentModel.TypeConverter) versehen werden. Als Parameter für das Attribut gibt man den Typ an, in dem der Typkonvertierer implementiert wurde. namespace Samples { public class Holiday { [...] [TypeConverter( typeof(HolidayDateConverter) )] public HolidayDate Date { get { return date; } set { date = value; } } } }
Markup Extensions Einleitung Markup Extensions sind ein Konzept von XAML, nicht von XML. Sie werden eingesetzt, um in Attributwerten Referenzen auf Objekte anzugeben, ohne auf die zuvor bereits beschriebenen Konzepte von Property-Element-Syntax oder Typkonvertierer
86
Die Basiselemente von XAML
zurückgreifen zu müssen. Im Gegensatz zu diesen beiden Methoden sind Markup Extensions wesentlich allgemeiner verwendbar. Sie müssen nur eine Referenz auf irgendein Objekt zurückliefern; es muss sich dabei aber nicht unbedingt um eine neue Instanz einer Klasse handeln. Markup Extensions kennzeichnet man in Attributwerten, indem man sie in geschwungene Klammern einschließt ({ und }):
Die geschwungene Klammer führt dazu, dass der XAML Reader erkennt, dass der Attributwert nicht ein Literal oder ein über Typkonvertierer umwandelbarer Wert ist, sondern dass der Wert für das Attribut über die Klasse zu ermitteln ist, welche die Markup Extension implementiert. Markup Extensions können auch geschachtelt verwendet werden: [...]
Für den Aufbau von Markup-Extension-Ausdrücken gelten folgende Regeln: 쐌 Nach dem Namen der Extension folgen die Parameter. Sie sind durch Kommas zu trennen. 쐌 Enthalten die Parameter keine „=“-Zeichen, werden sie an den Konstruktor der Markup Extension als Parameter übergeben.
schnell + kompakt
87
4 – Die Sprache XAML
Dementsprechend müssen Anzahl und Typ der Parameter für die Extension der Anzahl und den Typen des Konstruktors entsprechen. Hier ein Beispiel für diese Art der Instanziierung einer Markup Extension: {StaticResource dateConverter}
쐌 Enthalten die Parameter „=“-Zeichen, wird die Markup Extension mithilfe ihres Default-Konstruktors (ohne Parameter) angelegt. Die Parameternamen aus der Markup Extension müssen Eigenschaften der Extension-Klasse entsprechen. Die Parameterwerte werden in diese Eigenschaften übertragen. Hier ein Beispiel für diese Verwendungsart: {Binding Path=Name}
Die wichtigsten beiden Einsatzbereiche von Markup Extensions in WPF sind 쐌 Referenzen auf Ressourcen und 쐌 Data Binding. Ressourcen und Data Binding würden alleine Material für ein eigenes Buch bieten. An dieser Stelle sollen diese beiden Konzepte von WPF nicht vollständig beschrieben werden. Sie dienen uns nur als typische Anwendungsfälle, um Markup Extensions zu beschreiben. Markup Extensions und Ressourcen Jedes Framework-Element von WPF (Nachfahrenklassen von System.Windows.FrameworkElement oder FrameworkContentElement) hat eine Resources-Eigenschaft. In ihr kann eine Liste von Objekten abgelegt werden, von denen jedes über einen Schlüsselwert, in den meisten Fällen einen String, identifiziert werden kann (siehe dazu auch x:Key in Kapitel 3). Sinn dieses Konstrukts ist es, dass mehrfach verwendete Objekte wie Stile, Fonts, Brushes, Logos etc. nur einmal in einer XAML-Datei oder einer ganzen
88
Die Basiselemente von XAML
Applikation angegeben werden und an den entsprechenden Stellen auf sie verwiesen wird. Dadurch kann ein Entwickler einerseits Einheitlichkeit sicherstellen und andererseits den Wartungsaufwand reduzieren. Zum Verweisen auf Ressourcen werden die Klassen StaticResource und DynamicResource angeboten. In unserem WPF Client für das Feiertagsbeispiel sieht man die Verwendung der Markup Extension unter anderem zum zentralen Definieren der Schriftart für ComboBox und ListBox. Beide Controls sind von System.Windows.Controls.Primitives.Selector abgeleitet; daher wird der Stil für diesen Basistyp definiert. [...] [...] [...] [...] [...]
schnell + kompakt
89
4 – Die Sprache XAML
Markup Extensions und Data Binding Mit Data Binding bezeichnet man den Mechanismus, wie Elemente aus der Benutzerschnittstelle mit der Geschäftslogik- oder Datenzugriffsschicht verbunden werden. Richtig konfiguriert ist keine Programmierung mehr notwendig, um Daten aus einer Datenquelle ins Front-End zu übertragen. Zur Angabe von Data Bindings wird in XAML ebenfalls eine Markup Extension verwendet: Binding. Sie ermöglicht es, den Wert einer Eigenschaft eines Objekts an ein Ausdrucksobjekt zu binden. Dieses Ausdrucksobjekt wird erst zur Laufzeit ausgewertet; der Attributwert muss daher zur Kompilierzeit noch nicht fix angegeben werden. Einige typische Anwendungsbeispiele für die Markup Extension Binding wären:
쐌 Binden des Werts eines Steuerelements an ein Ergebnis einer Datenbankabfrage 쐌 Verknüpfen einer Eigenschaft eines Steuerelements an den Wert eines anderen (z.B. Verstecken einer TextBox, wenn in einer ComboBox ein bestimmtes Element ausgewählt ist) 쐌 Animationen (d.h. Binden einer Eigenschaft eines Steuerelements an eine Animationsklasse aus System.Windows.Media. Animation) In unserem WPF-Client muss beispielsweise der Inhalt der ListBox an die Holidays-Eigenschaft des in den Ressourcen definierten CountryHolidayCalendar-Objekts gebunden werden:
90
Die Basiselemente von XAML
[...] [...] [...] [...]
Wie man im XAML-Code sieht, wird die Eigenschaft ItemsSource von ListBox über ein Data Binding an ein Ausdrucksobjekt gebunden. Typisch für diese Eigenschaft ist, dass sie in Verbindung mit der Binding Markup Extension verwendet wird. Die Bindung besteht aus zwei Teilen: 쐌 Source gibt an, dass die Einträge in der Listbox (d.h. die Elemente in der Eigenschaft Items) an unser Feiertagskalenderobjekt aus den Ressourcen gebunden sind. 쐌 Die Eigenschaft Path ist relativ zu Source zu verstehen. Source liefert ein Objekt vom Typ CountryHolidayCalendar, Path legt fest, welche Eigenschaft dieser Klasse für das Data Binding verwendet werden soll. Implementierung in .NET Markup Extensions sind ein sehr mächtiges und auf den ersten Blick vielleicht ungewohntes Konzept. Eigene Markup Exten-
schnell + kompakt
91
4 – Die Sprache XAML
sions zu entwickeln, ist jedoch einfacher, als man vermuten könnte. Als Basisklasse für solche Eigenentwicklungen dient System.Windows.Markup.MarkupExtension. Diese Klasse muss die Methode ProvideValue implementieren. Ihre Aufgabe ist es, das entsprechende Objekt anzulegen oder aufzufinden und es an WPF zurückzugeben. In unserem Beispiel möchten wir eine Markup Extension verwenden, um XAML-Entwicklern das Anlegen von VariableHolidayDate-Objekten zu erleichtern. Folgende XAML-Syntax soll möglich werden:
Man könnte die Frage stellen, wozu hier auf eine Markup Extension zurückgegriffen wird. Schließlich wurde bereits zuvor das Konzept der Typkonvertierung erklärt, mit der eine sehr ähnliche Syntax ermöglicht werden könnte. Die Antwort würde lauten, dass in unserem Fall tatsächlich Typkonvertierung möglich wäre. Man muss jedoch bedenken, dass wir Sourcecode-Zugriff auf die Feiertagsbibliothek haben und nur deshalb ein Typkonvertierer überhaupt in Frage kommt. (Schließlich muss die Eigenschaft, für die er gelten soll, durch ein Attribut markiert sein.) Hätten wir nicht die Möglichkeit, den Sourcecode zu verändern, käme nur die Markup Extension in Frage. Als Name der Markup Extension wird im XAML der Name der Klasse verwendet, welche die Extension implementiert. Endet der Klassenname jedoch mit Extension, muss im XAML der Name ohne dieses Postfix verwendet werden (also zum Beispiel StaticResource in XAML für die Klasse StaticResourceExtension). Die Implementierung der Extension sieht folgendermaßen aus:
92
Die Basiselemente von XAML
using System; using System.Windows.Markup; namespace Samples { [MarkupExtensionReturnType (typeof(HolidayDate))] public class CreateVariableHolidayDate : MarkupExtension { private DayOfWeek weekday; private int month; private int number; public CreateVariableHolidayDate( DayOfWeek weekday, int month, int number) { this.weekday = weekday; this.month = month; this.number = number; } public override object ProvideValue( IServiceProvider serviceProvider) { VariableHolidayDate holiday = new VariableHolidayDate(); holiday.Weekday = weekday; holiday.Month = month; holiday.Number = number; return holiday; } } }
schnell + kompakt
93
4 – Die Sprache XAML
Attached Properties Einleitung WPF bietet viele Klassen an, die eine Sammlung von untergeordneten Objekten verwalten. Diese werden als Items-Controls bezeichnet (benannt nach der Basisklasse System.Windows.Controls.ItemsControl, von der alle diese Klassen abgeleitet sind). Im Kapitel „XAML-Attribute“ wurde bereits erklärt, wie Klassen Eigenschaften anbieten können, die eine Sammlung von Objekten speichern. In Zusammenhang mit solchen Konstrukten stellt sich häufig das Problem, dass das übergeordnete Objekt, das die Liste verwaltet, alle oder gewisse Elemente aus der Sammlung mit speziellen Eigenschaften oder Markierungen versehen muss. Leichter nachvollziehbar wird die Problemstellung wenn man sich ein typisches Beispiel aus WPF vor Augen führt: In einem Grid können Steuerelemente in einem Raster aus Zeilen und Spalten angeordnet werden. Dementsprechend muss je Steuerelement festgelegt werden, in welcher Zeile und welcher Spalte es angezeigt werden soll. Es wäre jedoch falsch, diese beiden Eigenschaften fix bei jedem Steuerelement vorzusehen. Schließlich können diese auch ohne weiteres außerhalb eines Grid-Control verwendet werden.
94
Die Basiselemente von XAML
[...] [...]
Die Lösung für diese Herausforderung sind Attached Properties. Sie werden zwar bei den einzelnen Objekten der Sammlung angegeben, sind jedoch im übergeordneten Objekt implementiert. In unserem WPF-Client des Feiertagsbeispiels wird dieses Prinzip angewandt: Die Syntax von Attached Properties gleicht der von Attributen in der Property-Element-Syntax (siehe dazu auch „XAML-Attribute“ in diesem Kapitel). Bei Attached Properties ist jedoch links neben dem Punkt der Name einer übergeordneten Klasse angeführt, während in der Property-Element-Syntax der Klassenname des gerade zu definierenden Objekts angegeben wird. Implementierung in .NET Damit eine Klasse ein Attached Property anbietet, muss sie zwei statische Methoden implementieren: 쐌 GetPropertyName erhält als Parameter das Objekt, auf welches das Attached Property angewandt wird. Der Rückgabewert muss dem Typ des Attached Property entsprechen.
schnell + kompakt
95
4 – Die Sprache XAML
쐌 SetPropertyName erhält als Parameter das Objekt, auf welches das Attached Property angewandt wird, sowie den Wert des Property. In unserem Feiertagsbeispiel sollen Attached Properties für die Klasse HolidayCalendar demonstriert werden. Sie soll es untergeordneten Holiday-Objekten ermöglichen, anzugeben, ob der Feiertag als arbeitsfrei gilt oder nicht. Natürlich wäre es möglich, ein entsprechendes Attribut direkt in der Holiday-Klasse vorzusehen. Durch die Implementierung über Attached Properties wird jedoch berücksichtigt, dass ein Tag in einem Land als arbeitsfrei gelten kann, in einem anderen aber nicht. Zum Implementieren des Attached Property wird die HolidayCalendar-Klasse um die notwendigen Get- und Set-Methoden erweitert: namespace Samples { [ContentProperty("Holidays")] public class HolidayCalendar { [...] private static List leisureDays = new List(); public static void SetLeisureDay( Holiday holiday, bool isLeasureDay) { if (isLeasureDay) leisureDays.Add(holiday); }
96
Die Basiselemente von XAML
public static bool GetLeisureDay( Holiday holiday) { return leisureDays.Contains(holiday); } } }
Im XAML-Code kann das Attached Property LeisureDay wie folgt verwendet werden: [...]
Es empfiehlt sich, Attached Properties als WPF Dependency Properties zu implementieren. Dependency Properties sind eine Erweiterung des aus .NET bekannten Property-Modells um WPF-
schnell + kompakt
97
4 – Die Sprache XAML
spezifische Elemente. Sie bieten WPF-spezielle Unterstützung für Funktionen wie Data Binding, Animationen, Templates und Styles etc. Nahezu alle Attached Properties der WPF-Klassen sind als Dependency Properties implementiert. An dieser Stelle soll jedoch nicht im Detail auf die Regeln und Möglichkeiten dieses neuen Typs von Eigenschaften eingegangen werden.
4.3 Zusammenfassung 쐌 Obwohl XAML auf XML basiert, bietet XAML eine Vielzahl an Erweiterungen. 쐌 Alle XAML-Konstrukte führen direkt zum Instanziieren entsprechender Objekte. Insofern hat jedes XAML-Konzept seine Entsprechung in C#. 쐌 Bei Attributen ermöglicht die Property Element Syntax das Setzen von Eigenschaftswerten, auch wenn sich die Werte nicht als einfache Zeichenkette darstellen lassen. 쐌 Mithilfe von Typkonvertierung lässt sich XAML-Code einfacher und besser lesbar gestalten. In eigenen Typkonvertierern kann Logik zum Anlegen von Objekten auf Basis von Zeichenketten gekapselt werden. 쐌 Markup Extensions sind in WPF im Speziellen für Data Binding und Ressourcen relevant. Mit ihnen können nicht nur neue Objekte angelegt, es kann auch an mehreren Stellen auf ein und dasselbe Objekt verwiesen werden. 쐌 Mit Attached Properties ermöglicht XAML das Hinzufügen von Eigenschaften zu Objekten, die nicht in deren Klasse vorgesehen sind. Übergeordnete Klassen, die eine Sammlung von untergeordneten Objekten verwalten, können so einzelne Objekte der Sammlung mit Informationen versehen.
98
KAPITEL 5 XAML und WF 5.1
Einleitung
5.2
XAML oder C#?
100
99
5.3
XAML mit WF – ein Beispiel
101
5.4
Kompilieren von XAML Workflows
107
5.5
Zusammenfassung
107
5.1 Einleitung Windows Workflow Foundation (WF) ist das zweite Produkt aus dem Hause Microsoft, das auf XAML setzt. Wie erwähnt ist XAML zwar im Zuge der Entwicklung von WPF entstanden. Die Technik ist jedoch von der Grundidee her überall dort geeignet, wo es darum geht, dass auf der einen Seite ein Teil eines Programms strukturell beschrieben wird und die Logik auf der anderen Seite von der Struktur getrennt mithilfe einer Programmiersprache wie C# entwickelt werden soll. Genau das ist bei WF der Fall: XAML wird eingesetzt, um die Struktur eines Workflow zu beschreiben, die Logik der einzelnen Workflow-Elemente steckt in den .NET Assemblies. Die Namensgebung ist in WF in Bezug auf XAML etwas verwirrend. Workflows werden in Dateien mit der Dateierweiterung .xoml gespeichert. Man findet sowohl im Web als auch in der einschlägigen Literatur immer wieder den Begriff eXtensible Object Markup Language als Langschrift für die Abkürzung XOML. Dieser Begriff wird jedoch von Microsoft nicht mehr verwendet. Die
schnell + kompakt
99
5 – XAML und WF
Abkürzung XOML blieb als Dateierweiterung für WorkflowDateien erhalten, die Markup-Sprache für Workflows heißt jedoch XAML!
5.2 XAML oder C#? Aufgrund der Tatsache, dass jedes XAML-Element immer ein Gegenstück in Form einer .NET-Klasse haben muss, hat man als Entwickler sowohl bei WF als auch bei WPF immer die Wahl, anstatt XAML alles in einer .NET-Programmiersprache wie C# zu schreiben. Bei WPF wird die Wahl sehr selten auf eine rein auf Code basierende Struktur fallen, da neben anderen Nachteilen berücksichtigt werden muss, dass alle grafischen Editoren ausschließlich XAML als Grundlage zur Gestaltung von Benutzeroberflächen unterstützen. Bei WF ist das anders. Visual Studio lässt einem Programmierer die freie Wahl, Workflows in XAML oder einer Sprache wie C# zu definieren. Selbst der grafische Workflow Designer von WF beherrscht das Generieren von Code und XAML. Abbildung 5.1 zeigt, wie Visual Studio einem Entwickler die Wahl zwischen Workflow-Erstellung als Code-Datei oder als XAML-Datei lässt. Wir empfehlen Ihnen, Workflows in XAML umzusetzen. Falls notwendig, kann Logik in eigenen Code-behind-Dateien untergebracht werden. Workflows in Programmcode haben einen entscheidenden Nachteil: Sie können nicht zur Laufzeit geladen werden, sondern müssen unbedingt kompiliert werden.
100
XAML mit WF – ein Beispiel
Abb. 5.1: Wahl zwischen Workflows in Code oder in XAML in Visual Studio 2005
5.3 XAML mit WF – ein Beispiel Was den XAML-Funktionsumfang betrifft, gelten für WF alle in den vorherigen Kapiteln erklärten Prinzipien. An dieser Stelle soll der Aufbau einer XAML-Workflow-Datei anhand eines einfachen Beispiels erklärt werden. Ziel unseres Beispiels ist es, in einer Schleife dem Anwender in einer Konsolenanwendung eine Frage zu stellen, die Antwort auszuwerten und eine entsprechende Rückantwort zu geben. Abbildung 5.2 zeigt den Workflow im Workflow Designer in Visual Studio.
schnell + kompakt
101
5 – XAML und WF
Abb. 5.2: Beispiel-Workflow im Workflow Designer
Als Wurzelelement enthalten XAML-Workflow-Dateien wie die oben dargestellte entweder 쐌 System.Workflow.Activities.SequentialWorkflowActivity (für sequentielle Workflows) 쐌 oder System.Workflow.Activities.StateMachineWorkflowActivity (für State Machine Workflows).
102
XAML mit WF – ein Beispiel
Der XML-Namespace für XAML-Workflow-Dateien ist http:// schemas.microsoft.com/winfx/2006/xaml/workflow. Dementsprechend sieht die XAML-Workflow-Datei unseres Beispiels auf oberer Ebene wie folgt aus: [... (siehe nächster Codeausschnitt)]
Innerhalb der Sequenz LoopSequence ist der Kern des Workflow enthalten:
schnell + kompakt
103
5 – XAML und WF
104
XAML mit WF – ein Beispiel
Wie man sieht, findet man schon in diesem einfachen Beispiel einer XAML-Workflow-Spezifikation viele in diesem Buch bereits beschriebene Sprachkonzepte. Zum Aufruf von Logik sind in WF verschiedene Konzepte enthalten. Unter anderem steht eine fertige Komponente für den Aufruf von Web Services zur Verfügung und es können Methoden von lokal zur Verfügung gestellten Service-Klassen aufgerufen werden (siehe im oben dargestellten Beispiel die Verwendung des CallExternalMethodActivity-Elements). Wir möchten an dieser Stelle jedoch mehr auf die XAML-bezogenen Funktionen zur Verbindung von Workflow-Spezifikation mit Code eingehen: den Code-behind-Dateien. Zu jedem XAML Workflow kann optional eine Code-behind-Datei verwendet werden. In ihr können Eigenschaften und Methoden implementiert werden, die im Workflow zur Verfügung stehen. In unserem Beispiel werden lokale, in der Code-behindDatei des Workflow enthaltene Methoden beispielsweise für die Bedingungen verwendet: [...]
schnell + kompakt
105
5 – XAML und WF
In der Code-behind-Datei sind diese Methoden implementiert. Sie müssen sich in der Klasse befinden, die im Wurzelelement der XAML-Workflow-Datei im Element x:Class angegeben ist. using System; using System.Workflow.Activities; namespace Samples { public partial class SampleWorkflowXaml : SequentialWorkflowActivity { public string UserAnswer = default(System.String); private void CorrectAnswer_Condition( object sender, ConditionalEventArgs e) { e.Result = UserAnswer == "cool"; } private void Quit_Condition( object sender, ConditionalEventArgs e) { e.Result = UserAnswer == "quit"; } private void Loop_Condition( object sender, ConditionalEventArgs e) { e.Result = UserAnswer != "quit"; } } }
106
Kompilieren von XAML Workflows
5.4 Kompilieren von XAML Workflows Genau wie für WPF auch bietet Microsoft für WF entsprechende Unterstützung beim Kompilieren von XAML-Workflow-Dateien mithilfe von MSBuild an. Im Gegensatz zu WPF steht für WF jedoch auch ein eigenes, spezialisiertes Kommandozeilenprogramm zum Übersetzen von XAML-Workflow-Dateien zur Verfügung: wfc.exe.
5.5 Zusammenfassung 쐌 XAML wird in WF zum Spezifizieren von Workflows verwendet. Mit den XAML-Dateien können optional Code-behindDateien verwendet werden. 쐌 Obwohl auch der grafische Workflow Designer die Implementierung von Workflows ausschließlich in Code erlaubt, ist die Nutzung von XAML zu empfehlen. Nur das XAML-Format erlaubt Ihnen das Laden von Workflows zur Laufzeit. 쐌 Die Wurzelelemente für XAML Workflows sind SequentialWorkflowActivity oder System.Workflow.Activities.StateMachineWorkflowActivity. 쐌 Zum Übersetzen von XAML-Workflow-Dateien wird wie bei WPF auch MSBuild verwendet. Als Alternative steht das Kommandozeilenwerkzeug wfc.exe zur Verfügung.
schnell + kompakt
107
KAPITEL 6 XAML Tools 6.1
Einleitung
109
6.2
Systemvoraussetzungen
110
6.3
Tools
111
6.1 Einleitung XAML wurde nicht zuletzt unter Berücksichtigung von Tools konzipiert. Während Programmierer Logik in klassischen Programmiersprachen wie C# oder VB.NET entwickeln, sollen Designer oder Workflow-Experten eigene, meist grafisch orientierte Werkzeuge für ihre Arbeit erhalten. XAML stellt die Brücke zwischen beiden Welten dar. Es liegt in der Natur der Sache, dass die meisten Werkzeuge rund um XAML aus dem Hause Microsoft kommen. Im Zentrum stehen dabei Visual Studio und die Produkte der Expression-Produktlinie. Während Ersteres sich primär an Softwareentwickler richtet, sind Designer von Benutzerschnittstellen die primäre Zielgruppe des zweiten. Von Drittanbietern werden noch viele weitere Tools für XAML angeboten, die man grob in folgende Kategorien einteilen kann: 쐌 Grafische Designer zur Gestaltung von Benutzerschnittstellen in WPF-Applikationen (ähnlich wie Microsoft Visual Studio und Microsoft Expression Blend).
schnell + kompakt
109
6 – XAML Tools
쐌 WPF beinhaltet umfangreiche Unterstützung für Anwendungen, die 3D-Grafiken und Animationen enthalten. Dementsprechend sind am Markt 3D Tools zum Erstellen dreidimensionaler Objekte in XAML verfügbar. 쐌 Werkzeuge zum Konvertieren von XAML-Dateien. Mit ihnen können andere Formate wie Flash, svg, dxf und 3ds in XAML umgewandelt werden. Manche erlauben auch den umgekehrten Weg. Dieses Kapitel beginnt mit einer Behandlung der erforderlichen Systemvoraussetzungen, um Anwendungen mit XAML entwickeln und ausführen zu können. Der zweite Teil stellt die XAMLWerkzeuge aus dem Hause Microsoft näher vor.
6.2 Systemvoraussetzungen Um Applikationen mit XAML entwickeln zu können, gelten folgende Voraussetzungen: 쐌 Das .NET Framework 3.0 wird auf den Betriebssystemen Windows XP (ab Service Pack 2), Windows Server 2003 (ab Service Pack 1) und Windows Vista unterstützt. 쐌 .NET Framework 3.0 Runtime Components – diese sind nur für Windows XP und Windows 2003 Server erforderlich, in Windows Vista sind diese bereits enthalten 쐌 Microsoft Windows SDK – er enthält Dokumentation, Beispiele, Header Files, Libraries und Tools, um .NET-3.0-Applikationen in folgenden Technologien zu entwickeln: 쐌 WPF (Windows Presentation Foundation) 쐌 WCF (Windows Communication Foundation) 쐌 WF (Windows Workflow Foundation) 쐌 Windows Card Space
110
Tools
쐌 Visual Studio 2005 Extensions für das .NET Framework 3.0 (WCF & WPF) – hier sind nur CTPs erhältlich, da die Release Version erst für die nächste Version von Visual Studio erhältlich sein wird (Codename Orcas). In den kostenlosen Express Editions von Visual Studio funktioniert der grafische Designer für WPF zurzeit noch nicht 쐌 Visual Studio 2005 Extensions für das .NET Framework 3.0 (WF) – diese werden benötigt, um Workflows im Visual Studio grafisch designen zu können. Der grafische Workflow-Editor funktioniert in den Express Editions von Visual Studio nicht!
6.3 Tools Zum Arbeiten mit XAML stehen jetzt schon eine Reihe von Programmen zur Verfügung, die das Erstellen und Bearbeiten von XAML-Dateien vereinfachen. Diese Liste wird laufend länger. Sowohl von Microsoft als auch von Drittanbietern kann man in nächster Zeit neue oder weiterentwickelte Werkzeuge erwarten. Viele der Tools dienen zur Erstellung von Benutzerschnittstellen für WPF-Applikationen. In grafischen Editoren können die Komponenten der Benutzerschnittstelle angeordnet und gelayoutet werden. Allerdings ist man zurzeit immer noch gut beraten, wenn man auch ohne grafischen Editor XAML lesen und schreiben kann, da es bei den verschiedenen Tools im Detail immer wieder zu Problemen kommt und der grafische Editor das XAML entweder falsch oder gar nicht darstellt. Die Meldung „Whoops!“ in Visual Studio ist das beste Beispiel dafür. Sie wird hoffentlich mit der nächsten Version der Entwicklungsumgebung (Codename Orcas) verschwinden.
schnell + kompakt
111
6 – XAML Tools
Abb. 6.1: Einschränkungen in Visual Studio erfordern manchmal die manuelle Bearbeitung von XAML.
Microsoft Visual Studio Microsoft Visual Studio 2005 und in Zukunft bald Microsoft Visual Studio 2008 sind sicher für viele Entwickler die Tools der ersten Wahl. Hier sind alle Features, die man bei der täglichen Arbeit benötigt, in einem Programm vereint: Von der Verwaltung der Solutions über Source Control bis zu Performance Tools und Datenzugriff. Auch bei XAML kann man in Visual Studio mit Unterstützung für WPF- und WF-Applikationen rechnen. Für WPF steht ähnlich wie in ASP.NET ein grafischer Designer für XAML-Dateien zur Verfügung. Bei etwas komplizierterem XAML gibt dieser allerdings bald auf. In diesen Fällen bleibt nur das Umschalten von der Designansicht auf die XAML-Ansicht. Dort kann man in einem XML- oder Texteditor den XAML-Code manuell editieren. Mithilfe von Intellisense fällt das gar nicht so schwer. Für Visual Studio 2008 wurden Verbesserungen angekündigt, sowohl was den grafischen WPF-Designer als auch was die Intellisense-Funktionalität angeht. Im folgenden Bild wird das Beispiel für x:XData aus Kapitel 3 in der XAML-Ansicht dargestellt. Die Designansicht funktioniert bei diesem Beispiel in Visual Studio leider nicht:
112
Tools
Abb. 6.2: Microsoft Visual Studio – WPF
Auch für WF hat man im Visual Studio die Wahl zwischen einem grafischen Designer für Workflows und dem XML Editor. Es gibt allerdings keine Möglichkeit, zwischen den beiden Ansichten hin- und herzuschalten. Wenn man XAML-Workflow-Dateien, welche die Dateierweiterung .xoml haben, im Visual Studio öffnet, wird standardmäßig der grafische Designer geöffnet. Um den XML Editor zu öffnen, kann man die Datei mit „Open With…“ öffnen und dort „XML Editor“ auswählen:
Abb. 6.3: „Open With…“ zum Öffnen von .xoml Dateien
schnell + kompakt
113
6 – XAML Tools
Abb. 6.4: Auswahl „XML Editor“
Microsoft Expression Blend Microsoft Expression Blend gehört zur Familie der ExpressionProdukte. Neben Expression Blend stehen auch noch Expression Web zum Gestalten von ASP.NET-Applikationen, Expression Design zum Erstellen von Pixel- und Vektorgrafiken und Expression Media zum Verwalten von digitalen Medien zur Verfügung. Expression Blend bietet – ähnlich wie Visual Studio – eine Umgebung, in der .NET-3.0-Projekte und Solutions erstellt und bearbeitet werden können. Allerdings liegt hier der Fokus auf der grafischen Gestaltung der Applikation. Im Vergleich zu Visual Studio ist der grafische Editor von Expression Blend wesentlich ausgereifter, die meisten Aufgaben lassen sich in diesem Werkzeug in der Designansicht bewältigen. In der XML-Ansicht gibt es allerdings einen großen Nachteil im Vergleich zum Visual Studio: Intellisense steht nicht zur Verfügung.
114
Tools
Expression Blend und Visual Studio lassen sich ausgezeichnet kombinieren, da beide auf dieselben Projekte und Solutions zugreifen können. So können Programmierer und Designer mit unterschiedlichen, auf ihre Bedürfnisse abgestimmten Tools am selben Projekt arbeiten. Im folgenden Bild wird wie zuvor für Visual Studio das x:XDataBeispiel aus Kapitel 3 dargestellt. Im Gegensatz zu Visual Studio wird die ListBox mit den beiden Elementen in der Designansicht richtig dargestellt:
Abb. 6.5: Microsoft Expression Blend
XAMLPad XAMLPad wird mit dem Windows SDK ausgeliefert und ist ein einfaches Tool zur Darstellung von XAML. Es steht ein Texteditor zur Verfügung, um XAML zu bearbeiten. Im oberen Bereich der Applikation wird das Ergebnis bei jeder Änderung sofort angezeigt. Auf Fehler im XAML-Code wird in der Statuszeile hingewiesen.
schnell + kompakt
115
6 – XAML Tools
XAMLPad ist hauptsächlich beim Experimentieren mit XAML von Nutzen, da man schnell verschiedene Varianten ausprobieren kann, ohne jedes Mal ein Projekt kompilieren und ausführen zu müssen.
Abb. 6.6: XAMLPad
116
Stichwortverzeichnis .NET 3.0 18 .NET Framework 3.0 18, 110 .NET Framework 3.0 Runtime Components 110
A AppDomain 42 Application 42, 44 Application Definition File 42, 43 ApplicationDefinition 27 ASP.NET 19 AssemblyInfo.cs 52 Attached Properties 94, 95, 96, 97, 98 Avalon 19
B BAML 24, 27, 28 Benutzeroberfläche 14 Binding 90 Build 27 Build Items 26 Build-Prozess 28
C CanConvertFrom 84 CLR Namespace 48, 49, 51, 53 CLR Namespace Mapping 48, 49 Code-behind 19, 22, 30, 35, 57, 100, 105, 106 Connect 33 Content 27 Content Syntax 75 ContentProperty 76
schnell + kompakt
ConvertFrom 84 Current 42
D Data Binding 88, 90, 91 deklarative Programmiersprache 71 Dependency Properties 97, 98 Deserialisierung 15, 47 Designansicht 112, 114 Designer 14 DynamicResource 61, 89
E Expression Blend 114 Expression Design 114 Expression Web 114 Expression-Produktlinie 109 eXtensible Object Markup Language 99
F FindLogicalNode 39 FindResource 43 FrameworkContentElement 40 FrameworkElement 40, 61
G g.cs 27, 29 g.resources 28 GetPropertyName 95 GUI-Designer 14
I IComponentConnector 33 ItemsSource 57
117
Stichwortverzeichnis
K
R
Kompilieren 24, 27, 107 Konsolenanwendung 44 Konsolenapplikation 20
ResourceDictionary 61 Ressourcen 27, 28, 61, 88 RuntimeNameProperty 61
L
S
Laden zur Laufzeit 36 LogicalTreeHelper 39
Serialisierung 15, 47 Serialisierungsmechanismus 19 Serialisierungswerkzeug 15 SetPropertyName 96 SequentialWorkflowActivity 102 Singleton-Pattern 42 StartupUri 44 StateMachineWorkflowActivity 102 StaticResource 61, 89 Systemvoraussetzungen 110
M Main-Methode 23, 42 Markup Extension 57, 73, 86, 87, 88, 90, 91, 92 mc Ignorable 68 ProcessContent 68 mc:Ignorable 68 mc:ProcessContent 69 Microsoft Expression Blend 68, 109, 114 Microsoft Visual Studio 109, 112 Microsoft Windows SDK 18, 110 MSBuild 24, 25, 26, 29, 37, 107
T Tools 109, 111 TypeConverter 84, 86 Typkonvertierer 81, 82, 84, 86, 92 Typkonvertierung 73, 81, 83, 92
N Namespace 21, 22, 47, 53 Namespace Mapping 47, 54
U
O
V
Objektgraph 19, 71 Orcas 18
Visual Studio 2005 Extensions 111
P
W
Page 27 PageFunction 58, 59 partial 30 partiell 22 Präfix 50, 53, 54, 55 Projektdatei 25, 26 Property Element Syntax 77, 95 ProvideValue 92
WF 9, 99 wfc.exe 107 Windows Presentation Foundation 9 Windows Vista 9, 18 Windows Workflow Foundation 9, 99 WPF 9
118
URI 53
Stichwortverzeichnis
X x 48, 55 Array 55 Class 57 ClassModifier 57 Code 59 FieldModifier 60 Key 61 Name 60 Null 63 Shared 61 Static 63 Subclass 57 Type 64 TypeArguments 57 XData 65 x:Class 21, 36, 38, 106 x:ClassModifier 57 x:Code 29, 30 x:FieldModifier 60 x:Key 61, 88 x:Name 31, 32, 38 x:Shared 62
schnell + kompakt
x:Subclass 58 x:TypeArguments 58 x:XData 112 XAML Attribute 77 XAML Compiler 55 XAML Elemente 74 XAML Loader 55 XAML Namespace 47 XAMLPad 115 XamlReader 19, 36 xml lang 68 space 67 XML Namespace 21, 48, 51, 53 XML Namespace Mapping 48, 53 XML-Attribut 67 XmlDataProviders 65 xmlns 49, 53, 54 xmlns:x 54 XmlnsDefinitionAttribute 52 xoml 99, 113
119