web-dev-qa-db-fra.com

ViewModels dans ViewModelLocator MVVM Light

Est-il correct de stocker tous mes ViewModels dans SimpleIoc? Par exemple, j'ai trois pages MainPage, Photos, Répertoires (donc trois ViewModels -> MainVM, PhotosVM, DirectoriesVM). Dois-je définir DataContext dans chaque page pour afficher la propriété du modèle dans ViewModelLocator ou imbriquer ViewModels en tant que propriétés dans MainVM et lier chaque page DataContext à Main.PhotosVMProperty, Main.DirectoriesVMProperty, etc.? Quelqu'un pourrait-il m'expliquer l'idée et le but de l'IoC?

33
fex

Tout d'abord, regardons ce que fait ViewModelLocator et pourquoi nous l'utilisons:

ViewModelLocator est déclaré en tant qu'objet sur notre page App.xaml et est un singleton d'application. Nous allons en avoir un, et un seul d'entre eux à la disposition de l'application lors de son exécution.

ViewModelLocator est la source de tous nos ViewModels dans MVVM Light. Pour chaque ViewModel, nous aurons une propriété sur le ViewModelLocator qui nous permet d'obtenir un ViewModel pour une vue. Ce code ressemble à ceci:

public class ViewModelLocator
{
    public MainPageViewModel MainPage
    {
        get { return new MainPageViewModel(); }
    }
}

Ceci est un morceau de mon App.xaml:

<Application.Resources>
    <vm:ViewModelLocator
        x:Key="ViewModelLocator" />
</Application.Resources>

Ceci est un morceau de View.xaml

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

Jusqu'ici tout va bien. Pour répondre à votre première question, devez-vous utiliser Ioc dans MVVM Light? Non. Il n'y a pas besoin que votre modèle de vue soit donné à votre vue entièrement construite et instanciée par ViewModelLocator.

Passons maintenant à votre deuxième question: quel est le but de l'IoC?

L'IoC est conçu pour vous permettre d'effectuer les opérations suivantes:

Avec Mvvm Light, vous procédez comme suit:

public class ViewModelLocator
{
    public ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        if (ViewModelBase.IsInDesignModeStatic)
        {
            SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
        }
        else
        {
            SimpleIoc.Default.Register<IDataService, DataService>();         
        }

        SimpleIoc.Default.Register<MainViewModel>();
    }

    public MainViewModel Main
    {
        get { return SimpleIoc.Default.GetInstance<MainViewModel>(); }
    }
}

public class MainViewModel
{
    public ObservableCollection<Foo> Foos { get; set; }

    public MainViewModel(IDataService dataService)
    {
        _dataService=dataService;
        Foos=_dataService.GetFoos();
    }
}

Lorsque je résous mon MainViewModel lorsque j'appelle

SimpleIoc.Default.GetInstance<MainViewModel>()

ce qui se passe en interne, c'est que SimpleIoc vérifie si le MainViewModel a des dépendances (paramètres dans son constructeur). Il essaie ensuite de résoudre ces paramètres en examinant les interfaces qui ont été enregistrées avec lui. Il le fait récursivement, donc si DataService avait une dépendance, elle serait instanciée et transmise au constructeur DataService lors de son instanciation également.

Pourquoi ferais-je tout ce travail?

  1. Rendez vos classes faciles à tester
  2. Faites votre code axé sur l'interface. Cela signifie que vous faites référence à des interfaces plutôt qu'à des classes concrètes
  3. Faites votre code librement couplé. Cela signifie que quelqu'un peut changer l'implémentation d'une interface et que les classes qui utilisent cette interface ne s'en soucient pas et n'ont pas besoin d'être recodées.
  4. Résolvez vos dépendances de classes de manière automatisée.
  5. Dans MVVM Light, vous verrez qu'il peut savoir quand il s'exécute en mode conception (ViewModelBase.IsInDesignModeStatic), cela signifie que vous pouvez créer des services au moment du design pour fournir vos données de modèles de vue afin que votre vue dans Visual Studio contienne des données réelles.
124
Faster Solutions

MVVM Light possède de nombreuses fonctionnalités intéressantes, mais il me semble que le localisateur de service crée une dépendance indésirable des vues sur les modèles de vue. Idéalement, j'aimerais avoir le ViewModelLocator dans la bibliothèque A, les modèles de vue dans la bibliothèque B et les vues dans la bibliothèque C. Ensuite, je peux les mélanger et les assortir selon les besoins pour les projets futurs. Cependant, dans la conception de MVVM Light, pour autant que je puisse voir, les vues (bibliothèque C) auront toujours une dépendance sur le ViewModelLocator (ce n'est pas grave) mais parce que le ViewModelLocator (bibliothèque A) aura toujours une dépendance sur le modèles de vues (bibliothèque B), les vues dépendront toujours des modèles de vues (ce n'est pas correct car une vue doit maintenant inclure toutes les bibliothèques de modèles de vues avec lesquelles elle a été utilisée dans tous les produits).

Je crois que Prism contourne ce problème en utilisant les clés de chaîne d'une manière ou d'une autre. Suis-je en train de manquer quelque chose?

Oops! Je pense que je viens de répondre à ma propre question. La solution consiste à rendre la bibliothèque A, le ServiceLocator, spécifique à une solution particulière (produit). Il contient ensuite une référence aux modèles de vue uniquement pour cette solution. Ensuite, les vues dépendent de ce ServiceLocator qui à son tour dépend de tous les modèles de vue pour ce produit. Le résultat final est que les vues dépendent uniquement des modèles de vues avec lesquels elles seront utilisées pour ce produit. Il n'y a aucun problème avec le fait que nous dupliquons le ServiceLocator pour chaque solution car ce module contient uniquement du code spécifique à la solution. Les composants de ServiceLocator tels que la classe SimpleIoc sont, bien sûr, communs à toutes les solutions, mais ils ont été intégrés dans des classes réutilisables que nous invoquons dans ServiceLocator.

Pour résumer les choses, le problème que j'essaie de résoudre est de supposer qu'une solution a 6 modèles de vue, dont quatre sont étroitement liés et deux sont étroitement liés. Nous créons donc deux assemblages, chacun contenant les modèles de vue étroitement liés. Supposons que nous concevions un produit qui utilise un ensemble de modèles de vue et que la solution soit conçue pour exécuter Windows 8. Maintenant, les vues sont toutes différentes et nous voulons réutiliser un seul ensemble (assemblage) de modèles de vue. Nous créons donc simplement un nouvel assemblage ServiceLocator qui pointe vers cet assemblage de modèles de vue et également vers tous les autres dont nous avons besoin. Nos nouvelles vues Windows 8 dépendent désormais de ce nouvel assemblage ServiceLocator et uniquement des modèles de vue utilisés dans notre nouveau produit (solution).

1
Richard