Quelqu'un peut-il me donner un bref résumé de ce qu'est un ViewModelLocator, de son fonctionnement et des avantages/inconvénients de son utilisation par rapport aux DataTemplates?
J'ai essayé de trouver des informations sur Google, mais il semble y avoir de nombreuses implémentations différentes et aucune liste de rayures sur ce que c'est et les avantages/inconvénients de l'utiliser.
Dans MVVM, la pratique habituelle est que les vues trouvent leurs ViewModels en les résolvant à partir d'un conteneur injection de dépendance (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).
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 (vues et modèles d'affichage) 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 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), de sorte que le conteneur résoudra mocks à la place des types "normaux" utilisés lors de l'exécution de l'application.
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 normales des applications et les tests unitaires, quelqu'un doit configurer le conteneur avec des instructions sur les types à résoudre; en outre, quelqu'un doit demander le conteneur pour résoudre les vues afin que les ViewModels puissent y être injectés.
Cependant, au moment du design, il n'y a pas de code 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:
DataContext
sera null
donc nous aurons une vue "vide" dans le concepteur - ce qui n'est pas très utileLe ViewModelLocator est une abstraction supplémentaire utilisée comme ceci:
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).
ViewModelLocator est un idiome qui vous permet de conserver les avantages de DI dans votre application MVVM tout en permettant à votre code de bien jouer avec les concepteurs visuels. C'est ce qu'on appelle parfois la "possibilité de fusion" de votre application (en se référant à Expression Blend).
Après avoir digéré ce qui précède, voir 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 des 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.
Un exemple d'implémentation de @ Jon's answer
J'ai une classe de localisateur de modèle de vue. Chaque propriété va être une instance du modèle de vue que je vais allouer sur ma vue. Je peux vérifier si le code s'exécute en mode conception ou n'utilise pas DesignerProperties.GetIsInDesignMode
. Cela me permet d'utiliser un modèle fictif lors de la conception du temps et de l'objet réel lorsque j'exécute l'application.
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);
}
}
Et pour l'utiliser, je peux ajouter mon localisateur à App.xaml
Ressources:
xmlns:core="clr-namespace:MyViewModelLocatorNamespace"
<Application.Resources>
<core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
Et puis pour câbler votre vue (ex: MainView.xaml) à votre modèle de vue:
<Window ...
DataContext="{Binding Path=MainViewModel, Source={StaticResource ViewModelLocator}}">
Je ne comprends pas pourquoi les autres réponses à cette question tournent autour du concepteur.
Le but du View Model Locator est de permettre à votre vue d'instancier cela (oui, View Model Locator = View First):
public void MyWindowViewModel(IService someService)
{
}
au lieu de cela:
public void MyWindowViewModel()
{
}
en déclarant ceci:
DataContext="{Binding MainWindowModel, Source={StaticResource ViewModelLocator}}"
Où ViewModelLocator
est une classe, qui fait référence à un IoC et c'est ainsi qu'il résout la propriété MainWindowModel
qu'il expose.
Cela n'a rien à voir avec la fourniture de modèles de vue simulée à votre vue. Si vous le voulez, faites-le
d:DataContext="{d:DesignInstance MockViewModels:MockMainWindowModel, IsDesignTimeCreatable=True}"
Le localisateur de modèle de vue est un wrapper autour de tout (n'importe quel) conteneur d'inversion de contrôle, tel que Unity par exemple.
Faire référence à: