Intro
In MVVM besteht die übliche Praxis darin, dass die Ansichten ihre ViewModels finden, indem sie aus einem DI-Container ( Dependency Injection ) aufgelöst werden. Dies geschieht automatisch, wenn der Container aufgefordert wird, eine Instanz der View-Klasse bereitzustellen (aufzulösen). Der Container fügt das ViewModel in die Ansicht ein, indem er einen Konstruktor der Ansicht aufruft, der einen ViewModel-Parameter akzeptiert. Dieses Schema wird als Inversion of Control (IoC) bezeichnet.
Vorteile von DI
Der Hauptvorteil hierbei ist, dass der Container zur Laufzeit mit Anweisungen zum Auflösen der von ihm angeforderten Typen konfiguriert werden kann. Dies ermöglicht eine bessere Testbarkeit, indem es angewiesen wird, die Typen (Ansichten und Ansichtsmodelle) aufzulösen, die wir verwenden, wenn unsere Anwendung tatsächlich ausgeführt wird, aber es anders anweist, wenn die Komponententests für die Anwendung ausgeführt werden. Im letzteren Fall verfügt die Anwendung nicht einmal über eine Benutzeroberfläche (sie wird nicht ausgeführt; nur die Tests sind ausgeführt), sodass der Container Mocks anstelle der "normalen" Typen auflöst, die bei der Ausführung der Anwendung verwendet werden.
Probleme aufgrund von DI
Bisher haben wir gesehen, dass der DI-Ansatz eine einfache Testbarkeit für die Anwendung ermöglicht, indem eine Abstraktionsschicht über die Erstellung von Anwendungskomponenten hinzugefügt wird. Bei diesem Ansatz gibt es ein Problem: Er funktioniert nicht gut mit visuellen Designern wie Microsoft Expression Blend.
Das Problem ist, dass sowohl in normalen Anwendungsläufen als auch in Unit-Testläufen jemand den Container mit Anweisungen zu den zu lösenden Typen einrichten muss . Außerdem muss jemand den Container bitten , die Ansichten aufzulösen, damit die ViewModels in sie eingefügt werden können.
In der Entwurfszeit wird jedoch kein Code von uns ausgeführt . Der Designer versucht, mithilfe der Reflexion Instanzen unserer Ansichten zu erstellen. Dies bedeutet:
- Wenn der View-Konstruktor eine ViewModel-Instanz benötigt, kann der Designer die View überhaupt nicht instanziieren - sie tritt auf kontrollierte Weise auf
- Wenn die Ansicht einen parameterlosen Konstruktor hat, wird die Ansicht instanziiert, aber dies
DataContext
wird null
so sein, dass wir im Designer eine "leere" Ansicht erhalten - was nicht sehr nützlich ist
Geben Sie ViewModelLocator ein
Der ViewModelLocator ist eine zusätzliche Abstraktion, die wie folgt verwendet wird:
- Die Ansicht selbst instanziiert einen ViewModelLocator als Teil ihrer Ressourcen und bindet seinen DataContext an die ViewModel-Eigenschaft des Locators
- Der Locator erkennt irgendwie, ob wir uns im Entwurfsmodus befinden
- Wenn sich der Locator nicht im Entwurfsmodus befindet, gibt er ein ViewModel zurück, das wie oben erläutert aus dem DI-Container aufgelöst wird
- Im Entwurfsmodus gibt der Locator ein festes "Dummy" -Ansichtsmodell mit seiner eigenen Logik zurück (denken Sie daran: In der Entwurfszeit befindet sich kein Container!). Dieses ViewModel wird normalerweise mit Dummy-Daten vorab gefüllt
Dies bedeutet natürlich, dass die Ansicht zunächst einen parameterlosen Konstruktor haben muss (andernfalls kann der Designer ihn nicht instanziieren).
Zusammenfassung
ViewModelLocator ist eine Redewendung, mit der Sie die Vorteile von DI in Ihrer MVVM-Anwendung beibehalten und gleichzeitig Ihren Code für visuelle Designer verwenden können. Dies wird manchmal als "Mischbarkeit" Ihrer Anwendung bezeichnet (siehe Ausdrucksmischung).
Nach dem obigen Verdauen findet ein praktisches Beispiel hier .
Schließlich ist die Verwendung von Datenvorlagen keine Alternative zur Verwendung von ViewModelLocator, sondern eine Alternative zur Verwendung expliziter View / ViewModel-Paare für Teile Ihrer Benutzeroberfläche. Oft müssen Sie möglicherweise keine Ansicht für ein ViewModel definieren, da Sie stattdessen eine Datenvorlage verwenden können.