Bei der Implementierung des Entwurfsmusters MVVM (Model-View-ViewModel) wird im ViewModel die Oberfläche durch Properties abgebildet. Dabei gelten für Aktionen, zum Beispiel die Logik beim Auslösen eines einfachen Buttons, dass das Handling mittels Commands umgesetzt wird. Das bringt einen enormen Vorteil mit sich, zum einen wäre das die 100% Entkopplung mittels Data-Binding. Allerdings erhält man zudem einen großen Nachteil. Commands beschränken sich nur auf die gängigen Standardaktionen. Dabei wird zum Beispiel nur beim betätigen eines Buttons das Command ausgelöst. Bei einer ListBox wäre dies wiederrum die Auswahl eines Items.
Diese Einschränkung kann unter WPF mittels EventTrigger elegant gelöst werden. Dennoch gibt es ein weiteres Problem, man erhält leider keine weiteren Informationen die im Normalfall von klassischen Events mittels EventArgs übergeben werden würden. Zudem gibt es unter Silverlight keine Trigger-Funktionalität.
In manchen Situationen können die Informationen zwar über CommandParameter übertragen werden, trotzdem tritt man immer wieder auf das fehlende Informationsproblem. Zudem möchte man Commands eventuell gar nicht auf die Standardaktionen binden. Viel eleganter wäre es, wenn man auf jeden beliebigen Event eines Controls ein Command auslösen könnte. Dazu müsste man dann vom ViewModel auf den EventManager zugreifen. Das benötigt leider etwas Source-Code und leider besteht eine gewisse Anhänglichkeit zu den jeweiligen Controls. Es müsste eine Lösung geben womit vom ViewModel entkoppelt auf Events Reagiert werden kann. Zudem wäre es wünschenswert, das kaum Source-Code dazu nötig ist.
Genau zu diesem Problem bietet Expression Blend 4 die Lösung mittels Behaviors. Behaviors sind fertige UI-Funktionalitäten die in gewisser Art als Snippet auf das gewünschte Control gezogen werden. Ab Expression Blend 4 bieten folgende Behavios die gewünschte Funktionalität:
| Name: | Funktion: |
| CallMethodAction | Löst bei einem bestimmten Event eine beliebige Methode eines Zielobjekts (z.B. ViewModel) aus. Dabei werden die jeweiligen EventArgs mit übertragen. |
| ChangePropertyAction | Bei einem Event wird ein Propertie eines beliebigen Zielobjekts (z.B. ViewModel), nach einer beliebigen Iteration ein Wert zugewiesen. |
| InvokeCommandBehavior | Erweitert ein Control durch ein Command, das auch auf jedem beliebigem Event ausgelöst werden kann. Es werden dabei keine EventArgs übertragen, nur CommandParameter . |
Ein Beispiel: Das SelectionChanged-Event einer Liste im ViewModel behandeln
Als einfaches Beispiel bietet sich das abfangen des SelectionChanged-Event einer Liste an. Dazu wird ein WPF oder Silverlight-Projekt mit einer ListBox benötigt. Die Beispieldaten können mit dem Data-Feature das es ab Expression Blend 3 gibt, zur Verfügung gestellt werden. Ein How-To dazu gibt es hier: Expression Blend 3 - Daten für Designer bereitstellen
Abb.1.1. – Eine Liste mit Beispieldaten
Der nächste Schritt ist das Anlegen des ViewModels. Dazu wird eine einfache Klasse mit dem Namen „ExampleViewModel“ erzeugt. Hier wird eine Methode und die dazugehörige SelectionChangedEventArgs-Signatur hinzugefügt.
1: using System;
2: using System.Windows.Controls;
3:
4: namespace WpfCallMethodExample.ViewModels
5: {
6: public class ExampleViewModel
7: {
8: public void SelectionChangedMethod(object sender, SelectionChangedEventArgs e)
9: {
10: throw new NotImplementedException();
11: }
12: }
13: }
Listing 1.1. – Source-Code des ViewModels „ExampleViewModel.cs“
Eine Instanz zum ViewModel wird von der View aus im Resourcen-Bereich der jeweiligen View erzeugt.
1: <Window
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
5: xmlns:local="clr-namespace:WpfCallMethodExample.ViewModels"
6: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="WpfCallMethodExample.MainWindow"
7: Title="MainWindow" Height="350" Width="525">
8: <Window.Resources>
9: <DataTemplate x:Key="ItemTemplate">
10: <StackPanel>
11: <TextBlock Text="{Binding Property1}"/>
12: </StackPanel>
13: </DataTemplate>
14: <local:ExampleViewModel x:Key="ViewModel" />
15: </Window.Resources>
16: <Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
17: <ListBox Margin="8,64,8,8" ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" />
18: <TextBlock Margin="8,8,8,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="52" FontSize="21.333"><Run Language="de-de" Text="CallMethodExample"/></TextBlock>
19: </Grid>
20: </Window>
Listing 1.2. – Eine Instanz zum ViewModel von der View erzeugen lassen.
Es folgt nun das Zuweisen eines Behaviors. Dabei wird im Assets-Fenster unter dem Reiterpunkt Behaviors, das Behavior CallMethodAction ausgewählt und mittels Drag & Drop auf die ListBox gezogen.
Abb.1.2. –Das CallMethodBehavior auf die ListBox ziehen
Um die Einstellungen des Behaviors zu Konfigurieren, muss im Objekt-Fenster das beliege Control aufgeklappt werden. Dahinter stehen dann alle definierten Behaviors. Nach der gewünschten Auswahl stehen die Einstellungen im Fenster Properties.
Abb.1.3 – Properties zum Behavior anzeigen
Bei den Properties zum CallMethodAction-Behavior wird nun unter Trigger das Event SelectionChanged ausgewählt. Dabei wird als Triggertype der EventTrigger belassen. Unter Common Properties muss als TargetObject ein Binding zum ViewModel erfolgen. Als MethodName wird dann der Methodenname manuell eingetragen. Ganz wichtig ist hierbei das auslasen der Klammern. Siehe Abb.1.4..
Abb.1.4. – CallMethodAction-Behavior konfigurieren
Der XAML-Code sollte nun wie unter Listing 1.3. aussehen. Dabei wurde das Projekt um die Microsoft.Expression.Interactions-Assembly erweitert. Diese bietet dann eine Erweiterung für Trigger-Funktionalitäten. Unter Silverlight wird somit die fehlende Trigger-Funktion zur Verfügung gestellt.
1: <Window
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
5: xmlns:local="clr-namespace:WpfCallMethodExample.ViewModels"
6: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="WpfCallMethodExample.MainWindow"
7: Title="MainWindow" Height="350" Width="525">
8: <Window.Resources>
9: <DataTemplate x:Key="ItemTemplate">
10: <StackPanel>
11: <TextBlock Text="{Binding Property1}"/>
12: </StackPanel>
13: </DataTemplate>
14: <local:ExampleViewModel x:Key="ViewModel" />
15: </Window.Resources>
16: <Grid DataContext="{Binding Source={StaticResource SampleDataSource}}">
17: <ListBox Margin="8,64,8,8" ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}">
18: <i:Interaction.Triggers>
19: <i:EventTrigger EventName="SelectionChanged">
20: <ei:CallMethodAction TargetObject="{Binding Mode=OneWay, Source={StaticResource ViewModel}}" MethodName="SelectionChangedMethod"/>
21: </i:EventTrigger>
22: </i:Interaction.Triggers>
23: </ListBox>
24: <TextBlock Margin="8,8,8,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="52" FontSize="21.333"><Run Language="de-de" Text="CallMethodExample"/></TextBlock>
25:
26: </Grid>
27: </Window>
Listing 1.3. – XAML-Code der View.
Zu guter Letzt wird nun die SelectionChangedMethod mit Logik erweitert. Dabei folgt nun ein MessageBox Aufruf mit den ausgewählten Datensatz. Wobei bemerkt werden muss, das ein MessageBox Aufruf in der Regel nicht vom ViewModel aus geschieht.
1: using System.Windows;
2: using System.Windows.Controls;
3: using Expression.Blend.SampleData.SampleDataSource;
4:
5: namespace WpfCallMethodExample.ViewModels
6: {
7: public class ExampleViewModel
8: {
9: public void SelectionChangedMethod(object sender, SelectionChangedEventArgs e)
10: {
11: Item selectedItem = (Item)e.AddedItems[0];
12: MessageBox.Show(selectedItem.Property1);
13: }
14: }
15: }
Listing 1.4. – Source-Code der Implementierung von SelectionChangedMethod.
Beim Ausführen der Anwendung, erscheint nun bei der Auswahl eines Items die jeweilige MessageBox.
Abb.1.5. – Die Auswahl eines Items wird in einer MessageBox dargestellt.
Fazit
Behaviors sind immer wichtiger geworden. Ich selbst sehe Sie auch als eine Art Aspektorientierte Programmierung für das Frontend. Es muss kein Code geschrieben werden, es kann zudem immer wieder verwendet werden und die Übersichtlichkeit des eigenen Codes bleibt gegeben. Die neuen Behaviors in Expression Blend 4 bringen eine große Abhilfe bei den Stolpersteinen der heutigen Entwicklung mit sich. Zum Beispiel bei der Entwicklung mit dem MVVM.
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitten "kicken" sie ihn.
