Che cos’è un ViewModelLocator e quali sono i suoi vantaggi / svantaggi rispetto a DataTemplates?

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

Ho provato a trovare informazioni su Google, ma sembra esserci molte implementazioni diverse e nessuna lista striaght su ciò che è e sui pro / contro del suo utilizzo.

Intro

In MVVM la pratica abituale è di avere le Views a trovare i loro ViewModels risolvendoli da un contenitore DI ( Injection Deployment). Ciò accade automaticamente quando viene chiesto al contenitore di fornire (risolvere) un’istanza della class View. Il contenitore inietta ViewModel nella vista chiamando un costruttore della vista che accetta un parametro ViewModel; questo schema è chiamato inversione del controllo (IoC).

Vantaggi di DI

Il vantaggio principale qui è che il contenitore può essere configurato in fase di esecuzione con le istruzioni su come risolvere i tipi da noi richiesti. Ciò consente una maggiore testabilità istruendolo a risolvere i tipi (Views e ViewModels) che usiamo quando la nostra applicazione viene effettivamente eseguita, ma istruendola diversamente quando si eseguono i test unitari 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 durante l’esecuzione dell’applicazione.

Problemi derivanti da DI

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

Il problema è che in entrambe le normali esecuzioni di applicazioni e test di unità, qualcuno deve impostare il contenitore con le istruzioni su quali tipi risolvere; inoltre, qualcuno deve chiedere al contenitore di risolvere le Views in modo che i ViewModels possano essere iniettati in essi.

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

  • Se la funzione di costruzione View richiede un’istanza ViewModel, il progettista non sarà in grado di creare un’istanza della vista – verrà eseguito un errore in qualche modo controllato
  • Se la vista ha un costruttore senza parametri, la vista verrà istanziata, ma il suo DataContext sarà null quindi otterremo una vista “vuota” nella finestra di progettazione – che non è molto utile

Inserisci ViewModelLocator

ViewModelLocator è un’astrazione aggiuntiva usata in questo modo:

  • La vista stessa istanzia un ViewModelLocator come parte delle sue risorse e databind 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 viene risolto dal contenitore DI, come spiegato sopra
  • Se in modalità progettazione, il localizzatore restituisce un ViewModel “fittizio” fisso utilizzando la propria logica (ricorda: non c’è contenitore in fase di progettazione!); questo ViewModel viene tipicamente prepopolato con dati fittizi

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

Sommario

ViewModelLocator è un idioma che ti consente di mantenere i benefici di DI nella tua applicazione MVVM e allo stesso tempo di consentire al tuo codice di giocare bene con i visual designer. A volte ciò viene chiamato “sfumabilità” della tua applicazione (riferito a Expression Blend).

Dopo aver digerito quanto sopra, vedi 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 View for a ViewModel perché puoi usare invece un modello di dati.

Un esempio di implementazione della risposta di @ Jon

Ho una class di localizzatore del modello di vista. Ogni proprietà sarà un’istanza del modello di vista che assegnerò alla mia vista. Posso verificare se il codice è in esecuzione in modalità progettazione o non utilizzare DesignerProperties.GetIsInDesignMode . Questo mi consente di utilizzare un modello di simulazione durante la fase di progettazione e l’object 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(); } } // 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 risorse App.xaml :

 xmlns:core="clr-namespace:MyViewModelLocatorNamespace"    

E poi per cablare la tua vista (es: MainView.xaml) sul tuo viewmodel:

  

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

Lo scopo del View Model Locator è di consentire alla Vista di creare un’istanza di questo tipo (sì, Visualizza il modello Locator = Visualizza prima):

 public void MyWindowViewModel(IService someService) { } 

invece di questo:

 public void MyWindowViewModel() { } 

dichiarando questo:

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

Dove ViewModelLocator è di class, che fa riferimento a un IoC ed è così che risolve la proprietà MainWindowModel che espone.

Non ha nulla a che fare con la fornitura di modelli di visualizzazione 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 (eventuali) inversori del contenitore di controllo, come Unity ad esempio.

Fare riferimento a: