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.
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
. Der Locator dient dazu, DI in den Ansichten tatsächlich zu aktivieren, da WPF die Bereitstellung so schlecht macht. Beispiel: Sie haben ein Hauptfenster, das ein Dialogfenster öffnet. Um das DI im Dialogfenster auf die übliche Weise zu lösen, müssten Sie es als Abhängigkeit vom Hauptfenster übergeben! Dies wird mit dem View Locator vermieden.Eine Beispielimplementierung von @ Jons Antwort
Ich habe eine View Model Locator-Klasse. Jede Eigenschaft wird eine Instanz des Ansichtsmodells sein, das ich meiner Ansicht zuweisen werde. Ich kann überprüfen, ob der Code im Entwurfsmodus ausgeführt wird oder nicht
DesignerProperties.GetIsInDesignMode
. Auf diese Weise kann ich während der Entwurfszeit ein Scheinmodell und beim Ausführen der Anwendung das reale Objekt verwenden.Und um es zu verwenden, kann ich meinen Locator zu
App.xaml
Ressourcen hinzufügen :Und dann, um Ihre Ansicht (z. B. MainView.xaml) mit Ihrem Ansichtsmodell zu verbinden:
quelle
this
anstelle vondummy
?Ich verstehe nicht, warum sich die anderen Antworten auf diese Frage um den Designer drehen.
Der Zweck des View Model Locator besteht darin, dass Ihre Ansicht dies instanziieren kann (ja, View Model Locator = View First):
statt nur dieses:
indem Sie dies erklären:
Wo
ViewModelLocator
ist die Klasse, die auf eine IoC verweist und auf diese Weise dieMainWindowModel
Eigenschaft löst, die sie verfügbar macht?Es hat nichts damit zu tun, Ihrer Ansicht Mock-Ansichtsmodelle bereitzustellen. Wenn Sie das wollen, tun Sie es einfach
Der View Model Locator ist ein Wrapper um einen (beliebigen) Inversion of Control-Container, z. B. Unity.
Beziehen auf:
quelle