Przykład # 1: Mam widok wyświetlany w mojej aplikacji MVVM (użyjmy Silverlight do celów dyskusji) i klikam przycisk, który powinien zabrać mnie na nową stronę.
Przykład # 2: Ten sam widok ma inny przycisk, który po kliknięciu powinien otworzyć widok szczegółów w oknie potomnym (oknie dialogowym).
Wiemy, że obiekty Command będą widoczne przez nasz ViewModel powiązany z przyciskami metodami reagującymi na kliknięcie użytkownika. Ale co wtedy? Jak ukończyć akcję? Nawet jeśli korzystamy z tak zwanej usługi nawigacji, to co mówimy?
Mówiąc ściślej, w tradycyjnym modelu Najpierw przeglądaj (takim jak schematy nawigacyjne oparte na adresach URL, takie jak w Internecie lub we wbudowanej ramie nawigacyjnej SL), obiekty Command musiałyby wiedzieć, który Widok będzie wyświetlany następnie. To wydaje się przekraczać granicę, jeśli chodzi o rozdzielenie obaw promowanych przez ten wzorzec.
Z drugiej strony, jeśli przycisk nie był podłączony do obiektu Command i zachowywał się jak hiperłącze, reguły nawigacji można zdefiniować w znacznikach. Ale czy chcemy, aby Widoki kontrolowały przepływ aplikacji i czy nawigacja nie jest tylko innym rodzajem logiki biznesowej? (W niektórych przypadkach mogę powiedzieć „tak”, a w innych „nie”).
Dla mnie utopijną implementacją wzorca MVVM (i słyszałem, jak inni to twierdzą) byłoby połączenie ViewModel w taki sposób, aby aplikacja mogła działać bez głowy (tj. Bez widoków). Zapewnia to największą powierzchnię do testowania opartego na kodzie i sprawia, że widoki są prawdziwą skórką w aplikacji. A mój ViewModel nie powinien dbać o to, czy wyświetla się w oknie głównym, panelu pływającym czy oknie potomnym, prawda?
Zgodnie z tym podejściem do innego mechanizmu w środowisku wykonawczym należy „powiązanie” tego, co Widok powinien być wyświetlany dla każdego modelu ViewModel. Ale co, jeśli chcemy udostępnić widok wielu modelom ViewModels lub odwrotnie?
Więc biorąc pod uwagę potrzebę zarządzania relacją View-ViewModel, abyśmy wiedzieli, co wyświetlać, wraz z potrzebą nawigowania między widokami, w tym wyświetlania okien / dialogów potomnych, jak naprawdę możemy to osiągnąć we wzorcu MVVM?
źródło
DataTemplateSelector
, co zwykle używam w aplikacjach Silverlight. 2) Używałem tego wcześniej w wielu sytuacjach. Na przykład jeden model ViewModel może mieć wiele widoków lub jeden widok może być powiązany z wieloma modelami ViewModel. Przykład tego pierwszego znajduje się na stronie rachel53461.wordpress.com/2011/05/28/… . Później musisz tylko określić, że wiele map ViewModels mapuje do tego samego widoku w DataTemplateSelector.CurrentPages
4) Ponownie, był to tylko podstawowy przykład. W rzeczywistości wszystkie moje PageViewModels opierają się na jakiejś klasie bazowej, więc mój ShellViewModel działa tylko z klasą bazową lub interfejsem, takim jakIPageViewModel
. Naprawdę największym bałaganem mapowania jest DataTemplateSelector, który musiałby zmapować 40 widoków do 40 modeli ViewModels.Ze względu na zamknięcie pomyślałem, że opublikuję kierunek, w którym ostatecznie postanowiłem rozwiązać ten problem.
Pierwszą decyzją było skorzystanie z frameworku Silverlight Page Navigation dostarczonego natychmiast po wyjęciu z pudełka. Decyzja ta była oparta na kilku czynnikach, w tym na wiedzy, że ten rodzaj nawigacji jest przenoszony przez Microsoft do aplikacji Windows 8 Metro i jest zgodny z nawigacją w aplikacjach Phone 7.
Aby to zadziałało, następnie przyjrzałem się pracy ASP.NET MVC z nawigacją opartą na konwencjach. Kontrolka Frame używa identyfikatorów URI do zlokalizowania „strony” do wyświetlenia. Podobieństwo umożliwiło zastosowanie podobnego podejścia opartego na konwencjach w aplikacji Silverlight. Sztuka polegała na tym, aby wszystko działało razem w sposób MVVM.
Rozwiązaniem jest usługa nawigacji. Ta usługa udostępnia kilka metod, takich jak NavigateTo i Back, których ViewModels może użyć do zainicjowania zmiany strony. Gdy wymagana jest nowa strona, NavigationService wysyła CurrentPageChangedMessage za pomocą funkcji MVVMLight Messenger.
Widok zawierający kontrolkę Frame ma własny zestaw ViewModel jako DataContext, który nasłuchuje tej wiadomości. Po otrzymaniu nazwa nowego widoku jest poddawana funkcji mapowania, która stosuje nasze reguły konwencji i jest ustawiona na właściwość CurrentPage. Właściwość Source kontrolki Frame jest powiązana z właściwością CurrentPage. W rezultacie ustawienie właściwości aktualizuje Źródło i uruchamia nawigację.
Wracając do usługi nawigacji. Metoda NavigateTo akceptuje nazwę strony docelowej. Aby upewnić się, że ViewModels nie ma problemów z interfejsem użytkownika, używana nazwa to nazwa ViewModel do wyświetlenia. W rzeczywistości stworzyłem wyliczenie, które ma pole dla każdego nawigowalnego modelu ViewModel jako pomocnika i aby wyeliminować magiczne ciągi w całej aplikacji. Funkcja mapowania, o której wspomniałem powyżej, usuwa sufiks „ViewModel” z nazwy, dołącza „Page” do nazwy i ustawia pełną nazwę na „Views {Name} Page.xaml”.
Na przykład, aby przejść do widoku szczegółów klienta, mogę zadzwonić:
Wartość CustomerDetails to „CustomerDetailsViewModel”, która jest odwzorowana na „Views \ CustomerDetailsPage.xaml”.
Piękno tego podejścia polega na tym, że interfejs użytkownika jest całkowicie oddzielony od modeli ViewModels, ale mamy pełną obsługę nawigacji. Teraz mogę jednak ponownie przeskalować moją aplikację i za każdym razem, gdy uznaję to za stosowne, bez żadnych zmian w kodzie.
Mam nadzieję, że wyjaśnienie to pomaga.
źródło
Podobnie do tego, co powiedziała Rachel, moja aplikacja głównie MVVM ma
Presenter
do obsługi przełączania między oknami lub stronami. Rachel nazywa to anApplicationViewModel
, ale z mojego doświadczenia wynika, że na ogół musi robić coś więcej niż tylko być wiążącym celem (takim jak odbieranie wiadomości, tworzenie systemu Windows itp.), Więc technicznie bardziej przypomina tradycyjnyPresenter
lubController
.W mojej aplikacji
Presenter
zaczynam odCurrentViewModel
.Presenter
Przechwytuje całą komunikację międzyView
aViewModel
. Jedną z rzeczy, któreViewModel
można zrobić podczas interakcji, jest zwrócenie nowejViewModel
, co oznacza, żeWindow
należy wyświetlić nową stronę lub nową .Presenter
Dba o tworzenie lub zastępowanieView
dla nowychViewModel
i ustawienieDataContext
.Rezultatem akcji może być również to, że a
ViewModel
jest „zakończone”, w którym to przypadkuPresenter
wykrywa to i zamyka okno lub zrywa jeViewModel
ze stosu maszyny wirtualnej i wraca do wyświetlania poprzedniej strony.źródło
Presenter
prostu przykleja zwróconyViewModel
element do drzewa wizualnego, a WPF chwyta odpowiedniView
, łączyDataContext
i umieszcza go w drzewie wizualnym. Możesz to zrobić za pomocąDataTemplate
s, które deklarują, jakiViewModel
typ renderują, lub możesz utworzyć niestandardowy selektor szablonu danych. Jest to nadal w zakresie funkcji WPF.