Binding zdarzeń w WPF, MVVM cz.2

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.

Leave a Reply

Your email address will not be published.