Was ist ein ViewModelLocator und welche Vor- und Nachteile hat er im Vergleich zu DataTemplates?

112

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 bei Google zu finden, aber es scheint viele verschiedene Implementierungen davon zu geben und keine genaue Liste darüber, was es ist und welche Vor- und Nachteile es hat.

Rachel
quelle

Antworten:

203

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 DataContextwird nullso 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.

Jon
quelle
4
+1 für eine gute Erklärung. Können Sie die Ansicht und ihre Ressourcen weiter ausbauen? Mit Ressourcen meinen Sie die Eigenschaften der Ansicht? Oder? Haben Sie einen Link zu einem konkreten Beispiel für dieses Muster?
Metro Schlumpf
@MetroSmurf: Ihr Link befindet sich im Abschnitt Zusammenfassung.
Jon
1
Vielen Dank. Gibt es Einschränkungen bei der Verwendung eines ViewModelLocator? Ich hatte einige Bedenken hinsichtlich der Tatsache, dass auf eine statische Ressource verwiesen wurde. Können ViewModels zur Laufzeit dynamisch erstellt werden? Und ist viel zusätzlicher Code erforderlich, um einen anzuschließen?
Rachel
@Rachel: Der gleiche Link in der Zusammenfassung sollte diese Fragen mit praktischen Beispielen beantworten.
Jon
2
Extrem irreführende Antwort. Der Hauptzweck von View Model Locator besteht nicht darin, dem Designer Dummy-Daten bereitzustellen. Sie können dies einfach durch Angabe tun 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.
Hyankov
10

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.

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 verwenden, kann ich meinen Locator zu App.xamlRessourcen hinzufügen :

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

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

Und dann, um Ihre Ansicht (z. B. MainView.xaml) mit Ihrem Ansichtsmodell zu verbinden:

<Window ...
  DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
BrunoLM
quelle
Gibt es einen Unterschied bei der Verwendung thisanstelle von dummy?
Sebastian Xawery Wiśniowiecki
5

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):

public void MyWindowViewModel(IService someService)
{
}

statt nur dieses:

public void MyWindowViewModel()
{
}

indem Sie dies erklären:

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

Wo ViewModelLocatorist die Klasse, die auf eine IoC verweist und auf diese Weise die MainWindowModelEigenschaft 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

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:

Hyankov
quelle
Für einen Ansichtsmodell-Locator ist kein Container erforderlich. Der Benutzer entscheidet, wie das Ansichtsmodell über die Konfiguration aufgelöst wird, und Sie können einen Container verwenden oder einfach selbst einen Typ neu erstellen. So können Sie beispielsweise die Position eines Ansichtsmodells auf Konventionsbasis festlegen, anstatt alle Ihre Ansichten und Ansichtsmodelle in einem Container vorab zu registrieren.
Chris Bordeman
Sie haben Recht, wenn Sie sagen " Ich verstehe nicht, warum sich die anderen Antworten [...] um den Designer wickeln " +1, aber der Zweck des Locators besteht darin, jegliches Wissen darüber, wie das Ansichtsmodell ist, aus der Ansicht zu entfernen erstellt, wodurch die Ansicht von dieser Instanziierung unabhängig wird und diese dem Locator überlassen bleibt. Der Locator kann verschiedene Varianten des Ansichtsmodells bereitstellen, möglicherweise einige benutzerdefinierte, die über Plugins hinzugefügt werden, die der Locator verwaltet (und eine bestimmte für die Entwurfszeit). Die Ansicht ist frei von diesem Vorgang, bei dem die richtige Version des Ansichtsmodells gefunden wird. Dies ist in der Tat gut für SoC.
Minuten