Szukaj na tym blogu

niedziela, 5 września 2010

Dziennik Zdrowej Diety – Wzorzec MVVM – Commanding

W poprzedni wpisie przedstawiłem podstawową koncepcję wzorca projektowego MVVM oraz pokazałem jak złączyć widok (View), będący graficzną reprezentacją naszej logiki biznesowej wraz modelem (Model) przy wykorzystaniu klasy pośredniczącej (ViewModel).
Brakującym elementem w przedstawionym przykładzie była implementacja zdarzeń jakie oferują nam kontrolki.
Z pomocą przychodzi nam wzorzec projektowy Command, który doczekał się swojej implenetacji w Silverlight 4.
Ogólna koncepcja działania polega na tym by udostępnić możliwość obsługi zdarzeń w ViewModel’u. W obecnej chwili zaimplementowane są tylko zdarzenia Click dla kontrolek będących implementacją/rozszerzeniem klasy ButtonBase oraz kontrolki HyperLink. Ograniczenia te jednak można przekroczyć poprzez własną implementację tego wzorca.
By wykorzystać tę nową funkcjonalność należy stworzyć klasę dziedziczącą po interfejsie ICommand (namespace System.Windows.Input)
namespace System.Windows.Input
{
    public interface ICommand
    {
        bool CanExecute(object parameter);
        void Execute(object parameter);
        event EventHandler CanExecuteChanged;
    }
}

CanExecute – metoda określająca status czy podpięta komenda może być wykonana,
Execute – komenda wywoływana w wyniku wywołania zdarzenia (np. wspomniany Click),
CanExecuteChanged – zdarzenie wywoływane w momencie zmiany statusu wywołania komency. Mechanizm bindujący kod XAML podpina się do tego zdarzenia tak więc możecie zauważyć dziwną zależność w postaci : nie podpinacie nic do zdarzenia, a jednak metody Execute oraz CanExecute oddziaływują na siebie.
Poniżej przedstawiam moją własną implementację wykorzystania wzorca Command.
public class MyCommand : ICommand
{
    private readonly Predicate<object> canExecute;
    private readonly Action<object> executeAction;
    
    public MyCommand(Action<object> executeAction, Predicate<object> canExecute)
    {
         this.canExecute = canExecute;
         this.executeAction = executeAction;
    }
   
    public bool CanExecute(object parameter)
    {
         if (canExecute != null)
             return canExecute(parameter);
    
         return true;    
    }
    
    public void Execute(object parameter)
    {
         if (executeAction != null)
             executeAction(parameter);
    }
    
    public event EventHandler CanExecuteChanged;
}

Klasa implemencująca interfejs ICommand korzysta z :
- delegatu Action<object> pozwalającego mi na oddelegowanie wykonania komendy do własnej implementacji metody Execute znajdującej się w ViewModel,
- delegatu Predicate<object> pozwalającego na oddelegowanie sprawdzenia testu możliwości wykonania komendy (CanExecute) do własnej implementacji metody znajdującej się w ViewModel.
Poniżej znajduje się część klasy ViewModel wykorzystującej powyższą komendę MyCommand
public class MyViewModel : INotifyPropertyChanged
{
    private readonly MyModel myModel;
    
    public MyViewModel()
    {
        myModel = new MyModel();
        ButtonCommandClick = new MyCommand(ButtonClickExecute, CanExecuteButtonClick);
    }
    
    private bool CanExecuteButtonClick(object obj)
    {
        if (!string.IsNullOrEmpty(Nickname) && Nickname == "Lukas")
            return false;
    
        return true;
    }
    
    private void ButtonClickExecute(object obj)
    {
        Description = String.Format("Witaj {0} ! To jest implementacja interfejsu ICommand", Nickname);
    }
    
    public string Nickname
    {
        get { return myModel.NickName ?? string.Empty; }
        set
        {
           if (myModel.ValidateNickName(value))
           {
               myModel.NickName = value;
               FirePropertyChanged("Nickname");
           }
        }
   }
    
   public ICommand ButtonCommandClick { get; set; }

By dopełnić dzieła wystarczy tylko zbindować właściwość Command klasy Button z właściwością ButtonCommandClick znajdującą się w naszy ViewModel’u.

<Button Content="Say Hello!" 
           Command="{Binding 
                     Source={StaticResource myViewModel},
                     Path=ButtonCommandClick}" />

Powyższy przykład jest najprostszą postacią wykorzystania możliwości jakie daje nam wzorzezc Command. Pozwala on jednak na postawienie pierwszych kroków przy zaznajamianiu się z możliwościami MVVM.
Przykładową aplikację możecie pobrać TUTAJ

2 komentarze:

  1. Duuuzy plus, jestes jedna z pierwszych osob ktore implemntujac beznadziejnie prosty interfejs ICommand nie pisza/mowia "dzieki JoshSmith" czy "za zgodza Josh Smith" publikuje implementacje ICommand.

    Bo to dla mnie porazka jest na tych wszystkich blogach. Tak jakby implemntacja ICommand byla "super" skomplikowana.

    OdpowiedzUsuń
  2. Dzięki

    Postaram się częściej zamieszczać własne implementacje (również bardziej skomplikowanych rzeczy).

    Pozdrawiam

    OdpowiedzUsuń