Cos'è un ViewModelLocator e quali sono i suoi pro / contro rispetto a DataTemplates?


112

Qualcuno può fornirmi un breve riepilogo di cosa sia un ViewModelLocator, come funziona e quali sono i pro / contro per il suo utilizzo rispetto a DataTemplates?

Ho provato a trovare informazioni su Google ma sembra che ci siano molte diverse implementazioni e nessun elenco rigoroso su cosa sia e sui pro / contro del suo utilizzo.

Risposte:


204

Intro

In MVVM la pratica abituale consiste nel fare in modo che le viste trovino i propri ViewModel risolvendoli da un contenitore di inserimento delle dipendenze (DI). Ciò avviene automaticamente quando al contenitore viene chiesto di fornire (risolvere) un'istanza della classe View. Il contenitore inietta il ViewModel nella vista chiamando un costruttore della vista che accetta un parametro ViewModel; questo schema è chiamato inversione di controllo (IoC).

Vantaggi di DI

Il vantaggio principale qui è che il contenitore può essere configurato in fase di esecuzione con istruzioni su come risolvere i tipi che richiediamo da esso. Ciò consente una maggiore testabilità istruendolo a risolvere i tipi (Views e ViewModels) che usiamo quando la nostra applicazione viene effettivamente eseguita, ma istruendola in modo diverso quando si eseguono gli unit test per l'applicazione. In quest'ultimo caso l'applicazione non avrà nemmeno un'interfaccia utente (non è in esecuzione; solo i test sono) quindi il contenitore risolverà i mock al posto dei tipi "normali" utilizzati quando l'applicazione viene eseguita.

Problemi derivanti da DI

Finora abbiamo visto che l'approccio DI consente una facile testabilità dell'applicazione aggiungendo uno strato di astrazione sulla creazione dei componenti dell'applicazione. C'è un problema con questo approccio: non funziona bene con i designer visivi come Microsoft Expression Blend.

Il problema è che sia nelle normali esecuzioni dell'applicazione che nelle esecuzioni di unit test, qualcuno deve impostare il container con le istruzioni sui tipi da risolvere; inoltre, qualcuno deve chiedere al contenitore di risolvere le viste in modo che i ViewModels possano essere iniettati in esse.

Tuttavia, in fase di progettazione non è in esecuzione alcun nostro codice . Il designer tenta di utilizzare la riflessione per creare istanze delle nostre viste, il che significa che:

  • Se il costruttore della vista richiede un'istanza ViewModel, il progettista non sarà in grado di creare un'istanza della vista - si verificherà un errore in modo controllato
  • Se la vista ha un costruttore senza parametri la vista sarà istanziata, ma DataContextsarà nullcosì avremo una vista "vuota" nel designer - che non è molto utile

Immettere ViewModelLocator

ViewModelLocator è un'astrazione aggiuntiva usata in questo modo:

  • La vista stessa crea un'istanza di ViewModelLocator come parte delle sue risorse e associa il suo DataContext alla proprietà ViewModel del localizzatore
  • Il localizzatore in qualche modo rileva se siamo in modalità progettazione
  • Se non è in modalità progettazione, il localizzatore restituisce un ViewModel che risolve dal contenitore DI, come spiegato sopra
  • Se in modalità di progettazione, il localizzatore restituisce un ViewModel "fittizio" fisso utilizzando la propria logica (ricorda: non c'è contenitore in fase di progettazione!); questo ViewModel viene tipicamente precompilato con dati fittizi

Ovviamente questo significa che la vista deve avere un costruttore senza parametri per cominciare (altrimenti il ​​progettista non sarà in grado di istanziarlo).

Sommario

ViewModelLocator è un idioma che ti consente di mantenere i vantaggi di DI nella tua applicazione MVVM, consentendo anche al tuo codice di funzionare bene con i designer visivi. Questa operazione viene talvolta chiamata "sfumabilità" dell'applicazione (con riferimento a Expression Blend).

Dopo aver digerito quanto sopra, vedere un esempio pratico qui .

Infine, l'utilizzo di modelli di dati non è un'alternativa all'utilizzo di ViewModelLocator, ma un'alternativa all'utilizzo di coppie View / ViewModel esplicite per parti dell'interfaccia utente. Spesso potresti scoprire che non è necessario definire una vista per un ViewModel perché puoi invece utilizzare un modello di dati.


4
+1 per un'ottima spiegazione. Puoi espandere ulteriormente la vista e le sue risorse? Per Risorse, intendi le proprietà della vista? O? Hai un collegamento con un esempio concreto a questo modello?
Metro Smurf

@MetroSmurf: il tuo link si trova nella sezione Riepilogo.
Jon

1
Grazie. Esistono limitazioni all'uso di ViewModelLocator? Avevo qualche dubbio sul fatto che facesse riferimento a una risorsa statica: i ViewModels possono essere creati dinamicamente in fase di esecuzione? E c'è molto codice extra per collegarne uno?
Rachel

@ Rachel: Lo stesso collegamento nel Riepilogo dovrebbe rispondere a queste domande con esempi pratici.
Jon

2
Risposta estremamente fuorviante. Lo scopo principale di View Model Locator non è fornire dati fittizi al progettista. Puoi farlo facilmente specificando d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}". Lo scopo del localizzatore è di abilitare effettivamente DI sulle viste, perché WPF è così pessimo nel fornirlo. Esempio: hai una finestra principale che apre una finestra di dialogo. Per risolvere il DI nella finestra di dialogo nel solito modo, dovresti passarlo come dipendenza dalla finestra principale! Questo viene evitato con il View Locator.
hyankov

10

Un esempio di implementazione della risposta di @ Jon

Ho una classe di localizzazione del modello di visualizzazione. Ogni proprietà sarà un'istanza del modello di visualizzazione che assegnerò alla mia vista. Posso verificare se il codice è in esecuzione in modalità progettazione o non lo utilizza DesignerProperties.GetIsInDesignMode. Ciò mi consente di utilizzare un modello fittizio durante la fase di progettazione e l'oggetto reale quando eseguo l'applicazione.

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);
    }
}

E per usarlo posso aggiungere il mio localizzatore alle App.xamlrisorse:

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"

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

E poi per collegare la tua vista (es: MainView.xaml) al tuo viewmodel:

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

c'è qualche differenza nell'usare thisinvece di dummy?
Sebastian Xawery Wiśniowiecki

5

Non capisco perché le altre risposte a questa domanda avvolgono il Designer.

Lo scopo del View Model Locator è quello di consentire alla tua vista di istanziare questo (sì, View Model Locator = View First):

public void MyWindowViewModel(IService someService)
{
}

invece di solo questo:

public void MyWindowViewModel()
{
}

dichiarando questo:

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

Dov'è la ViewModelLocatorclasse, che fa riferimento a un IoC ed è così che risolve la MainWindowModelproprietà che espone.

Non ha nulla a che fare con la fornitura di modelli di vista Mock alla tua vista. Se lo vuoi, fallo e basta

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

Il View Model Locator è un wrapper attorno ad alcuni (qualsiasi) contenitore Inversion of Control, come Unity per esempio.

Fare riferimento a:


Un localizzatore di modelli di visualizzazione non richiede un contenitore, l'utente decide come il modello di visualizzazione viene risolto tramite la configurazione ed è possibile utilizzare un contenitore o semplicemente creare un tipo da soli. Quindi puoi eseguire la posizione del modello di visualizzazione basata sulla convenzione, ad esempio invece di preregistrare tutte le tue viste e visualizzare i modelli in qualche contenitore.
Chris Bordeman

Hai ragione quando dici " Non capisco perché le altre risposte [...] avvolgono il Designer " +1, ma il punto del localizzatore è rimuovere dalla vista ogni conoscenza di come sia il modello di visualizzazione creato, rendendo la vista indipendente da questa istanza, lasciandola al localizzatore. Il localizzatore sarà in grado di fornire diversi gusti del modello di visualizzazione, forse alcuni personalizzati aggiunti tramite plugin che il localizzatore gestirà (e uno specifico per la fase di progettazione). La vista sarà pulita da qualsiasi processo di individuazione della versione corretta del modello di visualizzazione, che è davvero un bene per SoC.
min.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.