23 Die WPF-Controls
Das WPF-Framework liefert zahlreiche Controls mit, von den einfachsten wie beispielsweise dem Button bis hin zu ziemlich komplexen. In diesem Buch auf alle detailliert einzugehen, würde den Rahmen sprengen. Ich möchte Ihnen aber die wichtigsten Controls vorstellen, damit Sie schon einen guten Überblick über die Möglichkeiten haben.
Wenn Sie zukünftig in die .NET-Dokumentation schauen, müssen Sie darauf achten, dass Sie das richtige Control auswählen. WPF integriert seine Controls in einem eigenen Framework (Presentationframework), obwohl sich die einzelnen Komponenten in vielen Einzelheiten ähneln.
23.1 Ereignisse programmieren 

Natürlich reagiert auch eine WPF-Anwendung nur dann, wenn Ereignisse ausgelöst werden. Grundsätzlich unterscheiden sich die Ereignisse nicht von denjenigen, die Sie bei den WinForms kennengelernt haben. Die Ereignishandler müssen zwei Parameter aufweisen: Der erste muss vom Typ Object sein, der zweite muss EventArgs oder davon abgeleitet sein.
Anders als bei der Entwicklung herkömmlicher Windows-Anwendungen bietet Ihnen das Eigenschaftsfenster allerdings keine Umschaltung zu einer Ereignisliste an. Um einen Ereignishandler bereitzustellen, bleibt Ihnen somit nur die Möglichkeit, die XAML-Datei zu verwenden.
Nehmen wir an, Sie möchten das Click-Ereignis einer Schaltfläche in der XAML-Datei programmieren. Sie können die IntelliSense-Unterstützung nutzen und daraus Click auswählen. Durch zweimaliges Drücken der
-Taste wird im XAML-Code das Ereignis an einen Ereignishandler gebunden, der in der Code-Behind-Datei erzeugt wird.
<Grid>
<Button Click="button1_Click" Height="30" Name="button1"
Margin="70,95,133,0">Button1</Button>
</Grid>Stellt eine Komponente ein Standardereignis bereit, können Sie den Ereignishandler auch mittels Doppelklick auf die Komponente bereitstellen. Das gilt zum Beispiel auch für den Button.
Die Verknüpfung zwischen Ereignis und Ereignishandler können Sie auch im Code dynamisch festlegen, z. B. so:
public partial class Window1 : Window { public Window1() { InitializeComponent(); button1.Click += new RoutedEventHandler(button1_Click); } void button1_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Im Click-Ereignishandler"); } }
23.1.1 Weiterleiten von Ereignissen 

Auch wenn es im Moment den Anschein hat, als ob die Ereignisbehandlung unter WPF identisch mit der bei den WinForms sei, gibt es einen sehr großen Unterschied. Nehmen wir an, Sie hätten eine WinForm, in der sich ein Button befindet. Für beide sei das Click-Ereignis codiert. Wenn Sie zur Laufzeit auf den Button klicken, wird das Ereignis der angeklickten Komponente, also das des Buttons ausgelöst. Die übergeordnete WinForm wird von diesem Ereignis nichts erfahren; sie reagiert nicht auf die Aktion.
Ganz anders ist das Verhalten, wenn Sie mit WPF-Komponenten arbeiten. Ereignisse, die in einer untergeordneten Komponente ausgelöst werden, können an die übergeordnete Komponente weitergeleitet werden. Das würde bedeuten, dass beim Klicken auf eine Schaltfläche auch das übergeordnete WPF-Window auf das Ereignis reagieren kann. Diese grundsätzliche Charakteristik ist sinnvoll, denn in der WPF können Komponenten beliebig ineinander verschachtelt werden.
Ereignisse in WPF, die weitergeleitet werden können, werden als Routed Events bezeichnet. Es gibt drei verschiedene Mechanismen, die die Abläufe beim Auslösen eines Ereignisses beschreiben.
- Die einfachste Variante gleicht der bei den WinForms. Dabei wird ein Ereignis nur von der Komponente verarbeitet, bei der das Ereignis aufgetreten ist.
- Beim Bubbling von Ereignissen wird der ausgelöste Event an die übergeordnete Komponente weitergereicht. Diese kann nun ebenfalls darauf reagieren.
- Beim Tunneling beginnt die Ereigniskette beim Wurzelelement. In der Regel dürfte es sich dabei um Window handeln. Dieses reicht das Ereignis immer weiter an das nächste untergeordnete Element, das seinerseits wieder das ihm untergeordnete Element informiert. Das geschieht so lange, bis der Auslöser erreicht ist. Die Tunneling-Events sind durch das Präfix Preview gekennzeichnet.
Wir wollen uns nun ansehen, wie die Ereignisweiterleitung funktioniert.
Event Bubbling
Das folgende Beispielprogramm definiert im Window ein StackPanel, dem mit einem Button und einer ListBox zwei Elemente zugeordnet sind. Der Button enthält ein untergeordnetes Element vom Typ Label. Die ListBox dient ausschließlich zum Anzeigen der Abläufe.
Event Bubbling bedeutet, dass ein von einer Komponente ausgelöstes Ereignis an seinen übergeordneten Container weitergereicht wird. In diesem Beispiel wollen wird das mit dem Ereignis MouseDown testen. Beachten Sie, dass dieses Ereignis sowohl für das Label als auch für den Button, das StackPanel und das Window definiert ist.
Den vorherigen Aussagen zufolge müsste ein Klick auf das Label bewirken, dass das Ereignis zuerst im Label und danach der Reihe nach im Button, dem StackPanel und zuletzt im Window verarbeitet wird. Wird nur der Button angeklickt, wird das Ereignis an das StackPanel und das Window weitergereicht.
Sehen wir uns zuerst den XAML-Code an:
// -----------------------------------------------------// Beispiel: ...\Kapitel 23\RoutedEvents
// ---------------------------------------------------------
<Window x:Class="RoutedEvents.Window1" ...
Title="Window1" Height="300" Width="300"
MouseDown="Event_MouseDown">
<StackPanel MouseDown="Event_MouseDown">
<Button Name="button1">
<Label Name="label1"
MouseDown="Event_MouseDown">Label1
</Label>
</Button>
<ListBox Name="listBox1"></ListBox>
</StackPanel>
</Window>Das Folgende ist der Code in der Code-Behind-Datei:
public partial class Window1 : Window { public Window1() { InitializeComponent(); button1.AddHandler(MouseDownEvent, new MouseButtonEventHandler(Event_MouseDown), true); } private void Event_MouseDown(object sender, MouseButtonEventArgs e) { if (sender is Button) listBox1.Items.Add("BUTTON"); else if (sender is Label) listBox1.Items.Add("LABEL"); else if (sender is StackPanel) listBox1.Items.Add("STACKPANEL"); else if (sender is Window1) listBox1.Items.Add("WINDOW"); e.Handled = false; } }
Abbildung 23.1 Ereignisweiterreichung (Bubbling)
Wie Sie in Abbildung 23.1 sehen, wird bei einem Klick auf das Label das Ereignis durch die Hierarchie bis an das Wurzelelement Windows weitergereicht.
Beachten Sie bitte, dass in der XAML-Datei das Ereignis MouseDown beim Button nicht registriert ist. Stattdessen erfolgt die Registrierung mit einer speziellen Notation, die durch die Methode AddHandler in der Code-Behind-Datei beschrieben wird:
button1.AddHandler(MouseDownEvent,
new MouseButtonEventHandler(Event_MouseDown), true);Dieser Methode übergeben Sie zuerst den Eventtyp und danach ein Delegate auf den Ereignishandler. Der boolesche Parameter gibt an, ob bereits behandelte Ereignisse weiterverarbeitet werden sollen. Tragen Sie hier false ein, wird – falls auf das Label geklickt wird – nur das Ereignis für das Label behandelt. Alle anderen Ereignisse werden nicht mehr registriert. Ein Klick auf die Oberfläche des Buttons löst hingegen die Ereignisse für Button, StackPanel und Window aus.
Registriert ein »Zwischenelement«, beispielsweise das StackPanel, das Ereignis nicht, wird nicht zwangsläufig das Weiterreichen des Ereignisses abgebrochen. Ein Ereignis sucht immer in der Hierarchie in Richtung der übergeordneten Komponenten nach einer Komponente, die das Ereignis auch behandeln möchte.
Event Tunneling
Um getunnelte Ereignisse zu erleben, können wir die Struktur des vorigen Beispiels beibehalten. Auch die Code-Behind-Datei bedarf keiner Änderung. Nur den XAML-Code müssen wir anpassen, indem wir das Ereignis MouseDown durch PreviewMouseDown ersetzen.
// -----------------------------------------------------// Beispiel: ...\Kapitel 23\TunneledEvents
// ---------------------------------------------------------
<Window x:Class="RoutedEvents.Window1" ...
Title="Window1" Height="300" Width="300"
PreviewMouseDown="Event_MouseDown">
<StackPanel PreviewMouseDown = "Event_MouseDown">
<Button Name="button1">
<Label Name="label1"
PreviewMouseDown="Event_MouseDown">Label1
</Label>
</Button>
<ListBox Name="listBox1"></ListBox>
</StackPanel>
</Window>Nun beginnen die Ereignisse im an oberster Stelle stehenden Window und pflanzen sich über StackPanel bis zum Auslöser fort.
Abbildung 23.2 Getunnelte Ereignisse
Die Ereignisweiterreichung abbrechen
Bei einem Ereignis kann die Ereigniskette jederzeit abgebrochen werden. Dazu dient die Eigenschaft Handled des EventArgs-Parameters. Um das zu testen, können Sie den Code des vorigen Beispiels wie folgt verändern:
private void Event_MouseDown(object sender, MouseButtonEventArgs e) {
if (sender is Button)
listBox1.Items.Add("BUTTON");
else if (sender is Label)
listBox1.Items.Add("LABEL");
else if (sender is StackPanel) {
e.Handled = true;
listBox1.Items.Add("STACKPANEL");
}
else if (sender is Window1)
listBox1.Items.Add("WINDOW");
}Da der Ereigniscode für das StackPanel die Ereigniskette abbricht, werden die Ereignisse von Button und Label nicht mehr behandelt.
Die Reihenfolge der Routed Events
Sie können in einem Window parallel sowohl Tunneled Events als auch Bubbled Events behandeln. Damit Sie auch diese Variante gesehen haben und erfahren, in welcher Reihenfolge dabei die Ereignisse auftreten, wollen wir sie in einer Anwendung testen.
// -----------------------------------------------------// Beispiel: ...\Kapitel 23\EventReihenfolge
// ---------------------------------------------------------
<Window ...
PreviewMouseLeftButtonDown ="Event_PreviewMouseDown"
MouseLeftButtonDown="Event_MouseDown"
>
<StackPanel
PreviewMouseLeftButtonDown = "Event_PreviewMouseDown"
MouseLeftButtonDown="Event_MouseDown">
<Label
MouseLeftButtonDown="Event_MouseDown"
PreviewMouseLeftButtonDown="Event_PreviewMouseDown"
Background="Red" Height="50">
<Image Source="Images\net.jpg"
MouseLeftButtonDown="Event_MouseDown"
PreviewMouseLeftButtonDown="Event_PreviewMouseDown"/>
</Label>
<ListBox Name="listBox1"></ListBox>
</StackPanel>
</Window>Dazu gehört folgender C#-Code in der Code-Behind-Datei:
private void Event_PreviewMouseDown(object sender, MouseButtonEventArgs e) { if (sender is Label) listBox1.Items.Add("Preview - LABEL"); else if (sender is Image) listBox1.Items.Add("Preview - IMAGE"); else if (sender is StackPanel) listBox1.Items.Add("Preview - STACKPANEL"); else if (sender is Window1) listBox1.Items.Add("Preview - WINDOW") } private void Event_MouseDown(object sender, MouseButtonEventArgs e) { if (sender is Label) listBox1.Items.Add("LABEL"); else if (sender is Image) listBox1.Items.Add("IMAGE"); else if (sender is StackPanel) listBox1.Items.Add("STACKPANEL"); else if (sender is Window1) listBox1.Items.Add("WINDOW"); }
In diesem Beispielprogramm werden die Abläufe ebenfalls wieder in einer ListBox registriert. Sie werden sehen, dass zuerst die getunnelten Events ausgelöst werden, beginnend bei Window. Nachdem das auslösende Element erreicht worden ist, setzt sich die Ereigniskette mit den Bubbled Events fort, die erst beim Erreichen des Wurzelelements Window ihr Ende finden.
Abbildung 23.3 Die Ereigniskette







Jetzt bestellen





