WPF Naviguer à travers des vues à l’aide du modèle MVVM

Je construis mon premier WPF en utilisant le pattern MVVM. Avec l’aide de cette communauté, je parviens à créer mon modèle, mon premier ViewModel et ma première vue. Maintenant, je veux append de la complexité à la conception de l’application de base. Mon idée est d’avoir au moins 2 vues enfants et une vue principale et de les séparer sur plusieurs XAML:

  • Main.XAML
  • Produits.XAML
  • Clients.XAML

Main aura un menu et un espace pour charger les vues enfant (Produits et Clients). Suivant le modèle MVVM, toute la logique de navigation entre les vues doit être écrite sur un ViewModel. Donc, mi idée est d’avoir 4 ViewModels:

  • MainViewModel
  • ProductsViewModel
  • ClientsViewModel
  • NavigationViewModel

Donc, NavigationViewModel doit contenir une collection de modèles de vues enfants? et un viewmodel actif est-ce exact?

Donc mes questions sont:

1) Comment puis-je charger différentes vues (Produits, Clients) sur la vue principale à l’aide du modèle MVVM?

2) Comment puis-je implémenter viewModel de navigation?

3) Comment puis-je contrôler le nombre maximum de vues ouvertes ou actives?

4) Comment puis-je basculer entre les vues ouvertes?

J’ai fait beaucoup de recherches et de lectures et je n’ai trouvé aucun exemple simple de navigation MVVM avec WPF qui charge plusieurs vues dans une vue principale. Beaucoup d’entre eux:

1) Utilisez des outils externes, que je ne veux pas utiliser pour le moment.

2) Mettez tout le code pour créer toutes les vues dans un seul fichier XAML, ce qui ne semble pas être une bonne idée car je dois implémenter près de 80 vues!

Je suis sur le bon chemin ici? Toute aide, en particulier avec du code, sera appréciée.

METTRE À JOUR

Donc, je construis un projet de test suivant les conseils de @LordTakkera, mais rest bloqué. Voici comment ma solution ressemble à: Solution

Je crée:

  • Deux modèles (clients et produits)

  • Une MainWindow et deux contrôles utilisateur wpf (Clients et Produits) XAML.

  • Trois modèles de vue (clients, produits et principal ViewModel)

Ensuite, je mets dataContext sur chaque vue à viewModel correspondant. Après cela, je crée MainWindow avec ContentPresenter comme ceci et le lie à une propriété du viewmodel.

MainWindow.XAML

                    

Et c’est aussi viewmodel de MainWindow:

 class Main_ViewModel : BaseViewModel { public Main_ViewModel() { CurrentView = new Clients(); } private UserControl _currentView; public UserControl CurrentView { get { return _currentView; } set { if (value != _currentView) { _currentView = value; OnPropertyChanged("CurrentView"); } } } } 

Donc, cette vue par défaut des clients de chargement et ressemble à ceci (ce qui est juste!):

État actuel

Donc, je suppose que j’ai besoin d’un moyen de relier les boutons à gauche, avec un certain modèle de mémoire, puis de les lier avec la propriété CurrentView de Main viewModel. Comment puis je faire ça?

MISE À JOUR2

Selon les conseils de @LordTakkera, je modifie mon mode de vue principal de la manière suivante:

 class Main_ViewModel : BaseViewModel { public ICommand SwitchViewsCommand { get; private set; } public Main_ViewModel() { //CurrentView = new Clients(); SwitchViewsCommand = new RelayCommand((parameter) => CurrentView = (UserControl)Activator.CreateInstance(parameter as Type)); } private UserControl _currentView; public UserControl CurrentView { get { return _currentView; } set { if (value != _currentView) { _currentView = value; OnPropertyChanged("CurrentView"); } } } } 

J’utilise RelayCommand à la place de DelegateCommand mais je pense que cela fonctionne de la même manière. La commande est exécutée quand je tape sur les boutons et la chaîne de paramètre type est ok mais je reçois cette erreur:

Erreur

Traduction: la valeur ne peut pas être nulle. Nom du paramètre: type. Suggestion utiliser Nouveau mot-clé pour créer une instance d’object Je ne sais pas où placer le mot-clé Nouveau. J’ai essayé le paramètre de commande mais cela ne fonctionnera pas. Une idée? Merci

MISE À JOUR 3

Après tous les conseils et l’aide reçus ici, et beaucoup de travail, voici mon menu de navigation final et la base de mon interface d’application.

Capture 1Capture 2

Je ne suis pas sûr que vous ayez besoin d’un modèle de vue “navigation” séparé, vous pourriez facilement le mettre dans le vif. D’une manière ou d’une autre:

Pour séparer vos vues “enfants”, j’utiliserais un simple ContentPresenter sur votre vue “principale”:

  

Le moyen le plus simple d’implémenter la propriété de sauvegarde consiste à en faire un UserControl , bien que certains prétendent que cela viole MVVM (puisque ViewModel dépend désormais d’une classe “View”). Vous pourriez en faire un object, mais vous perdez un peu de sécurité. Chaque vue serait un UserControl dans ce cas.

Pour basculer entre eux, vous allez avoir besoin d’une sorte de contrôle de sélection. Je l’ai fait avec les boutons radio avant, vous les liez comme ça:

  

Le convertisseur est assez simple, dans “Convert” il vérifie simplement si le contrôle actuel est un type du paramètre, dans “ConvertBack” il retourne une nouvelle instance du paramètre.

 public class InstanceEqualsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return (parameter as Type).IsInstanceOfType(value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return (bool)value ? Activator.CreateInstance(parameter as Type) : Binding.DoNothing; } } 

La liaison à une liste déroulante ou à un autre contrôle de sélection suivrait un modèle similaire.

Bien sûr, vous pouvez également utiliser DataTemplates (avec un sélecteur, ce qui n’est malheureusement pas quelque chose que j’ai fait auparavant) et les charger dans vos ressources à l’aide de dictionnaires fusionnés (permettant des XAML séparés). Personnellement, je préfère la route de contrôle de l’utilisateur, choisissez celle qui vous convient le mieux!

Cette approche est “une vue à la fois”. Il serait relativement facile de convertir en plusieurs vues (votre UserControl devient une collection de contrôles utilisateur, utilisez .Contains dans le convertisseur, etc.).

Pour ce faire avec des boutons, j’utiliserais des commandes et tirer parti du paramètre CommandParameter.

Le bouton XAML ressemblerait à ceci:

  

Ensuite, vous avez une commande de délégué (tutoriel ici ) qui exécute le code d’activation à partir du convertisseur:

 public ICommand SwitchViewsCommand {get; private set;} public MainViewModel() { SwitchViewsCommand = new DelegateCommand((parameter) => CurrentView = Activator.CreateInstance(parameter as Type)); } 

Cela me dépasse, mais devrait être assez proche. Faites-moi savoir comment ça se passe!

Faites-moi savoir si je fournis des informations supplémentaires!

Mettre à jour:

Pour répondre à vos préoccupations:

  1. Oui, chaque fois que vous appuyez sur le bouton, une nouvelle instance de la vue est créée. Vous pouvez facilement résoudre ce problème en contenant un Dictionary qui contient des vues pré-créées et un index. Pour cela, vous pouvez utiliser un Dictonary et utiliser des chaînes simples comme parameters du convertisseur. L’inconvénient est que votre ViewModel est étroitement lié aux types de vues qu’il peut présenter (puisqu’il doit remplir ledit dictionnaire).

  2. La classe doit être éliminée, tant que personne d’autre ne la référence (pensez aux gestionnaires d’événements auxquels elle s’est inscrite).

  3. Comme vous le soulignez, une seule vue est créée à la fois, vous n’avez donc pas à vous soucier de la mémoire. Vous appelez, bien sûr, un constructeur, mais ce n’est pas si cher, en particulier sur les ordinateurs modernes, où nous avons souvent beaucoup de temps à consacrer au processeur. Comme toujours, la réponse aux questions de performances est “Analyse comparative”, car vous avez uniquement access aux cibles de déploiement prévues et à la source entière pour voir ce qui fonctionne le mieux.

IMHO le meilleur choix pour vous est d’utiliser le framework MVVM (PRISM, MMVM Light, Chinch, etc.) car la navigation est déjà implémentée. Si vous voulez créer votre propre navigation, essayez DataTemplate.