Kilka miesięcy temu pisałem o attached behaviour jako sposobie na wiązanie zdarzeń do komend. Sposób całkowicie poprawny i wygodny w użyciu. Dzisiaj jednak chciałbym przedstawić nieco prostsze rozwiązanie z wykorzystaniem bibliotek z Expression Blend SDK.
Jeśli jeszcze nie posiadacie SDK możecie je ściągnąć ze strony Microsoft’u. Do projektu dołączamy bibliotekę System.Windows.Interactivity (SDK). Następnie w pliku XAML spróbujmy powiązać zdarzenie MouseMove z komendą ShowMsgCmd:
<Button> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseMove"> <i:InvokeCommandAction Command="{Binding ShowMsgCmd}"/> </i:EventTrigger> </i:Interaction.Triggers> </Button>
Namespace “i” powinien być skojarzony z biblioteką System.Windows.Interactivity:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Jak widać Expression Blend dostarcza pewnie rozszerzenie dla wyzwalaczy. Możemy w ten sposób wykonać własny kod w momencie odpalenia wyzwalacza (który jest uruchamiany w tym przypadku na podstawie zdarzenia). W EventTrigger definiujemy własne akcje – klasy, które dziedziczą po TriggerAction. InvokeCommandAction dziedziczy po TriggerAction i wywołuje wskazaną komendę. Możemy nawet podać jakiś argument do komendy:
<Button> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseMove"> <i:InvokeCommandAction Command="{Binding ShowMsgCmd}" CommandParameter="argument"/> </i:EventTrigger> </i:Interaction.Triggers> </Button>
Niestety często potrzebować będziemy argumentów zdarzenia (EventArgs). Aktualna implementacja InvokeCommandAction nie pozwala na to. Aby móc w obsłudze (Execute) korzystać z EventArgs musimy zaimplementować własny TriggerAction:
public sealed class CustomCommandInvoker : TriggerAction<DependencyObject> { public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(CustomCommandInvoker), null); public ICommand Command { get { return (ICommand)this.GetValue(CommandProperty); } set { this.SetValue(CommandProperty, value); } } protected override void Invoke(object parameter) { if (this.AssociatedObject != null) { ICommand command = Command; if ((command != null) && command.CanExecute(parameter)) { command.Execute(parameter); } } } }
Jak widać, implementacja nie jest skomplikowana. Zastosowanie wygląda bardzo podobnie:
<Button> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseMove"> <customInvokers:CustomCommandInvoker Command="{Binding ShowMsgCmd}"/> </i:EventTrigger> </i:Interaction.Triggers> </Button>
Następnie w obsłudze komendy możemy wykorzystać EventArgs dostarczane przez zdarzenia:
private void ShowMsg(object args) { MouseEventArgs mouseArgs = (MouseEventArgs)args; MessageBox.Show(string.Format("Hello world! {0}:{1}", mouseArgs.GetPosition(this).X, mouseArgs.GetPosition(this).Y)); }
Na zakończenie warto dodać, że biblioteka z ExpressionBlend nie jest “ciężka” – zawiera dosłownie kilka klas. Dodając ją do projektu nie musimy się martwić o to, że dodamy wiele niepotrzebnych innych klas.