Routed Events sind gerade bei verschachtelten Steuerelementen enorm wichtig. Jedoch bringen Sie unter Silverlight einige Probleme mit sich. Zum einem sind Sie unter Silverlight auf folgende Events beschränkt:
KeyDown
KeyUp
GotFocus
LostFocus
MouseLeftButtonDown
MouseLeftButtonUp
MouseMove
MouseWheel
BindingValidationError
DragEnter
DragLeave
DragOver
Drop
Das wäre normal nicht wirklich schlimm, wenn man wie unter WPF seine eigenen Routed Events schreiben könnte. Leider kann man aktuell ab Silverlight 4 immer noch keine eigenen Routed Events schreiben. Das schränkt die Freiheiten bei bestimmten Anforderungen stark ein. Das nächste Problem wäre zudem die Abhängichkeit in der Code-Behind durch ein Event. Für die so eben genannten Probleme habe ich folgende Lösung parat: CallMethodAction-Behavior + UserControl-Facade.
Ein einfaches Beispiel: Die ListBox
Das folgende Beispiel verbildlicht ein gängiges Szenario unter Silverlight. Es soll auf ein Klick innerhalb der ListBox mit eigener Logik reagiert werden können. Dazu wurde bereits eine ListBox mit Beispieldaten und einem CallMethodAction-Behavior in Expression Blend 4 angelegt. Das CallMethodAction-Behavior soll auf das MouseLeftButtonDown-Event reagieren und die Methode ListBoxMouseLeftButtonDown im ViewModel aufrufen.
Das ist der folgende XAML-Code dazu:
1: ....
2: <ViewModel:ViewModelExample x:Key="ViewModel" />
3: </UserControl.Resources>
4:
5: <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource ViewModel}">
6: <ListBox x:Name="LbItems" Margin="36,43,39,58" ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding Source={StaticResource SampleDataSource}, Path=Collection}">
7: <i:Interaction.Triggers>
8: <i:EventTrigger EventName="MouseLeftButtonDown">
9: <ei:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="ListBoxMouseLeftButtonDown" />
10: </i:EventTrigger>
11: </i:Interaction.Triggers>
12: </ListBox>
13: </Grid>
14: ....
Listing.1 – XAML-Code mit ListBox und zugeordneten CallMethodAction-Behavior.
Das ViewModel hat zum Test folgenden Aufbau:
1: public class ViewModelExample
2: {
3: public void ListBoxMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
4: {
5: MessageBox.Show("Yeaahh... it´s work!");
6: }
7: }
Listing. 2 – Source-Code zum ViewModel.
Nun kann das folgende Beispiel auch gerne ohne MVVM-Pattern direkt mit Code-Behind-Code getestet werden. Egal welches der beiden Szenarios ausgeführt wird, man erhält das gleiche Ergebnis: Keine MessageBox! Außer man klickt ganz genau am Rand der ListBox. Es gebe jetzt zwar ein SelectionChanged-Event, das aber bei verschachtelten Items die innerhalb angeklickt werden ignoriert wird.
Woran kann das Problem liegen? Ein Bubbling-Event innerhalb der ListBox wird durch ein internes Grid-Element aufgefangen und gestoppt. Jetzt wäre es praktisch wenn man einen eigenen Routed Event dazu schreiben könnte.
Als Alternative gibt es folgende Lösung, man muss auf den allgemeinen Routed Event Manager implizit zugreifen und das bereits registrierte MouseLeftButtonDown-Event neu definieren. Dazu schreibt man sich einfach eine neue Klasse als Facade (Siehe Facade-Pattern). Die neue Klasse leitet von jeweiligen UserControl ab. Bei diesem Beispiel ist die ListBox dann die Basisklasse. Im Konstruktor greift man auf die abgeleitete Methode AddHandler zu. Diese bekommt dann das Event, die auszuführende Methode und eine Definition das der Handler weitergereicht werden darf. Die auszuführende Methode braucht auch nur auf dem Parameter der EventArgs zugreifen und gestattet dem Property Handler mittels false das es weitergereicht werden darf. Der Code dazu steht unter Listing 3.
1: public class ListBoxExtended : ListBox
2: {
3: public ListBoxExtended()
4: {
5: AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(ListBox_MouseLeftButtonDown), true);
6: }
7:
8: private void ListBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
9: {
10: e.Handled = false;
11: }
12: }
Listing. 3 – Source-Code zur UserControl-Facade ListBoxExtended.
Die UserControl-Facade muss nur noch mittels XML-Namespace im XAML bekannt gemacht werden und die alte ListBox wird durch die neue ListBox ersetzt. Der restlichen Deklarationen bleiben hierbei unverändert.
1: ....
2: <ViewModel:ViewModelExample x:Key="ViewModel" />
3: </UserControl.Resources>
4:
5: <Grid x:Name="LayoutRoot" Background="White" DataContext="{StaticResource ViewModel}">
6: <SilverlightBubblingExample:ListBoxExtended x:Name="LbItems" Margin="36,43,39,58" ItemTemplate="{StaticResource ItemTemplate}"
ItemsSource="{Binding Source={StaticResource SampleDataSource}, Path=Collection}">
7: <i:Interaction.Triggers>
8: <i:EventTrigger EventName="MouseLeftButtonDown">
9: <ei:CallMethodAction TargetObject="{Binding Mode=OneWay}" MethodName="ListBoxMouseLeftButtonDown" />
10: </i:EventTrigger>
11: </i:Interaction.Triggers>
12: </SilverlightBubblingExample:ListBoxExtended>
13: </Grid>
14: ....
Listing. 4 – XAML-Code mit der neuen ListBoxExtended.
Nun wird bei jedem klick auf der ListBox das Event wie erwünscht gefeuert.

Source-Code Download
SilverlightBubblingExample.zip (262,87 kb)
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitten "kicken" sie ihn.
