Dans le modèle MVVM pour WPF, la gestion des boîtes de dialogue est l'une des opérations les plus complexes. Comme votre modèle de vue ne sait rien de la vue, la communication par dialogue peut être intéressante. Je peux exposer ICommand
que lorsque la vue l'invoque, une boîte de dialogue peut apparaître.
Quelqu'un connaît-il un bon moyen de gérer les résultats des dialogues? Je parle de boîtes de dialogue Windows telles que MessageBox
.
L'une des façons dont nous l'avons fait était d'avoir un événement sur le modèle de vue auquel la vue s'abonnerait lorsqu'une boîte de dialogue était requise.
public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;
C'est correct, mais cela signifie que la vue nécessite du code, ce que je voudrais éviter.
Réponses:
Je suggère de renoncer aux dialogues modaux des années 1990 et d'implémenter un contrôle en superposition (canevas + positionnement absolu) avec une visibilité liée à un retour booléen dans la machine virtuelle. Plus proche d'un contrôle de type ajax.
C'est très utile:
un péché:
Voici comment j'en ai implémenté un en tant que contrôle utilisateur. Cliquer sur le «x» ferme le contrôle dans une ligne de code dans le code de l'utilisateur derrière. (Étant donné que j'ai mes vues dans un fichier .exe et ViewModels dans une DLL, je ne me sens pas mal à propos du code qui manipule l'interface utilisateur.)
la source
Vous devez utiliser un médiateur pour cela. Mediator est un modèle de conception commun également connu sous le nom de Messenger dans certaines de ses implémentations. C'est un paradigme de type Register / Notify et permet à votre ViewModel et à vos vues de communiquer via un mécanisme de messagerie à faible couplage.
Vous devriez consulter le groupe Google WPF Disciples et rechercher simplement Mediator. Vous serez très satisfait des réponses ...
Vous pouvez cependant commencer par ceci:
http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/
Prendre plaisir !
Edit: vous pouvez voir la réponse à ce problème avec le MVVM Light Toolkit ici:
http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338
la source
Une bonne boîte de dialogue MVVM devrait:
Malheureusement, WPF ne fournit pas ces fonctionnalités. L'affichage d'une boîte de dialogue nécessite un appel codé à
ShowDialog()
. La classe Window, qui prend en charge les boîtes de dialogue, ne peut pas être déclarée en XAML, elle ne peut donc pas être facilement liée aux données duDataContext
.Pour résoudre ce problème, j'ai écrit un contrôle de stub XAML qui se trouve dans l'arborescence logique et relie la liaison de données à un
Window
et gère l'affichage et le masquage de la boîte de dialogue. Vous pouvez le trouver ici: http://www.codeproject.com/KB/WPF/XAMLDialog.aspxC'est vraiment simple à utiliser et ne nécessite aucune modification étrange de votre ViewModel et ne nécessite pas d'événements ou de messages. L'appel de base ressemble à ceci:
Vous voudrez probablement ajouter un style qui définit
Showing
. Je l'explique dans mon article. J'espère que ceci vous aide.la source
"Showing a dialog requires a code-behind"
mmm vous pouvez appeler ça dans ViewModelJ'utilise cette approche pour les dialogues avec MVVM.
Il ne me reste plus qu'à appeler ce qui suit à partir de mon modèle de vue.
la source
Ma solution actuelle résout la plupart des problèmes que vous avez mentionnés, mais elle est complètement abstraite des éléments spécifiques à la plate-forme et peut être réutilisée. De plus, je n'ai utilisé aucune liaison de code uniquement avec des délégués qui implémentent ICommand. La boîte de dialogue est essentiellement une vue - un contrôle distinct qui a son propre ViewModel et il est affiché à partir du ViewModel de l'écran principal mais déclenché à partir de l'interface utilisateur via la liaison DelagateCommand.
Voir la solution complète de Silverlight 4 ici Dialogues modaux avec MVVM et Silverlight 4
la source
J'ai vraiment eu du mal avec ce concept pendant un certain temps lors de l'apprentissage (toujours en cours) de MVVM. Ce que j'ai décidé, et ce que je pense que d'autres ont déjà décidé, mais qui n'était pas clair pour moi, c'est ceci:
Ma pensée originale était qu'un ViewModel ne devrait pas être autorisé à appeler une boîte de dialogue directement car il n'a pas à décider comment une boîte de dialogue devrait apparaître. À cause de cela, j'ai commencé à réfléchir à la façon dont je pouvais passer des messages comme je le ferais dans MVP (c'est-à-dire View.ShowSaveFileDialog ()). Cependant, je pense que c'est la mauvaise approche.
Il est normal qu'un ViewModel appelle directement une boîte de dialogue. Cependant, lorsque vous testez un ViewModel, cela signifie que la boîte de dialogue apparaîtra pendant votre test, ou échouera tous ensemble (jamais vraiment essayé cela).
Donc, ce qui doit se produire, c'est que pendant le test, vous utilisez une version "test" de votre boîte de dialogue. Cela signifie que pour chaque boîte de dialogue que vous avez, vous devez créer une interface et simuler la réponse de la boîte de dialogue ou créer une maquette de test qui aura un comportement par défaut.
Vous devez déjà utiliser une sorte de localisateur de service ou IoC que vous pouvez configurer pour vous fournir la version correcte en fonction du contexte.
En utilisant cette approche, votre ViewModel est toujours testable et selon la façon dont vous modifiez vos boîtes de dialogue, vous pouvez contrôler le comportement.
J'espère que cela t'aides.
la source
Il existe deux bonnes façons de procéder: 1) un service de dialogue (facile, propre) et 2) une vue assistée. L'aide assistée fournit quelques fonctionnalités intéressantes, mais cela n'en vaut généralement pas la peine.
SERVICE DE DIALOGUE
a) une interface de service de dialogue comme via un constructeur ou un conteneur de dépendance:
interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }
b) Votre implémentation de IDialogService devrait ouvrir une fenêtre (ou injecter un certain contrôle dans la fenêtre active), créer une vue correspondant au nom du type dlgVm donné (utiliser l'enregistrement ou la convention de conteneur ou un ContentPresenter avec le type DataTemplates associé). ShowDialogAsync doit créer un TaskCompletionSource et renvoyer sa propriété .Task. La classe DialogViewModel elle-même a besoin d'un événement que vous pouvez invoquer dans la classe dérivée lorsque vous souhaitez fermer et regarder dans la vue de dialogue pour fermer / masquer réellement la boîte de dialogue et terminer la TaskCompletionSource.
b) Pour l'utiliser, appelez simplement wait this.DialogService.ShowDialog (myDlgVm) sur votre instance d'une classe dérivée de DialogViewModel. Après avoir attendu les retours, regardez les propriétés que vous avez ajoutées sur votre machine virtuelle de dialogue pour déterminer ce qui s'est passé; vous n'avez même pas besoin d'un rappel.
VOIR ASSISTÉ
Cela vous permet d'écouter un événement sur le modèle d'affichage. Tout cela pourrait être enveloppé dans un comportement Blend pour éviter le code et l'utilisation des ressources si vous êtes si enclin (FMI, sous-classe la classe "Behavior" pour voir une sorte de propriété attachée Blendable sur les stéroïdes). Pour l'instant, nous allons le faire manuellement sur chaque vue:
a) Créez un OpenXXXXXDialogEvent avec une charge utile personnalisée (une classe dérivée de DialogViewModel).
b) Demandez à la vue de s'abonner à l'événement dans son événement OnDataContextChanged. Assurez-vous de masquer et de vous désabonner si l'ancienne valeur! = Null et dans l'événement Unloaded de la fenêtre.
c) Lorsque l'événement se déclenche, demandez à la vue d'ouvrir votre vue, qui peut se trouver dans une ressource sur votre page, ou vous pouvez la localiser par convention ailleurs (comme dans l'approche du service de dialogue).
Cette approche est plus flexible, mais nécessite plus de travail à utiliser. Je ne l'utilise pas beaucoup. Le seul avantage est la possibilité de placer la vue physiquement à l'intérieur d'un onglet, par exemple. J'ai utilisé un algorithme pour le placer dans les limites du contrôle utilisateur actuel, ou s'il n'est pas assez grand, parcourir l'arborescence visuelle jusqu'à ce qu'un conteneur assez grand soit trouvé.
Cela permet aux boîtes de dialogue d'être proches de l'endroit où elles sont réellement utilisées, atténuer uniquement la partie de l'application liée à l'activité en cours, et permettre à l'utilisateur de se déplacer dans l'application sans avoir à repousser manuellement les boîtes de dialogue, même en ayant plusieurs quasi- les boîtes de dialogue modales s'ouvrent sur différents onglets ou sous-vues.
la source
Utilisez une commande figable
la source
Je pense que la gestion d'une boîte de dialogue devrait être la responsabilité de la vue, et la vue doit avoir du code pour le supporter.
Si vous modifiez l'interaction ViewModel - View pour gérer les boîtes de dialogue, le ViewModel dépend de cette implémentation. Le moyen le plus simple de résoudre ce problème consiste à confier à la vue la responsabilité d'effectuer la tâche. Si cela signifie afficher une boîte de dialogue, c'est bien, mais cela peut également être un message d'état dans la barre d'état, etc.
Mon point est que tout le point du modèle MVVM est de séparer la logique métier de l'interface graphique, donc vous ne devriez pas mélanger la logique GUI (pour afficher une boîte de dialogue) dans la couche métier (le ViewModel).
la source
Une alternative intéressante consiste à utiliser des contrôleurs chargés d'afficher les vues (boîtes de dialogue).
Comment cela fonctionne est montré par le cadre d'application WPF (WAF) .
la source
Pourquoi ne pas simplement déclencher un événement dans la machine virtuelle et vous abonner à l'événement dans la vue? Cela garderait la logique d'application et la vue séparées et vous permettrait toujours d'utiliser une fenêtre enfant pour les boîtes de dialogue.
la source
J'ai implémenté un comportement qui écoute un message du ViewModel. Il est basé sur la solution Laurent Bugnion, mais comme il n'utilise pas de code derrière et est plus réutilisable, je pense que c'est plus élégant.
Comment faire en sorte que WPF se comporte comme si MVVM était pris en charge immédiatement
la source
Je pense que la vue pourrait avoir du code pour gérer l'événement à partir du modèle de vue.
Selon l'événement / scénario, il peut également avoir un déclencheur d'événement qui s'abonne pour afficher les événements du modèle et une ou plusieurs actions à invoquer en réponse.
la source
J'ai eu la même situation et enveloppé la MessageBox dans un contrôle invisible de concepteur. Les détails sont dans mon blog
http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx
La même chose peut être étendue à toutes les boîtes de dialogue modales, le contrôle de navigation dans les fichiers, etc.
la source
J'ai roulé mon propre chargeur de fenêtre décrit dans une réponse à cette question:
Gestion de plusieurs vues WPF dans une application
la source
Karl Shifflett a créé un exemple d'application pour afficher les boîtes de dialogue en utilisant l'approche service et l'approche Prism InteractionRequest.
J'aime l'approche de service - Elle est moins flexible, donc les utilisateurs sont moins susceptibles de casser quelque chose :) Elle est également cohérente avec la partie WinForms de mon application (MessageBox.Show) Mais si vous prévoyez d'afficher beaucoup de dialogues différents, alors InteractionRequest est un meilleure façon d'aller.
http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/
la source
Je sais que c'est une vieille question, mais quand j'ai fait cette recherche, je trouve beaucoup de questions connexes, mais je n'ai pas trouvé de réponse vraiment claire. Je fais donc ma propre implémentation d'une boîte de dialogue / boîte de message / popin, et je la partage!
Je pense que c'est "MVVM proof", et j'essaie de le rendre simple et approprié, mais je suis nouveau sur WPF, alors n'hésitez pas à commenter, ou même à faire une pull request.
https://github.com/Plasma-Paris/Plasma.WpfUtils
Vous pouvez l'utiliser comme ceci:
Ou comme ça si vous voulez un popin plus sophistiqué:
Et ça montre des choses comme ça:
la source
L'approche standard
Après avoir passé des années à résoudre ce problème dans WPF, j'ai finalement trouvé la manière standard d'implémenter des dialogues dans WPF. Voici les avantages de cette approche:
Alors quelle est la clé. C'est DI + IoC .
Voici comment cela fonctionne. J'utilise MVVM Light, mais cette approche peut également être étendue à d'autres cadres:
Ajoutez une interface IDialogService au projet VM:
Exposez une propriété statique publique de
IDialogService
type dans votreViewModelLocator
, mais laissez la partie d'enregistrement pour que la couche View s'exécute. C'est la clé .:Ajoutez une implémentation de cette interface dans le projet App.
ShowMessage
,AskBooleanQuestion
etc.), d'autres sont spécifiques à ce projet et utilisent desWindow
s personnalisés . Vous pouvez ajouter d'autres fenêtres personnalisées de la même manière. La clé est de conserver les éléments spécifiques à l'interface utilisateur dans la couche View et d'exposer simplement les données renvoyées à l'aide de POCO dans la couche VM .Effectuez l'enregistrement IoC de votre interface dans la couche View à l'aide de cette classe. Vous pouvez le faire dans le constructeur de votre vue principale (après
InitializeComponent()
appel):Voilà. Vous avez désormais accès à toutes vos fonctionnalités de dialogue sur les couches VM et View. Votre couche VM peut appeler ces fonctions comme ceci:
Autres avantages gratuits
IDialogService
dans votre projet Test et enregistrer cette classe dans IoC dans le constructeur de votre classe de test.Microsoft.Win32
pour accéder aux boîtes de dialogue Ouvrir et Enregistrer. Je les ai laissés de côté, car il existe également une version WinForms de ces boîtes de dialogue, plus quelqu'un peut vouloir créer sa propre version. Notez également que certains des identifiants utilisés dansDialogPresenter
sont des noms de mes propres fenêtres (par exempleSettingsWindow
). Vous devrez soit les supprimer de l'interface et de l'implémentation, soit fournir vos propres fenêtres.DispatcherHelper.Initialize()
début du cycle de vie de votre application.À l'exception de ceux
DialogPresenter
qui sont injectés dans la couche View, les autres ViewModals doivent être enregistrés dansViewModelLocator
, puis une propriété statique publique de ce type doit être exposée pour que la couche View puisse consommer. Quelque chose comme ça:Pour la plupart, vos boîtes de dialogue ne devraient pas avoir de code pour des choses comme la liaison ou la définition de DataContext etc. Vous ne devriez même pas passer des choses en tant que paramètres de constructeur. XAML peut faire tout cela pour vous, comme ceci:
DataContext
Cette configuration vous offre toutes sortes d'avantages au moment de la conception tels que Intellisense et l'auto-complétion.J'espère que cela aide tout le monde.
la source
Je réfléchissais à un problème similaire en demandant à quoi devrait ressembler le modèle de vue pour une tâche ou une boîte de dialogue .
Ma solution actuelle ressemble à ceci:
Lorsque le modèle de vue décide qu'une entrée utilisateur est requise, il extrait une instance de
SelectionTaskModel
avec les choix possibles pour l'utilisateur. L'infrastructure s'occupe de faire apparaître la vue correspondante, qui en temps voulu appellera laChoose()
fonction au choix de l'utilisateur.la source
J'ai lutté avec le même problème. J'ai trouvé un moyen d'intercommuniquer entre le View et le ViewModel. Vous pouvez lancer l'envoi d'un message du ViewModel à la vue pour lui dire d'afficher une boîte de message et il fera rapport avec le résultat. Ensuite, le ViewModel peut répondre au résultat renvoyé par la vue.
Je le démontre dans mon blog :
la source
J'ai écrit un article assez complet sur ce sujet et j'ai également développé une bibliothèque de pop-in pour MVVM Dialogs. L'adhésion stricte à MVVM est non seulement possible mais très propre lorsqu'elle est correctement mise en œuvre, et elle peut être facilement étendue à des bibliothèques tierces qui n'y adhèrent pas elles-mêmes:
https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM
la source
Désolé, mais je dois intervenir. J'ai parcouru plusieurs des solutions suggérées avant de trouver l'espace de noms Prism.Wpf.Interactivity dans le projet Prism. Vous pouvez utiliser les demandes d'interaction et l'action de fenêtre contextuelle pour ouvrir une fenêtre personnalisée ou pour des besoins plus simples, des fenêtres contextuelles de notification et de confirmation sont intégrées. Celles-ci créent de véritables fenêtres et sont gérées comme telles. vous pouvez passer un objet de contexte avec toutes les dépendances dont vous avez besoin dans la boîte de dialogue. Nous utilisons cette solution dans mon travail depuis que je l'ai trouvée. Nous avons de nombreux développeurs seniors ici et personne n'a rien trouvé de mieux. Notre solution précédente était le service de dialogue en superposition et l'utilisation d'une classe de présentateur pour y arriver, mais vous deviez avoir des usines pour tous les modèles de vue de dialogue, etc.
Ce n'est pas anodin mais ce n'est pas non plus super compliqué. Et il est intégré à Prism et est donc la meilleure (ou meilleure) pratique IMHO.
Mes 2 cents!
la source
EDIT: oui, je suis d'accord que ce n'est pas une approche MVVM correcte et j'utilise maintenant quelque chose de similaire à ce qui est suggéré par blindmeis.
L'une des façons dont vous pourriez y parvenir est
Dans votre modèle de vue principale (où vous ouvrez le modal):
Et dans votre fenêtre modale View / ViewModel:
XAML:
ViewModel:
ou similaire à ce qui est affiché ici WPF MVVM: Comment fermer une fenêtre
la source