piątek, 1 października 2010

Podstawy MVVM Light Toolkit [część 2]

Drugiej części posta o MVVM Light Toolkit chciałem przedstawić bardzo ciekawe funkcjonalności, mianowicie: EventToCommand oraz obiekt Messenger. Postaram się przedstawić je na przykładzie, który będzie rozwinięciem aplikacji z poprzedniego posta.

EventToCommand
Jak już wcześniej wspominałem, w kontrolkach WPF'a komendy podpinane są domyślnie do jednego z góry przewidzianego zdarzenia, na przykład dla przycisku jest to odpowiednik OnClick. Nie ma możliwości podłączenia komendy do innych zdarzeń, jednak tu przychodzi z pomocą MVVM Light Toolkit. Nie jest to co prawda część samego wzorca MVVM, ale jeden z helperów, jednak jest to niesłychanie pomocna funkcjonalność. 
Dzięki niej istnieje możliwość podłączenia komend z obiektu ViewModel do dowolnych zdarzeń kontrolki bez angażowania do tego jakiegokolwiek Code Behind, czyli nadal żyjemy w zgodzie z założeniami MVVM. W moim przykładzie pod zdarzenie LostFocus podepnę sprawdzenie czy podana przez użytkownika liczba jest wylosowanym sekretnym numerem, dlatego też nie będzie konieczne klikanie w przycisk "Check guess", ale wystarczy na przykład naciśnięcie klawisza TAB, by przeskoczyć do następnej kontrolki. 
Najłatwiej wykonać połączenie między zdarzeniem i komendą z poziomu Expression Blend'a. Przed rozpoczęciem konieczne jest dodanie do projektu referencji do bilbioteki GalaSoft.MvvmLight.Extras.WPF4.dll. Następnie w zakładce Assets w grupie Behaviors należy odnaleźć pozycję EventToCommand i przeciągnąć ją na kontrolkę do której zdarzeń chcemy podpinać komendy. Pojawi nam się nowa pozycja w hierarchii obiektów, której właściwości możemy edytować. 
Z listy wybieramy nazwę zdarzenia, które chcemy wykorzystać (w naszym przypadku jest to LostFocus), a następnie podpinamy do niego komendę. Do właściwości Command poprzez binding podpinamy komendę CheckGuess z ExampleViewModel, natomiast do Command Parameter właściwość Text z kontrolki txbUserGuess, jednocześnie pamiętając o konieczności wykorzystania konwertera wartości z typu string do int, który został stworzony w poprzednim poście.
Oczywiście to samo można wykonać z poziomu kodu XAML, oto fragment odpowiedzialny z wykorzystanie mechanizmu EventToCommand, który został wygenerowany przez Blend'a.

 
  
   
  
 

Wykorzystuje on zaimportowane przestrzenie nazw:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4" 
Znaczenie powyższego kodu jest bardzo intuicyjne: do istniejącego TextBox'a odpowiedzialnego za wprowadzanie przez użytkownika liczby podpinamy nowy event trigger (o pozostałych dwóch rozdzajach triggerów można na przykład poczytać tu). Jest to trigger reagujący na zajście zdarzenia LostFocus i cała magia MVVM Light Toolkit polega na tym, że za pomocą jednego znacznika możemy podpiąć komendę do tego triggera dokładnie w taki sam sposób jak wcześniej podpięliśmy ją do przycisku.
Dodanie kolejnego triggera to po prostu dopisanie dodatkowego znacznia i:EventTrigger. Dokładniej o triggerach w WPF'ie można poczytać w artykule.

Messenger
Z założenia obiekt Messenger został dodany do MVVM Light Toolkit jako narzędzie do wszechstronnej komunikacji i to nie tylko pomiędzy obiektami ViewModel, ale między dowolnymi klasami. Nie jest on częścią wzorca MVVM, ale jednym z helperów stworzonych przez autora.
Idea opiera się na statycznym obiekcie Messenger, który udostępnia mechanizmy do wysyłania komunikatów oraz rejestrowania akcji reagujących na konkretne komunikaty. Możliwych scenariuszy pracy z obiektem Messenger jest naprawdę sporo. Możemy wysyłać komunikaty prostych typów do wszystkich zarejestrowanych odbiorców, stworzyć własne typy specyfikowanych komunikatów, wysyłać komunikaty tylko do konkretnych obiektów lub oznaczać komunikaty tokenami pozwalającymi na ich rozróżnienie. Ciekawą możliwością jest także wysyłanie komunikatów zawierających funkcję zwrotną (callback), co pozwala na wygodną dwustronną komunikację. Opcji jest sporo, ja do tej pory próbowałem tylko kilku najprostszych, jednak mam nadzieję, że podczas dalszej pracy znajdę zastosowania także dla pozostałych funkcjonalności.
W przykładowej aplikacji umieszczę tylko najprostszy przypadek, aby pokazać ogólną ideę działania. Mianowicie stworzony zostanie nowy obiekt ViewModel, który będzie jakby panelem opcji programu. Jedyną opcją będzie maksymalny zakres z jakiego można losować sekretną liczbę. Po zatwierdzeniu zmian zaktualizowany zostanie maksymalny zakres liczb, a dzięki bindingowi zostanie także odświerzona informacja o aktualnym maksymalnym zakresie.
Zaczynamy od stworzenia nowej kotrolki użytkownika jako nowego widoku w aplikacji i stworzenia prostego interfejsu użytkownika (pominąłem nieistotne na razie fragmenty): 

    
        
            
            
        
        
Następnie dodajemy odpowiadający temu widokowi obiekt ViewModel o nazwie OptionsViewModel i dziedziczący oczywiście po ViewModelBase. Dodajemy jedną prostą komendę SaveOptions, która zostanie podpięta do przycisku:
private RelayCommand<int> _saveCommand;

public RelayCommand<int> SaveOptions
{
 get {
  if (_saveCommand == null)
   _saveCommand = new RelayCommand<int>(
    x =>
    {
     Messenger.Default.Send<ChangeOptionsMessage>(new ChangeOptionsMessage(x));
    });
  
  return _saveCommand; }
}

Jej jedynym przeznaczeniem jest przesłać dalej otrzymany argument za pomocą obiektu Messenger. Argument, który będzie ona otrzymywać wynika z bindingu samego przycisku i jest to po prostu zawartość pola tekstowego z maksymalną liczbą, odpowiednio przekonwertowany na liczbę całkowitą:
Przesyłany komunikat jest typu ChangeOptionsMessage, jest to nasz własny typ komunikatu, który możemy dostosować tak aby zawierał takie informacje jakie są nam potrzebne w aktualnym scenariuszu. Dla wygody dziedziczy on po typie MessageBase z MVVM Light Toolkit i zawiera jedną dodatkową właściwość przechowującą maksymalną liczbę do wylosowania.
public class ChangeOptionsMessage : MessageBase
{
 public int MaxRange { get; set; }

 public ChangeOptionsMessage(int _maxRange)
 {
  MaxRange = _maxRange;
 }
}
Teraz nie pozostaje nam nic innego jak odebrać wysłany komunikat w głównym oknie. W konstruktorze ExampleViewModel rejestrujemy akcję reagującą na wysłanie komunikatu typu ChangeOptionsMessage:
Messenger.Default.Register(this, HandleChangeOptions);
oraz piszemy prostą funkcję obsługującą komunikat:
private void HandleChangeOptions(Messages.ChangeOptionsMessage m)
{
 this.MaxRange = m.MaxRange;
 initGuess();
}
Funkcja initGuess() rozpoczyna nową turę zgadywania z już zmienionym zakresem.
Źródła z tym przykładem do pobrania stąd.


Krótkie podsumowanie
MVVM Light Toolkit jest bardzo wygodnym narzędziem, nie przeraża mnogością funkcjonalności, jest prosty i intuicyjny w użyciu oraz wprowadza sporo udogodnień podczas pracy (jak na przykład gotowe szablony i snippety). Muszę jednak przyznać, że jestem nieco rozczarowany brakiem dokumentacji z prawdziego zdarzenia. Jedyna na czym można się opierać to przykłady aplikacji stworzone przez autora oraz społeczności użytkowników na forum projektu oraz na StackOverflow. To dość spora wada, ale mam nadzieję, że to tylko początkowe problemy i znikną one po opanowaniu toolkita.

Brak komentarzy:

Prześlij komentarz