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
DataContext
sarà null
così 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.