Intro
W MVVM zwykłą praktyką jest, aby widoki znajdowały swoje ViewModels, rozwiązując je z kontenera iniekcji zależności (DI). Dzieje się to automatycznie, gdy kontener jest proszony o dostarczenie (rozwiązanie) wystąpienia klasy View. Kontener wstrzykuje ViewModel do widoku, wywołując konstruktora widoku, który akceptuje parametr ViewModel; schemat ten nazywany jest odwróceniem kontroli (IoC).
Korzyści z DI
Główną zaletą jest to, że kontener można skonfigurować w czasie wykonywania za pomocą instrukcji dotyczących rozwiązywania typów, których od niego żądamy. Pozwala to na większą testowalność, instruując go, aby rozwiązać typy (widoki i modele widoków), których używamy, gdy nasza aplikacja faktycznie działa, ale instruując ją inaczej podczas wykonywania testów jednostkowych dla aplikacji. W tym drugim przypadku aplikacja nie będzie miała nawet interfejsu użytkownika (nie jest uruchomiona; tylko testy są), więc kontener będzie rozwiązywać makiety zamiast „normalnych” typów używanych podczas uruchamiania aplikacji.
Problemy wynikające z DI
Jak dotąd widzieliśmy, że podejście DI umożliwia łatwe testowanie aplikacji poprzez dodanie warstwy abstrakcji nad tworzeniem komponentów aplikacji. Jest jeden problem z tym podejściem: nie działa dobrze z projektantami wizualnymi, takimi jak Microsoft Expression Blend.
Problem polega na tym, że zarówno w przypadku normalnych uruchomień aplikacji, jak i testów jednostkowych, ktoś musi skonfigurować kontener z instrukcjami, jakie typy należy rozwiązać; dodatkowo ktoś musi poprosić kontener o rozpoznanie widoków, aby można było do nich wstrzyknąć ViewModels.
Jednak w czasie projektowania nasz kod nie działa . Projektant stara się wykorzystać odbicie do tworzenia instancji naszych Widoków, co oznacza, że:
- Jeśli konstruktor widoku wymaga wystąpienia ViewModel, projektant nie będzie w stanie w ogóle utworzyć wystąpienia widoku - wystąpi błąd w kontrolowany sposób
- Jeśli View ma konstruktor bez parametrów, wystąpi View, ale tak
DataContext
będzie, null
więc w projektancie otrzymamy "pusty" widok - co nie jest zbyt przydatne
Wprowadź ViewModelLocator
ViewModelLocator to dodatkowa abstrakcja używana w następujący sposób:
- Widok sam tworzy wystąpienie ViewModelLocator jako część jego zasobów i łączy dane DataContext z właściwością ViewModel lokalizatora
- Lokalizator w jakiś sposób wykrywa, czy jesteśmy w trybie projektowania
- Jeśli nie jest w trybie projektowania, lokalizator zwraca ViewModel, który jest rozpoznawany z kontenera DI, jak wyjaśniono powyżej
- W trybie projektowania lokalizator zwraca ustalony „fikcyjny” ViewModel przy użyciu własnej logiki (pamiętaj: w czasie projektowania nie ma kontenera!); ten ViewModel zazwyczaj jest wstępnie wypełniony danymi fikcyjnymi
Oczywiście oznacza to, że widok musi mieć na początku konstruktora bez parametrów (w przeciwnym razie projektant nie będzie mógł go utworzyć).
Podsumowanie
ViewModelLocator to idiom, który pozwala zachować zalety DI w aplikacji MVVM, jednocześnie umożliwiając dobrą współpracę kodu z projektantami wizualnymi. Nazywa się to czasem „mieszalnością” aplikacji (w odniesieniu do Expression Blend).
Po przetrawieniu powyższego zobacz praktyczny przykład tutaj .
Wreszcie, używanie szablonów danych nie jest alternatywą dla używania ViewModelLocator, ale alternatywą dla używania jawnych par View / ViewModel dla części interfejsu użytkownika. Często może się okazać, że nie ma potrzeby definiowania widoku dla ViewModel, ponieważ zamiast tego można użyć szablonu danych.
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
. Celem lokalizatora jest faktyczne włączenie DI w widokach, ponieważ WPF tak źle go udostępnia. Przykład: masz główne okno, które otwiera jakieś okno dialogowe. Aby rozwiązać DI w oknie dialogowym w zwykły sposób, musisz przekazać go jako zależność od okna głównego! Można tego uniknąć dzięki narzędziu View Locator.Przykładowa implementacja odpowiedzi @ Jona
Mam klasę lokalizatora modelu widoku. Każda właściwość będzie instancją modelu widoku, który zamierzam zaalokować w moim widoku. Mogę sprawdzić, czy kod działa w trybie projektowania, czy nie
DesignerProperties.GetIsInDesignMode
. Dzięki temu mogę używać makiety podczas projektowania i rzeczywistego obiektu podczas uruchamiania aplikacji.Aby z niego skorzystać, mogę dodać mój lokalizator do
App.xaml
zasobów:A następnie, aby połączyć widok (np. MainView.xaml) z modelem widoku:
źródło
this
zamiastdummy
?Nie rozumiem, dlaczego inne odpowiedzi na to pytanie otaczają Projektanta.
Celem narzędzia View Model Locator jest umożliwienie Twojemu Viewowi utworzenia tego wystąpienia (tak, View Model Locator = View First):
zamiast tego:
oświadczając, że:
Gdzie
ViewModelLocator
jest klasa, która odwołuje się do IoC i tak rozwiązujeMainWindowModel
eksponowaną właściwość.Nie ma to nic wspólnego z udostępnianiem modeli widoku Mock do widoku. Jeśli tego chcesz, po prostu zrób
View Model Locator jest opakowaniem wokół pewnego (dowolnego) kontenera Inversion of Control, takiego jak na przykład Unity.
Odnosić się do:
źródło