Zdarzenia, a komendy we wzorcu MVVM

Jeżeli zaczynasz swoją przygodę ze wzorcem MVVM to tak samo, jak ja prędzej czy później napotkasz na problem z obsługą zdarzeń. Teoretycznie powinny zastąpić nam je tzw. komendy. Jednak nie wszystkie kontrolki je obsługują(np. ComboBox) i nie dla wszystkich zdarzeń można je „ot, tak” wywołać. Jak zatem sobie z tym poradzić? Oczywiście trzeba powiązać dane zdarzenie z komendą 😉

Czego będziemy potrzebować?

Aby móc poradzić sobie z tym problemem, w miarę prosty sposób musimy posłużyć się frameworkiem ułatwiającym pisanie we wzorcu MVVM (w moim przypadku jest to MVVM Light, którego można zainstalować wpisując Install-Package MvvmLight w Package Manager Console). Dodatkowo będziemy potrzebowali biblioteki o nazwie System.Windows.Interactivity, która jest dołączona do MVVM Light. Jeżeli jednak korzystasz z innego frameworka to możesz ją podpiąć do projektu wpisując w tym samym narzędziu Install-Package System.Windows.Interactivity.WPF. Jednak na potrzeby tego „tutotiala” zalecam instalację MVVM Light – żeby było wiadomo, że wszystko będzie działać 😉

Framework zainstalowany, projekt gotowy. Co dalej?

Jeżeli projekt jest gotowy to możemy przejść do przykładów. Jak wiadomo standardowo zdarzenie Click buttona normalnie zastępuje się komendą:

 <Button Command="{Binding ClickCommand}" Content="Button" HorizontalAlignment="Left" Margin="258,289,0,0" VerticalAlignment="Top" Width="75"/>

Którą następnie wiąże się w ViewModel’u:

public ICommand ClickCommand { get; set; }

public MainViewModel()
{
    ClickCommand = new RelayCommand(ClickMethod);
}

private void ClickMethod()
{
    MessageBox.Show("Click!");
}

Co jednak, jeżeli chcemy wywołać jakąś akcję np. w przypadku najechania na przycisk? Normalnie posłużylibyśmy się zdarzeniem MouseMove. Jednak z poziomu ViewModelu to niemożliwe. Co w takim razie zrobić? Z pomocą przychodzi klasa System.Windows.Interactivity. Najpierw należy podpiąć ją pod naszego xaml’a, najlepiej zaraz za linijką x:Class=”(…).MainWindow”:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

Po tej operacji możemy już używać dobrodziejstw, jakie nam ta klasa zapewnia. Powiążmy, więc zdarzenie MouseMove z nową komendą:

<Button Content="Button" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseMove">
                    <i:InvokeCommandAction Command="{Binding MoveButton}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
</Button>
public ICommand MoveButton { get; set; }
public MainViewModel()
{
    MoveButton = new RelayCommand(MoveMethod);
}

private void MoveMethod()
{
    MessageBox.Show("Moved!");
}

Jak widać całość, jest bardzo prosta. Wystarczy dopisać w środku danej kontrolki powyższy kod oraz wskazać dla jakiego zdarzenia <i:EventTrigger EventName=”MouseMove„> chcemy przypisać komendę <i:InvokeCommandAction Command=”{Binding MoveButton}”/>

Powyższy przykład można stosować właściwie we wszystkich kontrolkach i w przypadku wszystkich zdarzeń. Również w przypadku samego okna. Dzięki temu możemy potraktować zdarzenie zamknięcia okna jako… komendę! Wystarczy pomiędzy znacznik <Window></Window> wrzucić:

<i:Interaction.Triggers>
        <i:EventTrigger EventName="Closing">
            <i:InvokeCommandAction Command="{Binding WindowClosing}"/>
        </i:EventTrigger>
</i:Interaction.Triggers>

Oraz następnie powiązać komendę z jakąś metodą w ViewModelu:

public ICommand WindowClosing { get; set; }
public MainViewModel()
{
    WindowClosing = new RelayCommand(ClosingMethod);
}

private void ClosingMethod()
{
    MessageBox.Show("Close...");
}

Rzecz bardzo ciekawa, tym bardziej że we wcześniejszym wpisie opisałem inny sposób poradzenia sobie z tym problemem.

Aktualizacja

W związku z sugestią @djfoxer pokażę jeszcze jak przesyłać parametry z event’ów do metod w ViewModel’u. Nie jest to właściwie nic trudnego pod warunkiem, że korzystamy z frameworka MVVMLight. Oto przykładowy kod w xaml’u:

<Button Content="Button" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <cmd:EventToCommand  Command="{Binding ClickButton}" PassEventArgsToCommand="True" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
</Button>

Gdzie cmd to:

xmlns:cmd ="http://www.galasoft.ch/mvvmlight"

Oraz ViewModel:

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows.Input;
using System;
using System.Windows;

namespace Commands.ViewModel
{
    public class MainViewModel : ViewModelBase
    {
        public ICommand ClickButton { get; set; }

        public MainViewModel()
        {
            ClickButton = new RelayCommand<object>(ClickMethod);
        }

        private void ClickMethod(object args)
        { 
            MessageBox.Show(args.ToString());
        }
    }
}

Cóż to byłoby chyba na tyle, jak zwykle zachęcam do zadawania pytań i zgłaszania uwag w komentarzach 😉

2,890 total views, 1 views today

3 przemyślenia nt. „Zdarzenia, a komendy we wzorcu MVVM

Możliwość komentowania jest wyłączona.