MVVM jest bardzo popularny w środowisku programistów WPF i Silverlight gdyż umożliwia wykorzystanie najważniejszych atutów tego frameworku: bindowania, szablonów wyświetlania danych, komend (command), zachowań (behaviors).
Widok
W idealnym rozwiązaniu kod widoku zawiera tylko konstruktor wywołujący metodę InitializeComponent(). Można spotkać się jednak z kodem który nie jesteśmy w stanie wyrazić za pomocą XAMLa np. skomplikowaną animacją.Widok porozumiewa się z modelem za pomocą ViewModel. Komponenty widoku są bindowane np. za pomocą komend z odpowiednimi metodami w kodzie ViewModel. ViewModel jest połączony z View za pomocą właściwości DataContext którą udostępnia View.
ViewModel
Reprezentuje dane wysyłane do widoku - nie mając przy tym żadnej referencji do widoku. Widok za pomocą komend (command) oraz bindowania do właściwości odwołuje się do składników ViewModel. Takie rozwiązanie umożliwia testowanie ViewModelu odrębnie od części prezentacyjnej oraz modelu. Często w ViewModel-u wprowadza się dodatkową walidację czy też dodatkową logikę przedstawianą w warstwie prezentacji dla użytkownika.Jak rozpoznać czy daną operację umieścić w widoku czy też w ViewModel? Odpowiedź jest prosta:
View - wszystko co jest związane z prezentacją danych użytkownikowi
ViewModel - wszystko co związane z logiką i danymi
Model
Odpowiada w omawianym wzorcu za logikę biznesową aplikacji. Często jest tworzony przy użyciu obiektowych mapperów baz danych (np. nHibernate, Entity Framework, LINQ2SQL, WCF Services itp). Klasy w modelu najczęściej implementują interfejs INotifyPropertyChanged, który współpracuje z bindingiem wykorzystywanym w WPF oraz Silverlight. Korzystając z interfejsu IDataErrorInfo możemy zaimplementować sprawdzanie poprawności wprowadzanych danych przez użytkownika.
Patrząc z perspektywy View i ViewModel, Model nie ma referencji do żadnej z tych warstw - jest od nich całkowicie niezależny.
Bindowanie danych w WPF
WPF oferuje bardzo rozbudowany system bindowania danych do kontrolek wizualnych. Aby wykorzystać możliwości WPF należy wcześniej poznać niektóre przydatne interfejsy i klasy, dzięki którym będziemy mogli powiadomić użytkownika o zmianach w danych w niższych warstwach i odzwierciedlić je w widoku.INotifyPropertyChanged
Implementując ten interfejs umożliwiamy powiadomienie użytkownika o zmianie wartości pola. Przykład takiej implementacji:
Code:
Implementacja jest prosta i intuicyjna - dla każdej właściwości podczas zmiany jej wartości sprawdzamy czy ktoś nie "nasłuchuje". W przypadku gdy tak jest wysyłamy powiadomienie o tej sytuacji. Niestety rozwiązanie ma jedną wadę: mnożymy kod dwa zmieniając nazwę zmiennej musimy pamiętać o zmianie w argumencie PropertyChangedEventArgs - jest to często powód wielu ukrytych błędów które później mogą dać nam po palić. W Prism przedstawiono klasę po której możemy dziedziczyć i wykorzystać aby wysyłać powiadomienia korzystając z Lambda Expression:
Code:
ICollectionView
Jest to kolejny, ciekawy interfejs specjalnie przygotowany dla kolekcji obiektów które mają być wyświetlane w kontrolkach typu listbox, grdiview itp. Interfejs ten umożliwia m.in. filtrowanie, sortowanie i grupowanie danych. W WPF mamy do dyspozycji klasę która implementuje ten interfejs - ListCollectionView, a w Silverlight PagedCollectionView.Przykład:
Code:
Implementacja jak widać bardzo prosta: w klasie dodajemy pole typu naszego interfejsu i tworzymy klasę odpowiednią dla Silverlight bądź też WPF.
Polecenia (Commands)
Pominę w dalszej części polskie tłumaczenie tego słowa (angielskie wydaje mi się bardziej trafne). W największym uproszczeniu odpowiadają one eventą - np. Button_Click. W rzeczywistości stanowią bardziej abstrakcyjny byt, który nie jest zależny od interfejsu. Dzięki uzależnieniu commands od widoku, uzyskujemy kod który możemy wykorzystać w wielu miejscach naszego programu a dodatkowo jest on testowalny. Standardowa implementacja polega na zaimplementowaniu interfejsu ICommand:
Code:
Implementacja prosta, choć biblioteka Prism dostarcza jeszcze prostszą metodę na implementację command - DelegateCommand:
Code:
W metodzie podajemy co ma zrobić komenda oraz warunek determinujący kiedy ma zostać wykonana. Do komendy możemy przesłać także jakąś wartość - dlatego mamy możliwość stworzenia generycznej wersji DelegateCommand
Code:
Metoda Error zwraca wszystkie błędy, zaś indexer błąd dla konkretnego pola w klasie. Należy więc rozważyć implementację metod - nie mogą być zbyt czasochłonne by nie obciążyć zbytnio aplikacji. Każda zmiana właściwości spowoduje wywołanie walidacji tak więc kod walidacji powinien być w miarę prosty i szybki.Odwołanie w ViewModel do tak stworzonej walidacji wygląda następująco:
Code:
Przedstawione tutaj zagadnienia to podstawy MVVM. Polecam zgłębiać wiedzę w tym zakresie, gdyż raz poznając MVVM nie będziemy chcieli tworzyć codebehind.
Na wstępie jest mały błąd:
OdpowiedzUsuń"umożliwia oddzielenie warstwy prezentacji od warstwy prezentacji"
Pozdrawiam
Dzięki :)
Usuń"Klasy w modelu najczęściej implementują interfejs INotifyPropertyChanged, który współpracuje z bindingiem wykorzystywanym w WPF oraz Silverlight."
OdpowiedzUsuńNie modelu, tylko viewModel:
"ViewModel powinien implementować interfejs INotifyPropertyChanged"
http://msdn.microsoft.com/pl-pl/library/wprowadzenie-do-wzorca-projektowego-model-view-viewmodel-na-przykladzie-aplikacji-wpf.aspx
"The view model typically does not directly reference the view. It implements properties and commands to which the view can data bind. It notifies the view of any state changes via change notification events via the INotifyPropertyChanged and INotifyCollectionChanged interfaces."
http://msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspx