Intro
Dans MVVM, la pratique habituelle consiste à demander aux vues de trouver leurs ViewModels en les résolvant à partir d'un conteneur d' injection de dépendances (DI). Cela se produit automatiquement lorsque le conteneur est invité à 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 de contrôle (IoC).
Avantages de DI
Le principal avantage 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 plus grande 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 l'instruisant différemment lors de l'exécution des tests unitaires pour 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), donc le conteneur résoudra les simulations à la place des types "normaux" utilisés lorsque l'application s'exécute.
Problèmes découlant de l'ID
Jusqu'à présent, nous avons vu que l'approche DI permet une testabilité facile pour l'application en ajoutant une couche d'abstraction sur la création de composants d'application. Il y a un problème avec cette approche: elle ne fonctionne pas bien avec les concepteurs visuels tels que Microsoft Expression Blend.
Le problème est que dans les exécutions d'applications normales et les exécutions de tests unitaires, quelqu'un doit configurer le conteneur avec des instructions sur les types à résoudre; De plus, quelqu'un doit demander au conteneur de résoudre les vues afin que les ViewModels puissent y être injectés.
Cependant, au moment de la conception, aucun de nos codes n'est en cours d'exécution . Le concepteur tente d'utiliser la réflexion pour créer des instances de nos vues, ce qui signifie que:
- Si le constructeur de vue nécessite une instance de ViewModel, le concepteur ne pourra pas du tout instancier la vue - il provoquera une erreur de manière contrôlée
- Si la vue a un constructeur sans paramètre, la vue sera instanciée, mais elle le
DataContext
sera null
ainsi nous obtiendrons une vue "vide" dans le concepteur - ce qui n'est pas très utile
Entrez ViewModelLocator
Le ViewModelLocator est une abstraction supplémentaire utilisée comme ceci:
- La vue elle-même instancie un ViewModelLocator dans le cadre de ses ressources et lie son DataContext à la propriété ViewModel du localisateur
- Le localisateur détecte en quelque sorte 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
- Si en mode conception, le localisateur renvoie un ViewModel "factice" fixe en utilisant sa propre logique (rappelez-vous: il n'y a pas de conteneur au moment du design!); ce ViewModel est généralement pré-rempli 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 pourra pas 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 de bien jouer avec les concepteurs visuels. Ceci est parfois appelé la «possibilité de fusion» 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 interface utilisateur. Vous constaterez souvent 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.
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
. Le but du localisateur est en fait d'activer DI sur les vues, car WPF est si mauvais pour le fournir. Exemple: vous avez une fenêtre principale qui ouvre une fenêtre de dialogue. Pour résoudre le DI sur la fenêtre de dialogue de la manière habituelle, vous devez le passer comme une dépendance de la fenêtre principale! Ceci est évité avec le localisateur de vue.Un exemple d'implémentation de la réponse de @ Jon
J'ai une classe de localisateur de modèle de vue. Chaque propriété sera une instance du modèle de vue que je vais allouer à ma vue. Je peux vérifier si le code s'exécute en mode conception ou non
DesignerProperties.GetIsInDesignMode
. Cela me permet d'utiliser un modèle simulé pendant la conception et l'objet réel lorsque j'exécute l'application.Et pour l'utiliser, je peux ajouter mon localisateur aux
App.xaml
ressources:Et puis pour câbler votre vue (ex: MainView.xaml) à votre viewmodel:
la source
this
au lieu dedummy
?Je ne comprends pas pourquoi les autres réponses de cette question tournent autour du concepteur.
Le but du View Model Locator est de permettre à votre View de l'instancier (oui, View Model Locator = View First):
au lieu de cela:
en déclarant ceci:
Où
ViewModelLocator
est la classe, qui fait référence à un IoC et c'est ainsi qu'elle résout laMainWindowModel
propriété qu'elle expose.Cela n'a rien à voir avec la fourniture de modèles de vue simulée à votre vue. Si tu veux ça, fais juste
Le localisateur de modèle de vue est un wrapper autour de certains (n'importe quel) conteneur d'inversion de contrôle, tel que Unity par exemple.
Faire référence à:
la source