web-dev-qa-db-de.com

Was ist ein ViewModelLocator und was sind seine Vor- und Nachteile im Vergleich zu DataTemplates?

Kann mir jemand eine kurze Zusammenfassung geben, was ein ViewModelLocator ist, wie er funktioniert und welche Vor- und Nachteile er im Vergleich zu DataTemplates hat?

Ich habe versucht, Informationen auf Google zu finden, aber es scheint viele verschiedene Implementierungen zu geben, und es gibt keine klare Liste darüber, was es ist und welche Vor-/Nachteile es mit sich bringt.

109
Rachel

Intro

In MVVM besteht die übliche Praxis darin, dass die Views ihre ViewModels finden, indem sie aus einem Abhängigkeitsinjektion (DI) -Container aufgelöst werden. Dies geschieht automatisch, wenn der Container aufgefordert wird, eine Instanz der View-Klasse bereitzustellen (aufzulösen). Der Container injiziert das ViewModel in die View, indem er einen Konstruktor der View aufruft, der einen ViewModel-Parameter akzeptiert; dieses Schema heißt Umkehrung der Kontrolle (IoC).

Vorteile von DI

Der Hauptvorteil hierbei ist, dass der Container zur Laufzeit konfiguriert werden kann und Anweisungen zum Auflösen der von uns angeforderten Typen enthält. Dies ermöglicht eine bessere Testbarkeit, indem es angewiesen wird, die Typen (Views und ViewModels) aufzulösen, die wir verwenden, wenn unsere Anwendung tatsächlich ausgeführt wird, es jedoch anders anweist, wenn die Komponententests für die Anwendung ausgeführt werden. Im letzteren Fall hat die Anwendung nicht einmal eine Benutzeroberfläche (sie wird nicht ausgeführt; nur die Tests sind vorhanden), sodass der Container mocks anstelle der "normalen" Typen auflöst, die bei der Ausführung der Anwendung verwendet werden.

Probleme, die von DI herrühren

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: Es funktioniert nicht gut mit visuellen Designern wie Microsoft Expression Blend.

Das Problem ist, dass sowohl in normalen Anwendungsläufen als auch in Komponententestläufen jemand den Container mit Anweisungen zu den aufzulösenden Typen einrichten muss; Zusätzlich muss jemand fragen den Container auflösen, damit die ViewModels in ihn eingefügt werden können.

Jedoch in der Entwurfszeit läuft kein Code von uns. Der Designer versucht, mithilfe der Reflektion Instanzen unserer Ansichten zu erstellen. Dies bedeutet, dass:

  • Wenn für den View-Konstruktor eine ViewModel-Instanz erforderlich ist, kann der Designer die View überhaupt nicht instanziieren
  • Wenn die Ansicht einen parameterlosen Konstruktor hat, wird die Ansicht instanziiert, aber ihre DataContext wird null sein, so 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 folgendermaßen verwendet wird:

  • Die View selbst instanziiert einen ViewModelLocator als Teil seines resources und bindet seinen DataContext an die ViewModel-Eigenschaft des Locators
  • Der Locator erkennt irgendwie ob wir uns im Design-Modus befinden
  • Wenn sich der Locator nicht im Entwurfsmodus befindet, gibt er ein ViewModel zurück, das er wie oben erläutert aus dem DI-Container auflöst
  • Im Entwurfsmodus gibt der Locator ein festes "Dummy" -Ansichtsmodell mit eigener Logik zurück (denken Sie daran, dass sich in der Entwurfszeit kein Container befindet!). Dieses ViewModel wird normalerweise mit Dummy-Daten gefüllt geliefert

Dies bedeutet natürlich, dass die Ansicht zunächst einen parameterlosen Konstruktor haben muss (andernfalls kann der Designer sie nicht instanziieren).

Zusammenfassung

ViewModelLocator ist eine Redewendung, mit der Sie die Vorteile von DI in Ihrer MVVM-Anwendung beibehalten und gleichzeitig sicherstellen können, dass Ihr Code gut mit visuellen Designern zusammenarbeitet. Dies wird manchmal als "Mischbarkeit" Ihrer Anwendung bezeichnet (siehe Ausdrucksmischung).

Schauen Sie sich nach dem Aufschluss ein praktisches Beispiel an 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. Häufig müssen Sie möglicherweise keine Ansicht für ein ViewModel definieren, da Sie stattdessen eine Datenvorlage verwenden können.

190
Jon

Eine Beispielimplementierung von @ Jons Antwort

Ich habe eine Ansichtsmodell-Locator-Klasse. Jede Eigenschaft ist eine Instanz des Ansichtsmodells, das ich meiner Ansicht zuweisen werde. Ich kann mit DesignerProperties.GetIsInDesignMode Prüfen, ob der Code im Entwurfsmodus ausgeführt wird oder nicht. Auf diese Weise kann ich während des Entwurfs ein Modell und beim Ausführen der Anwendung das eigentliche Objekt verwenden.

public class ViewModelLocator
{
    private DependencyObject dummy = new DependencyObject();

    public IMainViewModel MainViewModel
    {
        get
        {
            if (IsInDesignMode())
            {
                return new MockMainViewModel();
            }

            return MyIoC.Container.GetExportedValue<IMainViewModel>();
        }
    }

    // returns true if editing .xaml file in VS for example
    private bool IsInDesignMode()
    {
        return DesignerProperties.GetIsInDesignMode(dummy);
    }
}

Und um es zu benutzen, kann ich meinen Locator zu App.xaml - Ressourcen hinzufügen:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

<Application.Resources>
    <core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

Und um Ihre Ansicht (zB MainView.xaml) mit Ihrem Ansichtsmodell zu verbinden:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
8
BrunoLM

Ich verstehe nicht, warum sich die anderen Antworten auf diese Frage um den Designer drehen.

Der View Model Locator dient dazu, dass Ihre View dies instanziiert (ja, View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

statt nur das:

public void MyWindowViewModel()
{
}

indem Sie dies erklären:

DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"

Wobei ViewModelLocator eine Klasse ist, die auf eine IoC verweist und auf diese Weise die MainWindowModel -Eigenschaft löst, die sie verfügbar macht.

Es hat nichts damit zu tun, Mock-Ansichtsmodelle für Ihre Ansicht bereitzustellen. Wenn Sie das wollen, tun Sie es einfach

d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"

Der View Model Locator ist ein Wrapper um einen (beliebigen) Inversion of Control-Container, z. B. Unity.

Beziehen auf:

2
Hristo Yankov