none
Concerne DataContext et MVVM RRS feed

  • Question

  • Bonjour,

    Voilà, j'ai en fait deux questions qui sont liées.

    J'ai deux Vues (Window1.xaml et Window2.xaml). Window1.xaml est liée à un DataContext via la ViewModel(ProductViewModel), dans cette Vue, il y a une ListBox qui va afficher une liste de Produits. Quand je sélectionne un produit de cette liste, je voudrais récupérer la Vue(Window2.xaml) pour qu'elle affiche le détail du produit(pour certaines contraintes techniques, je suis obligé de passer par une Window et pas par un UserControl).

    Donc mes questions sont:

    1) Dans un modèle MVVM, comment faire pour partager le même DataContext pour les deux Vues ?

    2) Comment afficher le détail de l'item(Product) dans la deuxième Vue que j'ai sélectionné depuis la ListeBox ? J'arrive à récupérer l'item via le SelectedItem="{Binding SelectedEvenement}" suivant le code ci-dessous.

     <ListBox Grid.Column="1" Grid.Row="1" Grid.RowSpan="4" HorizontalAlignment="Left" Height="269" Margin="33,0,0,0" VerticalAlignment="Top" Width="768" ItemsSource="{Binding Products}" SelectedItem="{Binding SelectedProduct}"/>

    Merci d'avance.

    vendredi 26 septembre 2014 06:45

Réponses

  • Bonjour,

    Voilà, suite à votre dernière remarque concernant l'ouverture de la deuxième fenêtre. J'ai pu trouver une solution en utilisant un menu contextuel.

    C'est galère galère comme début pour moi le MVVM.

    En tous cas merci pour vos remarque et pistes de solutions.

    Cordialement.

    • Marqué comme réponse Wenjli jeudi 2 octobre 2014 08:20
    jeudi 2 octobre 2014 08:20

Toutes les réponses

  • Bonjour,

    Pour le premier point. Il est possible de setter la propriété datacontext d'une window via code:

    Window2 wd = new Window2();
    wd.DataContext = myDataContext;

    ou via xaml:

    <Window.DataContext>
        <vms:MyDataContext />
    </Window.DataContext>

    Cordialement

    Cédric

    vendredi 26 septembre 2014 07:16
  • Bonjour,

    1) Attention c'est l'inverse, c'est un ViewModel que l'on partage, pas un DataContext. Maintenant si vous voulez le même datacontext pour deux vues il suffit d'affecter le même viewmodel aux deux DataContext, ou d'affecter le DataContext d'une vue au DataContext de l'autre vue.

    2) Si vous faîtes le choix d'utiliser une Window, il faut provoquer son ouverture lorsqu'un évènement arrive (clic sur un bouton, double clic sur la listbox, etc.). Pour cela vous avez plusieurs possibilités :

    • si vous utilisez un Framework MMVM regardez ce qu'il propose en terme de navigation et appliquez sa logique, généralement il s'agit de la mise en place d'une commande qui appelle un service de navigation. Ensuite il vous suffit d'appeler la commande depuis le\les évènements de votre vue.
    • si vous faîtes tout par vous même, vous pouvez tout coder en dur dans la vue (pas MVVM du tout)
    • L'approche logique de MVVM est la suivante, a vous de l'implémenter totalement ou partiellement en fonction de vos besoins
    1. Créer un service (une interface) qui fourni une méthode vous permettant de naviguer vers un ViewModel précis ou de manière générique
    2. Implémenter ce service : par exemple votre fenêtre principale va être capable de créer les fenêtres enfants correspondant aux ViewModels demandés.
    3. Enregistrer le service dans un "Service Locator", généralement il s'agit un conteneur IoC (ou Dependency Container, ou Dependency Resolver, selon la terminologie de chacun) afin qu'il soit accessible depuis n'importe quel ViewModel. Si vous n'en avez pas vous pouvez envisager une classe Singleton qui va maintenir les différents services dont vous avez besoin.
    4. Dans votre ViewModel créer une commande (basée sur l'interface ICommand), on trouve des exemples de code pour implémenter des RelayCommand ou DelegateCommand. Cette commande lorsqu'elle est exécutée va chercher le service de navigation et appeler la méthode permettant d'ouvrir le ViewModel recherché.
    5. Dans votre vue lorsque l'évènement est provoqué récupérer le ViewModel se trouvant dans le Datacontext et provoquer l'exécution de la commande naviguant vers votre vue

    La partie navigation est certainement la plus lourde à mettre en oeuvre dans MMVM, et la plus casse-pied aussi ;)

    Vous trouverez également d'autres approches de la navigation dans MVVM en faisant une recherche "mvvm navigation".

    Cordialement,


    Yan Grenier

    vendredi 26 septembre 2014 07:24
  • D'accord, je vois ce que vous faire. Mais, le problème c'est que le DataContext a été initialisé au niveau de ma Window1.

            public Window1()
            {
                InitializeComponent();
                ProductViewModel pdtDataContext = new ProductViewModel();
                this.DataContext = pdtDataContext ;
            }

    Comment puis-je le récupérer le DataContext (pdtDataContext ) pour faire comme vous me l'avez proposé ?

    Window2 wd = new Window2();
    wd.DataContext = myDataContext;

    vendredi 26 septembre 2014 08:49
  • Merci beaucoup pour le détail de votre réponse. Je vais essayé de découvrir les pistes que vous m'avez proposées. En espérant pouvoir ingurgiter ces nouveaux concepts, d'autant plus que je m'en sort difficilement avec cette nouvelle approche MVVM qui est toute nouvelle pour moi.

    Cordialement.

    vendredi 26 septembre 2014 08:59
  • MVVM comme toutes les approches (MVC, MVP, ...) nécessite un certain temps d'adaptation pour "penser" comme il faut, surtout en ce qui concerne la séparation des couches (notion de découplage).

    Avec le temps ca devient naturel, mais il faut "en bouffer". A mon humble avis il ne faut pas chercher dés le début à tout faire full MVVM, surtout si votre projet est complexe.

    En tout cas personnellement c'est ainsi que j'ai procédé. J'ai déjà appliqué essentiellement les ViewModel pour le binding, toute la navigation était en dur, puis petit à petit j'ai commencé à intégrer des notions de services, puis l'injection de dépendance (IoC/ServiceLocator), donc après la gestion des commandes.

    Cordialement,


    Yan Grenier

    vendredi 26 septembre 2014 09:24
  • Merci de me rassurer, parce que je commençais à me faire des idées. Surtout quand on sait pas si le code que l'on a fait est conforme aux principes du MVVM ou pas.

    Je reviens si vous le permettez à votre proposition de solution. Quand vous dites "c'est un ViewModel que l'on partage, pas un DataContext". Si j'ai bien compris, une fois que l'on a instancier le ViewModel pour l'affecter au DataContext d'une vue. Est-ce c'est la même ViewModel c'est à dire la référence de cette instanciation qui est partagée pour une deuxième Vue, ou bien c'est une nouvelle instanciation de cette même ViewModel que l'on affecte à la deuximème Vue ?

    vendredi 26 septembre 2014 10:31
  • Pour la récupération du datacontext, je ne sais pas ou sont instanciés vos fenetres.

    Si c'est au même endroit alors il sera possible de faire:

    wd.DataContext = window1.DataContext;

    Mais pour etre tout a fait honnête, ca commence a être un peu moche comme design !

    Si les 2 fenetres sont instanciées au même endroit, pourquoi ne pas imaginer créer un datacontext a cette endroit et le proposer au fenêtres via la propriété datacontext des windows ?

    C'est comme cela que l'on procède pour des usercontrols.

    Après, je suis totallement d'accord avec Yan, le MVVM c'est bien mais il y a plein de considération comme l'existant, les contraintes techniques, le temps de dev ...

    La philosophie c'est bien, mais le code parle parfois de lui même. Je suis sur un gros projet tout en WPF. On a appliqué le MVVM le plus possible et puis parfois, on a fait au plus simple. A chaque refactorisation, on repense a notre design et, en mettant a profit l'expérience acquise, on voit si on peut se rapprocher du model MVVM.

    Faire du full MVVM from scratch sans l'avoir mis en place plusieur fois me parait bien difficile.

    Cédric

    vendredi 26 septembre 2014 11:00
  • Voici l'arborescence:

    View
      Window1.xaml
      Window2.xaml

    ViewModel
      ProductViewModel

    Et c'est dans la class Windwo1.cs que j'initialise le DataContext comme suite:

        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
                ProductViewModel productViewModelDataContext = new ProductViewModel();
                this.DataContext = productViewModelDataContext;
            }
        }

    Donc à  ce niveau je sais pas récupérer une référence de productViewModelDataContext pour l'affecter au DataContext de Window2. Si ce n'est par une nouvelle instanciation ProductViewMode.

    A vrai dire, je ne sais pas si c'est au bon endroit où il faut que je récupère de ViewModel.

    Et l'instanciation de Window2. Se fait lorsque je récupère l'objet Product depuis la ListBox par SelectedItem qui me renvoit au ProductViewModel au niveau de la propriété:

            public Product SelectedProduct
            {
                get
                {
                    return m_SelectedProduct;
                }
                set
                {
                    m_SelectedEvenement = value;
                    Window2 W2 = new Window2 ();
                    W2.ShowDialog();
                }
            }

    • Modifié Wenjli vendredi 26 septembre 2014 11:38
    vendredi 26 septembre 2014 11:30
  • Houla ouvrir une fenêtre sur un changement de propriété, c'est à proscrire en terme d'interface !!!! Surtout que potentiellement le SelectedProduct est régulièrement mis à jour.

    Il faut revoir votre design d'application, privilégiez un double clic sur la listbox par exemple. Ou placez un bouton dans le ItemTemplate de votre listbox.

    Donc je vous conseilles de déplacer votre ouverture de fenêtre Window2 dans une méthode, et dans votre vue branchez vous sur différents évènements pour appeler la méthode de votre viewmodel.

    Sinon concernant votre problème je ne comprends pas !, Si vous êtes dans votre fenêtre this.DataContext de la fenêtre fourni votre viewmodel (quitte à le transtyper). Vous pouvez également ajouter une propriété à votre Window1 pour un accès plus rapide

    public class Window1{
      ...
      public ProductViewModel ViewModel{get{return (ProductViewModel)DataContext;}}
      ...
    }


    Pour avoir un accès direct. Sinon dans le code que vous indiquez vous êtes dans votre ViewModel ! donc le code suivant suffit

    Window2 W2 = new Window2 ();
    W2.DataContext = this;
    W2.ShowDialog();

    Cordialement,


    Yan Grenier

    vendredi 26 septembre 2014 13:22
  • Bonjour,

    Voilà, suite à votre dernière remarque concernant l'ouverture de la deuxième fenêtre. J'ai pu trouver une solution en utilisant un menu contextuel.

    C'est galère galère comme début pour moi le MVVM.

    En tous cas merci pour vos remarque et pistes de solutions.

    Cordialement.

    • Marqué comme réponse Wenjli jeudi 2 octobre 2014 08:20
    jeudi 2 octobre 2014 08:20