Analizując wcześniejsze przykłady pokazujące użycie DelegateCommand można odnieść wrażenie, że mimo tego iż są one bardzo użyteczne, ich użycie jest niestety bardzo ograniczone.
Jedynie niektóre komponenty WPF-a i Silverlight-a mają właściwość Command do której można zbindować nasz obiekt DelegateCommand.
Ponadto właściwość ta reaguje jedynie na wybrane zahardcodowane w kontrolce zdarzenie (zdarzenie Click).
Co w przypadku gdybyśmy chcieli zareagować np. na zdarzenie MouseMove za pomocą komendy? Nasz problem możemy rozwiązać poprzez:
InteractivityInteractionsCommandBehaviors
W tym poście zostanie przedstawione rozwiązanie trzecie - CommandBehaviors
Pierwszą rzeczą jaką należy zrobić w celu stworzenia komendy reagującej na inne zdarzenie niż Click jest stworzenie klasy dziedziczącej po CommandBehaviorBase.
Załóżmy, że zrobimy komendę reagującą na zdarzenie MouseMove
public class MouseMoveCommandBehavior : CommandBehaviorBase<Control>{ private MouseOutCommandBehavior(Control control) : base(control) { control.MouseMove += (sender, args) => { this.ExecuteCommand(); }; }}Piersza część już za nami, ale pojawia się teraz pytanie jak z tego skorzystać.
Żeby mieć możliwość użycia powyżej klasy wykorzytamy attached dependency property.
Musimy sobie stworzyć pomocniczą statyczną klasę, w której to zarejestrujemy nasze właściwości.
Zacznijmy od zarejestrowania dependency property o nazwie Command - do tego obiektu będziemy bindowali komendy z ViewModelu (komendy reagujące na zdarzenie MouseMove)
public static class MouseMove{ public static ICommand GetCommand(DependencyObject obj) { return (ICommand)obj.GetValue(CommandProperty); }
public static void SetCommand(DependencyObject obj, ICommand value) { obj.SetValue(CommandProperty, value); }
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(MouseMove), new PropertyMetadata(OnSetCustomCommandCallback));}zauważmy, że podłączyliśmy event handlera reagującego na zmianę naszej nowo stworzonej właściwości Command.
Funkcja OnSetCustomCommandCallback wygląda następująco:
private static void OnSetCustomCommandCallback(DependencyObject dep, DependencyPropertyChangedEventArgs e){ var control = dep as Control; if (control != null) { MouseMoveCommandBehavior behavior = GetorCreateMouseMoveBehavior(control); behavior.Command = e.NewValue as ICommand; }}Funkcja ta po prostu w razie potrzeby tworzy nowy obiekt typu MouseMoveCommandBehavior (gdy ktoś zbinduje komende) i dopina go do naszej kontrolki.
Pomocnicza funkcja GetorCreateMouseMoveBehavior wygląda tak:
private static MouseMoveCommandBehavior GetorCreateMouseMoveBehavior(Control listBox){ var behavior = listBox.GetValue(MouseMoveBehaviorProperty) as MouseMoveCommandBehavior; if (behavior == null) { behavior = new MouseMoveCommandBehavior(listBox); listBox.SetValue(MouseMoveBehaviorProperty, behavior); } return behavior;}Obiekt rejestrowany poprzez funkcję GetorCreateMouseMoveBehavior jest dopinany do kolejnej attached property, która również została zadeklarowana w tym pliku
public static MouseMoveCommandBehavior GetMouseMoveBehavior(DependencyObject obj){ return (MouseMoveCommandBehavior)obj.GetValue(MouseMoveBehaviorProperty);}
public static void SetMouseMoveBehavior(DependencyObject obj, MouseMoveCommandBehavior value){ obj.SetValue(MouseMoveBehaviorProperty, value);}
public static readonly DependencyProperty MouseMoveBehaviorProperty = DependencyProperty.RegisterAttached("MouseMoveBehavior", typeof(MouseMoveCommandBehavior), typeof(MouseMove), null );Mając zdefiniowane wszystkie powyższe właściwości możemy już wykorzystać naszą komendę.
Jednakże przydałoby się dodać jeszcze jedną właściwość (opcjonalnie), mianowicie właściwość CommandParameters
public static object GetCommandParametr(DependencyObject obj){ return (object)obj.GetValue(CommandParametrProperty);}
public static void SetCommandParametr(DependencyObject obj, object value){ obj.SetValue(CommandParametrProperty, value);}
public static readonly DependencyProperty CommandParametrProperty = DependencyProperty.RegisterAttached("CommandParametr", typeof(object), typeof(MouseMove), new PropertyMetadata(OnSetCustomCommandParameterCallback));`W tym propertisie również reagujemy na przypisnie obiektu.
Handler OnSetCustomCommandParameterCallback wygląda następująco
private static void OnSetCustomCommandParameterCallback(DependencyObject dep,DependencyPropertyChangedEventArgs e){ var listBox = dep as Control; if (listBox != null) { MouseMoveCommandBehavior behavior = GetorCreateMouseMoveBehavior(listBox); ; behavior.CommandParameter = e.NewValue; }}Mając już zdefiniowane wszystkie niezbędne właściwości możemy użyć ich w XAML-u.
Po pierwsze musimy dodać alias do namespaca gdzie znajduje się nasza statycza klasa MouseMove.
W moim przypadku wygląda to następująco:
xmlns:AttachedCommands="clr-namespace:MainModule.Commands"Następnie bindujemy jakąś kontrolkę do naszego attached property Command.
Wygląda to w następujący sposób:
<Button Name="txtButton" Grid.Column="2" AttachedCommands:MouseMove.Command="{Binding SomeCommandFromViewModel}"></Button>Od teraz za każdym razem jak najedziemy na txtButton wywoła się komenda SomeCommandFromViewModel.
Podsumowując trzeba się całkiem sporo napisać żeby zmusić do działania CommandBehaviors .
W następnym poście przedstawię dwa sposoby na osiągnięcie tego samego efektu mniejszym kosztem.