Intro
En MVVM, la pratique habituelle consiste à faire en sorte que les vues trouvent leurs ViewModels en les résolvant à partir d'un fichier de type injection de dépendances (DI). Cela se produit automatiquement lorsqu'il est demandé au conteneur de fournir (résoudre) une instance de la classe View. Le conteneur injecte le ViewModel dans la vue en appelant un constructeur de la vue qui accepte un paramètre ViewModel ; ce schéma est appelé inversion du contrôle (IoC).
Avantages de l'AI
L'avantage principal ici est que le conteneur peut être configuré au moment de l'exécution avec des instructions sur la façon de résoudre les types que nous lui demandons. Cela permet une meilleure testabilité en lui demandant de résoudre les types (Views et ViewModels) que nous utilisons lorsque notre application s'exécute réellement, mais en lui donnant des instructions différentes lors de l'exécution des tests unitaires de l'application. Dans ce dernier cas, l'application n'aura même pas d'interface utilisateur (elle n'est pas en cours d'exécution, seuls les tests le sont). mocks à la place des types "normaux" utilisés lors de l'exécution de l'application.
Problèmes découlant du DI
Jusqu'à présent, nous avons vu que l'approche DI permet une testabilité facile de l'application en ajoutant une couche d'abstraction sur la création des composants de l'application. Il y a un problème avec cette approche : il ne joue pas bien avec les concepteurs visuels comme Microsoft Expression Blend.
Le problème est que, tant dans les applications normales que dans les tests unitaires, quelqu'un doit mettre en place le conteneur avec des instructions sur les types à résoudre ; en outre, quelqu'un doit demandez à le conteneur pour résoudre les vues afin que les ViewModels puissent être injectés dans celles-ci.
Cependant, dans le temps de conception, il n'y a pas de code à nous qui fonctionne. . Le concepteur tente d'utiliser la réflexion pour créer des instances de nos vues, ce qui signifie que :
- Si le constructeur de la vue nécessite une instance de ViewModel, le concepteur ne sera pas en mesure d'instancier la vue du tout - il se trompera d'une manière contrôlée.
- Si la vue possède un constructeur sans paramètre, la vue sera instanciée, mais sa fonction
DataContext
sera null
donc nous aurons une vue "vide" dans le concepteur, ce qui n'est pas très utile.
Entrer ViewModelLocator
Le ViewModelLocator est une abstraction supplémentaire utilisée comme ceci :
- La vue elle-même instancie un ViewModelLocator en tant que partie de son ressources et lie son DataContext à la propriété ViewModel du localisateur
- Le localisateur en quelque sorte détecte si nous sommes en mode conception
- S'il n'est pas en mode conception, le localisateur renvoie un ViewModel qu'il résout à partir du conteneur DI, comme expliqué ci-dessus.
- En mode conception, le localisateur renvoie un ViewModel fixe "factice" utilisant sa propre logique (n'oubliez pas qu'il n'y a pas de conteneur au moment de la conception) ; ce ViewModel est généralement fourni avec des données factices.
Bien sûr, cela signifie que la vue doit avoir un constructeur sans paramètre pour commencer (sinon le concepteur ne sera pas en mesure de l'instancier).
Résumé
ViewModelLocator est un idiome qui vous permet de conserver les avantages de la DI dans votre application MVVM tout en permettant à votre code d'être compatible avec les concepteurs visuels. Ceci est parfois appelé la "blendabilité" de votre application (en référence à Expression Blend).
Après avoir digéré ce qui précède, voyez un exemple pratique ici .
Enfin, l'utilisation de modèles de données n'est pas une alternative à l'utilisation de ViewModelLocator, mais une alternative à l'utilisation de paires View/ViewModel explicites pour certaines parties de votre IU. Souvent, vous constaterez qu'il n'est pas nécessaire de définir une vue pour un ViewModel car vous pouvez utiliser un modèle de données à la place.