Exemple # 1: J'ai une vue affichée dans mon application MVVM (utilisons Silverlight pour les besoins de la discussion) et je clique sur un bouton qui devrait m'amener à une nouvelle page.
Exemple # 2: Cette même vue possède un autre bouton qui, lorsque vous cliquez dessus, devrait ouvrir une vue détaillée dans une fenêtre enfant (boîte de dialogue).
Nous savons qu'il y aura des objets Command exposés par notre ViewModel lié aux boutons avec des méthodes qui répondent au clic de l'utilisateur. Mais alors quoi? Comment terminons-nous l'action? Même si nous utilisons un soi-disant NavigationService, que disons-nous?
Pour être plus précis, dans un modèle View-first traditionnel (comme les schémas de navigation basés sur des URL comme sur le Web ou le cadre de navigation intégré SL), les objets Command devraient savoir quelle vue afficher ensuite. Cela semble franchir la ligne quand il s'agit de la séparation des préoccupations promues par le modèle.
D'un autre côté, si le bouton n'était pas connecté à un objet Command et se comportait comme un lien hypertexte, les règles de navigation pouvaient être définies dans le balisage. Mais voulons-nous que les vues contrôlent le flux des applications et la navigation n'est-elle pas simplement un autre type de logique métier? (Je peux dire oui dans certains cas et non dans d'autres.)
Pour moi, l'implémentation utopique du modèle MVVM (et j'en ai entendu d'autres professer cela) consisterait à câbler le ViewModel de manière à ce que l'application puisse fonctionner sans tête (c'est-à-dire sans vues). Cela fournit la plus grande surface pour les tests basés sur le code et fait des vues un véritable habillage sur l'application. Et mon ViewModel ne devrait pas se soucier s'il s'affiche dans la fenêtre principale, un panneau flottant ou une fenêtre enfant, n'est-ce pas?
Selon cette approbation, il appartient à un autre mécanisme au moment de l'exécution de «lier» quelle vue doit être affichée pour chaque ViewModel. Mais que se passe-t-il si nous voulons partager une vue avec plusieurs ViewModels ou vice versa?
Donc, étant donné la nécessité de gérer la relation View-ViewModel afin que nous sachions quoi afficher quand ainsi que la nécessité de naviguer entre les vues, y compris l'affichage des fenêtres / boîtes de dialogue enfants, comment pouvons-nous vraiment accomplir cela dans le modèle MVVM?
la source
DataTemplateSelector
, ce que j'utilise habituellement pour les applications Silverlight. 2) J'ai déjà utilisé cela dans de nombreuses situations. Par exemple, un ViewModel peut avoir plusieurs vues, ou une vue peut être associée à plusieurs ViewModels. Pour un exemple de la première, voir rachel53461.wordpress.com/2011/05/28/… . Pour les versions ultérieures, il vous suffit de spécifier que plusieurs ViewModels mappent vers la même vue dans votre DataTemplateSelector.CurrentPages
4) Encore une fois, ce n'était qu'un exemple de base. En réalité, mes PageViewModels sont tous basés sur une classe de base, donc mon ShellViewModel ne fonctionne qu'avec la classe ou l'interface de base, commeIPageViewModel
. Vraiment le plus gros morceau de mappage désordonné est le DataTemplateSelector qui devrait mapper 40 vues à 40 ViewModels.Dans un souci de clôture, j'ai pensé publier la direction que j'ai finalement choisie pour résoudre ce problème.
La première décision a été de tirer parti du cadre de navigation de page Silverlight fourni dès le départ. Cette décision était basée sur plusieurs facteurs, notamment la connaissance du fait que ce type de navigation est repris par Microsoft dans les applications Windows 8 Metro et est compatible avec la navigation dans les applications Phone 7.
Pour le faire fonctionner, j'ai ensuite examiné le travail effectué par ASP.NET MVC avec la navigation basée sur les conventions. Le contrôle Frame utilise des URI pour localiser la «page» à afficher. La similitude a permis d'utiliser une approche similaire basée sur des conventions dans l'application Silverlight. L'astuce consistait à faire fonctionner tout cela de manière MVVM.
La solution est le NavigationService. Ce service expose plusieurs méthodes, telles que NavigateTo et Back, que ViewModels peut utiliser pour lancer un changement de page. Lorsqu'une nouvelle page est demandée, le NavigationService envoie un CurrentPageChangedMessage à l'aide de la fonction MVVMLight Messenger.
La vue qui contient le contrôle Frame a son propre ViewModel défini comme DataContext qui écoute ce message. Une fois reçu, le nom de la nouvelle vue est placé dans une fonction de mappage qui applique nos règles de convention et défini sur la propriété CurrentPage. La propriété Source du contrôle Frame est liée à la propriété CurrentPage. Par conséquent, la définition de la propriété met à jour la source et déclenche la navigation.
Revenons au NavigationService. La méthode NavigateTo accepte le nom de la page cible. Pour vous assurer que les ViewModels n'ont aucun problème d'interface utilisateur, le nom utilisé est le nom du ViewModel à afficher. J'ai en fait créé une énumération qui a un champ pour chaque ViewModel navigable comme aide et pour éliminer les chaînes magiques partout dans l'application. La fonction de mappage que j'ai mentionnée ci-dessus supprimera le suffixe "ViewModel" du nom, ajoutera "Page" au nom et définira le nom complet sur "Vues {Nom} Page.xaml".
Ainsi, par exemple, pour accéder à la vue des détails du client, je peux appeler:
La valeur de CustomerDetails est "CustomerDetailsViewModel" qui est mappée sur "Views \ CustomerDetailsPage.xaml".
La beauté de cette approche est que l'interface utilisateur est complètement découplée des ViewModels mais que nous avons une prise en charge complète de la navigation. Cependant, je peux maintenant refaire ma demande et à chaque fois que je le juge bon sans aucun changement de code.
J'espère que l'explication aide.
la source
Semblable à ce que Rachel a dit, mon application principalement MVVM a
Presenter
pour gérer les commutateurs entre les fenêtres ou les pages. Rachel appelle cela unApplicationViewModel
, mais d'après mon expérience, cela doit généralement faire plus que simplement être une cible contraignante (comme recevoir des messages, créer Windows, etc.) donc c'est techniquement plus comme un traditionnelPresenter
ouController
.Dans ma candidature, mon
Presenter
commence par unCurrentViewModel
. LePresenter
intercepte toutes les communications entre leView
et leViewModel
. L'une des choses que vousViewModel
pouvez faire pendant une interaction est de renvoyer une nouvelleViewModel
, ce qui signifie qu'une nouvelle page ou une nouvelleWindow
doit être affichée. LePresenter
prend soin de créer ou d'écraser leView
pour le nouveauViewModel
et de le paramétrerDataContext
.Le résultat d'une action peut également être que a
ViewModel
est "terminé", auquel cas lePresenter
détecte et ferme la fenêtre, ou la faitViewModel
disparaître de la pile VM et revient à l'affichage de la page précédente.la source
Presenter
colle simplement le retournéViewModel
dans l'arborescence visuelle, et WPF saisit le appropriéView
, raccorde leDataContext
et le place dans l'arborescence visuelle à la place. Vous pouvez le faire en utilisant desDataTemplate
s qui déclarent quelViewModel
type ils rendent, ou vous pouvez créer un sélecteur de modèle de données personnalisé. Cela fait toujours partie des fonctionnalités de WPF.