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.