Bonjour à tous et à toutes.

Je développe une application WPF basée sur le Ribbon Microsoft.
Cette application utilise MVVM Light et je suis le tutoriel écrit ici.

Derrière chaque vue (fenêtre pricipale, Ribbon, chaque onglet du Ribbon) est accroché un DataContext qui permet, via un Locator d'associer à la vue un ViewModel, ça donne un truc comme çà :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
 
<Custom:RibbonTab DataContext="{Binding RibbonTabHome, Source={StaticResource Locator}}"
                           Header="Accueil"
                           Visibility="{Binding TabVisible, ConverterParameter=Visibility, Converter={StaticResource mGUIConverter}}">
Seulement, au lieu de présenter sous le ruban toujours le même document, je présente des vues complètement différentes en fonction de l'onglet (oui, un peu comme une gestion d'onglet, ok le ribbon n'est pas vraiment fait pour çà).
J'ai par exemple un Onglet Utilisateur qui permet via deux boutons de voir la liste des profiles et de les modifier et de voir la liste des utilisateurs et de les modifier.

J'ai donc, dans ma fenêtre principale, sous le Ribbon, une grosse zone qui doit être chargée dynamiquement :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
            <!-- Panel qui contient tous les autres panneaux chargés dynamiquement -->
            <ContentControl Name="MainPlaceHolder" Content="{Binding PlaceHolder}"/>
Du coup, lorsque l'on change d'onglet, je reçois dans la fenêtre principale un évènement :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
        private void Ribbon_SelectionChanged(object _oSender, SelectionChangedEventArgs _selChangedArgs)
Mais que faire de cet évènement ? il faudrait que j'assigne MainPlaceHolder.Content = instance de "ma vue";
Seulement voilà, chaque onglet en comporte plusieurs (en fonction d'un bouton de l'onglet qui peut changer des choses) et seul le ViewModel sait quel vue est active pour lui-même. Je peux donc poster un message de la vue vers les ViewModel via Messenger.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        private void Ribbon_SelectionChanged(object _oSender, SelectionChangedEventArgs _selChangedArgs)
        {
            if (_selChangedArgs.AddedItems.Count == 1)
            {   // Récupérer onglet sélectionné
                RibbonTab selectedTab = _selChangedArgs.AddedItems[0] as RibbonTab;
                if (selectedTab.DataContext != null)
                {   // Si le data context est non null, ok
                    Messenger.Default.Send(new NotificationMessage<RibbonTabSelectionChangedMessage>(new RibbonTabSelectionChangedMessage{PlaceHolder = MainPlaceHolder,Ribbon = selectedTab},RibbonTabSelectionChangedMessage.NotificationString));
                }
                else
                {   // Ça ne doit JAMAIS arriver
                    Logger.Log.Warn("Le modèle de l'onglet est null !");
                }
            }
        }
Du coup, je pourrais avoir cette fonction dans le ViewModel :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        /// <summary>
        /// Fonction appelée lorsque la sélection d'onglet change.
        /// </summary>
        public virtual void OnSelectedTabChanged(NotificationMessage<RibbonTabSelectionChangedMessage> _msgRibbonTabChanged)
        {
            if (_msgRibbonTabChanged.Content.Ribbon.DataContext == this)
            {   // Seulement si celui qui a changé est moi-même : je deviens actif
                OnActivate(_msgRibbonTabChanged.Content.PlaceHolder);
            }
        }
 
        /// <summary>
        /// Fonction appelée lorsque la sélection d'onglet change.
        /// 
        /// Permet de créer la fenêtre à afficher dans la zone de données.
        /// </summary>
        protected abstract void OnActivate(ContentControl _placeHolder);
Cette fonction permet d'appeler OnActivate de la classe dérivée du RibbonTabBaseViewModel duquel est issu cette fonction.
Dans ce cas c'est la classe dérivée qui crée la vue si elle n'existe pas et qui set le placeholder avec cette nouvelle vue.

C'est la solution la plus simple mais pas propre du tout !!
Car dans MVVM, le ViewModel ne doit pas connaître la vue or là, dans ce cas, c'est le ViewModel qui crée la vue !

Sinon je peux faire un truc immonde dans la vue principale mais ça va être compliqué à faire.
Une autre solution, ça serait de charger toutes les vues au démarrage et de les afficher ou pas en fonction du contexte sauf que je ne sais pas empiler les vues les unes au dessus des autres...
Et puis je veux un fichier xaml par vue, je veux pas tout mettre dans la vue principale sinon ça va devenir ingérable !

Voilà je voudrais une architecture propre mais pas bordélique, que chaque onglet / chaque vue ai son propre code séparé des autres afin d'avoir du code simpe mais je ne sais pas comment charger ma vue PlaceHolder et surtout QUI doit la créer ?

Si vous avez une idée ? (si j'ai été assez clair)