Gestion des dialogs dans WPF avec MVVM

Dans le modèle MVVM pour WPF, la gestion des boîtes de dialog est l’une des opérations les plus complexes. Comme votre modèle de vue ne sait rien de la vue, la communication par dialog peut être intéressante. Je peux exposer une ICommand que lorsque la vue l’invoque, une boîte de dialog peut apparaître.

Est-ce que quelqu’un connaît un bon moyen de gérer les résultats des dialogs? Je parle de boîtes de dialog Windows telles que MessageBox.

L’une des façons de procéder était d’avoir un événement sur le modèle de vue auquel la vue serait abonnée lorsqu’une boîte de dialog était requirejse.

public event EventHandler RequiresDeleteDialog; 

C’est correct, mais cela signifie que la vue nécessite du code, ce que je voudrais éviter.

Je suggère de renoncer aux dialogs modaux des années 1990 et d’implémenter un contrôle en tant que superposition (canevas + positionnement absolu) avec une visibilité liée à un booléen dans la VM. Plus proche d’un contrôle de type ajax.

Ceci est très utile:

  

un péché:

  

Voici comment j’en ai un implémenté en tant que contrôle utilisateur. En cliquant sur le «x», vous fermez le contrôle dans une ligne de code dans le code de la commande utilisateur. (Comme j’ai mes vues dans un fichier .exe et ViewModels dans une DLL, je ne me sens pas mal à propos du code manipulant l’interface utilisateur.)

Dialogue Wpf

Vous devriez 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 qui permet à ViewModel et aux vues de communiquer via un mécanisme de messagerie à faible couplage.

Vous devriez vérifier le groupe Google Disciples de WPF, et juste rechercher Mediator. Vous serez très heureux avec les 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

Un bon dialog MVVM devrait:

  1. Être déclaré avec seulement XAML.
  2. Obtenez tout son comportement de la liaison de données.

Malheureusement, WPF ne fournit pas ces fonctionnalités. Afficher une boîte de dialog nécessite un appel code-behind à ShowDialog (). La classe Window, qui prend en charge les dialogs, ne peut pas être déclarée dans XAML et ne peut donc pas être facilement connectée à DataContext.

Pour résoudre ce problème, j’ai écrit un contrôle de module XAML qui se trouve dans l’arborescence logique et relaie la liaison de données à une fenêtre et gère l’affichage et le masquage de la boîte de dialog. Vous pouvez le trouver ici: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

C’est vraiment simple à utiliser et ne nécessite aucun changement étrange à votre ViewModel et ne nécessite pas d’événements ou de messages. L’appel de base ressemble à ceci:

  

Vous voulez probablement append un style qui définit l’option d’affichage. Je l’explique dans mon article. J’espère que ceci vous aide.

J’utilise cette approche pour les dialogs avec MVVM.

Tout ce que j’ai à faire maintenant, c’est d’appeler le suivant de mon modèle de vue.

 var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM); 

Ma solution actuelle résout la plupart des problèmes que vous avez mentionnés, mais elle est complètement abstraite des choses spécifiques à la plate-forme et peut être réutilisée. De plus, je n’ai utilisé aucune liaison avec code-behind uniquement avec DelegateCommands qui implémente ICommand. Dialog est essentiellement un View – un contrôle distinct qui a son propre ViewModel et qui 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 Les dialogs modaux avec MVVM et Silverlight 4

J’ai vraiment eu du mal avec ce concept lorsque j’ai appris (encore en apprentissage) le MVVM. Ce que j’ai décidé, et ce que je pense que d’autres ont déjà décidé, mais ce qui n’était pas clair pour moi, c’est ceci:

Ma pensée initiale était qu’un ViewModel ne devrait pas être autorisé à appeler une boîte de dialog directement car il n’a pas à décider comment une boîte de dialog devrait apparaître. À cause de cela, j’ai commencé à réfléchir à la façon dont je pouvais transmettre des messages comme je l’aurais fait dans MVP (à savoir View.ShowSaveFileDialog ()). Cependant, je pense que c’est la mauvaise approche.

Un ViewModel peut appeler directement une boîte de dialog. Cependant, lorsque vous testez un ViewModel, cela signifie que la boîte de dialog apparaîtra lors de votre test ou échouera tous ensemble (jamais vraiment essayé).

Donc, ce qui doit arriver, c’est que pendant les tests, vous devez utiliser une version “test” de votre boîte de dialog. Cela signifie que pour toujours dialogr, vous devez créer une interface et simuler la réponse de la boîte de dialog ou créer un modèle de test qui aura un comportement par défaut.

Vous devriez déjà utiliser un type de localisateur de services ou d’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 manière dont vous modélisez vos boîtes de dialog, vous pouvez contrôler le comportement.

J’espère que cela t’aides.

Utilisez une commande gelable

        
 public class MessageBoxCommand : Freezable, ICommand { public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register( "YesCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register( "OKCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register( "CancelCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register( "NoCommand", typeof (ICommand), typeof (MessageBoxCommand), new FrameworkPropertyMetadata(null) ); public static readonly DependencyProperty MessageProperty = DependencyProperty.Register( "Message", typeof (ssortingng), typeof (MessageBoxCommand), new FrameworkPropertyMetadata("") ); public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register( "MessageBoxButtons", typeof(MessageBoxButton), typeof(MessageBoxCommand), new FrameworkPropertyMetadata(MessageBoxButton.OKCancel) ); public ICommand YesCommand { get { return (ICommand) GetValue(YesCommandProperty); } set { SetValue(YesCommandProperty, value); } } public ICommand OKCommand { get { return (ICommand) GetValue(OKCommandProperty); } set { SetValue(OKCommandProperty, value); } } public ICommand CancelCommand { get { return (ICommand) GetValue(CancelCommandProperty); } set { SetValue(CancelCommandProperty, value); } } public ICommand NoCommand { get { return (ICommand) GetValue(NoCommandProperty); } set { SetValue(NoCommandProperty, value); } } public MessageBoxButton MessageBoxButtons { get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); } set { SetValue(MessageBoxButtonsProperty, value); } } public ssortingng Message { get { return (ssortingng) GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } public void Execute(object parameter) { var messageBoxResult = MessageBox.Show(Message); switch (messageBoxResult) { case MessageBoxResult.OK: OKCommand.Execute(null); break; case MessageBoxResult.Yes: YesCommand.Execute(null); break; case MessageBoxResult.No: NoCommand.Execute(null); break; case MessageBoxResult.Cancel: if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null break; } } public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; protected override Freezable CreateInstanceCore() { throw new NotImplementedException(); } } 

Il y a deux bonnes façons de le faire: 1) un service de dialog (facile, propre) et 2) une vue assistée. L’aide assistée fournit quelques fonctionnalités intéressantes, mais n’en vaut généralement pas la peine.

SERVICE DE DIALOGUE

a) une interface de service de dialog telle que 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 de dlgVm donné (utiliser l’enregistrement ou la convention conteneur ou un ContentPresenter avec type DataTemplates associé). ShowDialogAsync devrait créer un TaskCompletionSource et retourner son object .Task. La classe DialogViewModel elle-même nécessite un événement que vous pouvez appeler dans la classe dérivée lorsque vous souhaitez fermer, et observez dans la vue de la boîte de dialog pour fermer / masquer la boîte de dialog et terminer la source de tâches TaskCompletionSource.

b) Pour utiliser, il suffit d’appeler wait this.DialogService.ShowDialog (myDlgVm) sur votre instance d’une classe dérivée de DialogViewModel. Après l’attente des retours, examinez les propriétés que vous avez ajoutées sur votre machine virtuelle de dialog pour déterminer ce qui s’est passé. vous n’avez même pas besoin d’un rappel.

VISITE ASSISTÉE

Votre vue écoute un événement sur le modèle de vue. Cela pourrait être intégré dans un comportement de mélange pour éviter le code derrière et l’utilisation des ressources si vous êtes tellement enclin (FMI, sous-classe de la classe “Comportement” à voir une sorte de propriété attachée Blendable sur les stéroïdes). Pour l’instant, nous le ferons manuellement sur chaque vue:

a) Créez un OpenXXXXXDialogEvent avec une charge utile personnalisée (une classe dérivée DialogViewModel).

b) Demandez à la vue de s’abonner à l’événement dans son événement OnDataContextChanged. Veillez à masquer et désabonner si l’ancienne valeur! = Null et dans l’événement Unloaded de Window.

c) Lorsque l’événement se déclenche, ouvrez la vue, qui peut se trouver dans une ressource sur votre page, ou localisez-la par convention ailleurs (comme dans l’approche du service de dialog).

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 dans 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, remonter dans l’arborescence visuelle jusqu’à ce qu’un conteneur suffisamment grand soit trouvé.

Cela permet aux boîtes de dialog d’être proches de l’endroit où elles sont réellement utilisées, de ne réduire que la partie de l’application liée à l’activité en cours et de laisser l’utilisateur se déplacer dans l’application sans avoir à repousser manuellement les dialogs. les boîtes de dialog modales s’ouvrent sur différents tabs ou sous-vues.

Je pense que la gestion d’une boîte de dialog 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 dialogs, alors ViewModel dépend de cette implémentation. La manière la plus simple de résoudre ce problème consiste à rendre la vue responsable de l’exécution de la tâche. Si cela signifie afficher un dialog, alors très bien, mais pourrait aussi être un message d’état dans la barre d’état, etc.

Ce que je veux dire, c’est que le but principal du modèle MVVM est de séparer la logique métier de l’interface graphique, vous ne devriez donc pas mélanger la logique graphique (pour afficher une boîte de dialog) dans la couche de gestion (ViewModel).

Une alternative intéressante consiste à utiliser des contrôleurs chargés de montrer les vues (dialogs).

La manière dont cela fonctionne est indiquée par le framework d’application WPF (WAF) .

Pourquoi ne pas simplement créer un événement dans la machine virtuelle et vous abonner à l’événement dans la vue? Cela garderait la logique de l’application et la vue séparées et vous permettrait toujours d’utiliser une fenêtre enfant pour les dialogs.

J’ai implémenté un comportement qui écoute un message provenant de ViewModel. Il est basé sur la solution de Laurent Bugnion, mais comme il n’utilise pas de code et qu’il est plus réutilisable, je pense que c’est plus élégant.

Comment faire en sorte que WPF se comporte comme si MVVM était supporté par défaut

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 / le scénario, il peut également y avoir un déclencheur d’événement qui s’abonne pour afficher les événements du modèle et une ou plusieurs actions à appeler en réponse.

J’ai eu la même situation et enveloppé le 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 à tous les dialogs modaux, au contrôle de navigation des fichiers, etc.

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

Karl Shifflett a créé un exemple d’application pour afficher des boîtes de dialog à l’aide de l’approche de service et de l’approche Prism InteractionRequest.

J’aime l’approche du service – C’est moins flexible, donc les utilisateurs sont moins susceptibles de casser quelque chose 🙂 C’est aussi cohérent avec la partie WinForms de mon application (MessageBox.Show) Mais si vous prévoyez d’afficher beaucoup de dialogs différents, InteractionRequest est un meilleure façon de faire.

http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/

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 très claire. Je fais donc ma propre implémentation d’une boîte de dialog / messagebox / popin, et je la partage!
Je pense que c’est “MVVM proof”, et j’essaie de le rendre simple et correct, mais je suis nouveau sur WPF, alors n’hésitez pas à commenter, ou même à faire une requête pull.

https://github.com/Plasma-Paris/Plasma.WpfUtils

Vous pouvez l’utiliser comme ceci:

 public RelayCommand YesNoMessageBoxCommand { get; private set; } async void YesNoMessageBox() {    var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);    if (result == System.Windows.MessageBoxResult.Yes)        // [...] } 

Ou comme ça si vous voulez un popin plus sophistiqué:

 var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ }); 

Et ça montre des choses comme ça:

2

Après avoir passé du temps avec elle, j’ai finalement trouvé la solution suivante. Les principaux avantages de cette approche sont les suivants:

  1. Implémente le propre IDialogService MVVM Light.
  2. View n’a pas besoin d’append la référence de MVVM Light.
  3. VM n’a pas besoin de faire une activité au niveau de la présentation. Ne nécessite même pas la référence PresentationFramework .
  4. Utilise le propre canal Messenger de MVVM Light, de sorte que les couches de présentation et de VM sont découplées.
  5. Prend en charge les boîtes de dialog avec une valeur de retour, telles que les questions Oui / Non ou les situations OK / Annuler.
  6. Prend en charge les boîtes de dialog personnalisées.

Voici l’implémentation de IDialogService (va dans le projet ViewModel ):

 using System; using System.Linq; using System.Threading.Tasks; namespace VM { public enum MessageBoxButtonVM { OK, OKCancel, YesNo } public enum MessageBoxImageVM { None, Information, Question, Error } public class MessageBoxArgs { public MessageBoxButtonVM Buttons { get; set; } public MessageBoxImageVM Icon { get; set; } public ssortingng Title { get; set; } public ssortingng Message { get; set; } } //For custom dialogs that return a value public class MessageBoxNotificationWithAction { private readonly Action _callback; public MessageBoxArgs Notification { get; set; } public MessageBoxNotificationWithAction(MessageBoxArgs notification, Action callback) { Notification = notification; CheckCallback(callback); _callback = callback; } public virtual void Execute(T argument) { _callback.Invoke(argument); } private static void CheckCallback(Delegate callback) { if (callback == null) { throw new ArgumentNullException(nameof(callback), "Callback must not be null"); } } } ///  /// Provides an implementation-agnostic way of communicating with the user through dialog boxes. Clients must register for communication messages using /// MVVM Light messaging system. ///  public class DialogService : GalaSoft.MvvmLight.Views.IDialogService { private static GalaSoft.MvvmLight.Messaging.IMessenger Messenger = GalaSoft.MvvmLight.Messaging.Messenger.Default; private ssortingng _ProductName = ""; public ssortingng ProductName { get { if (_ProductName == "") { //The following statement returns the Title atsortingbute of the current assembly, as defined in project properties (Assembly Information dialog). var TitleAtsortingb = System.Reflection.Assembly.GetExecutingAssembly().GetCustomAtsortingbutesData().First(x => x.AtsortingbuteType.Name == "AssemblyTitleAtsortingbute"); if (TitleAtsortingb != null) { _ProductName = TitleAtsortingb.ConstructorArguments[0].Value.ToSsortingng(); } else { _ProductName = "Default Application Name"; } } return _ProductName; } } public Task ShowError(Exception error, ssortingng title, ssortingng buttonText, Action afterHideCallback) { return ShowError(error.Message, title, buttonText, afterHideCallback); } public Task ShowMessage(ssortingng message, ssortingng title) { return Task.Run(() => MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error)); } public Task ShowError(ssortingng message, ssortingng title, ssortingng buttonText, Action afterHideCallback) { return Task.Run(() => { MessengerSend(message, title, MessageBoxButtonVM.OK, MessageBoxImageVM.Error); afterHideCallback?.Invoke(); }); } public Task ShowMessage(ssortingng message, ssortingng title, ssortingng buttonText, Action afterHideCallback) { return Task.Run(() => { MessengerSend(message, title); afterHideCallback?.Invoke(); }); } public Task ShowMessage(ssortingng message, ssortingng title, ssortingng buttonConfirmText, ssortingng buttonCancelText, Action afterHideCallback) { if ((buttonConfirmText == "OK" && buttonCancelText == "Cancel") || (buttonConfirmText == "Yes" && buttonCancelText == "No")) { return Task.Run(() => { MessageBoxButtonVM btn; if (buttonConfirmText == "OK") btn = MessageBoxButtonVM.OKCancel; else btn = MessageBoxButtonVM.YesNo; bool Response = false; Messenger.Send(new MessageBoxNotificationWithAction( new MessageBoxArgs() { Buttons = btn, Icon = MessageBoxImageVM.Question, Title = (ssortingng.IsNullOrEmpty(title) ? _ProductName : title), Message = message }, (result) => Response = result )); afterHideCallback?.Invoke(Response); return Response; }); } else throw new ArgumentException($"{nameof(buttonConfirmText)} and {nameof(buttonCancelText)} must either be OK/Cancel or Yes/No."); } ///  /// For debugging purpose only ///  ///  ///  ///  public Task ShowMessageBox(ssortingng message, ssortingng title) => ShowMessage(message, title); private void MessengerSend(ssortingng msg, ssortingng title = "", MessageBoxButtonVM btn = MessageBoxButtonVM.OK, MessageBoxImageVM icon = MessageBoxImageVM.Information) { Messenger.Send(new MessageBoxArgs() { Buttons = MessageBoxButtonVM.OK, Icon = MessageBoxImageVM.Information, Title = (ssortingng.IsNullOrEmpty(title) ? _ProductName : title), Message = msg }); } } } 

Voici la couche de présentation (va dans le projet View )

 using System.Windows; using VM; namespace View { class DialogPresenter { private Window _Parent; public DialogPresenter() { //For simple information boxes GalaSoft.MvvmLight.Messaging.Messenger.Default.Register(this, (arg) => ShowDialog(arg)); //For Yes/No or OK/Cancel dialog boxes. GalaSoft.MvvmLight.Messaging.Messenger.Default.Register>(this, (arg) => arg.Execute(ShowDialog(arg.Notification))); //For notifications that require a ssortingng response (such as Manual Timeslot Description) GalaSoft.MvvmLight.Messaging.Messenger.Default.Register>(this, (arg) => arg.Execute(ShowSsortingngInputDialog(arg.Notification.Title, arg.Notification.Message))); } private bool ShowDialog(MessageBoxArgs arg) { MessageBoxButton btn = MessageBoxButton.OK; MessageBoxImage ico = MessageBoxImage.None; switch (arg.Buttons) { case MessageBoxButtonVM.OK: btn = MessageBoxButton.OK; break; case MessageBoxButtonVM.OKCancel: btn = MessageBoxButton.OKCancel; break; case MessageBoxButtonVM.YesNo: btn = MessageBoxButton.YesNo; break; } switch (arg.Icon) { case MessageBoxImageVM.Error: ico = MessageBoxImage.Error; break; case MessageBoxImageVM.Information: ico = MessageBoxImage.Information; break; case MessageBoxImageVM.None: ico = MessageBoxImage.None; break; case MessageBoxImageVM.Question: ico = MessageBoxImage.Question; break; } bool Result = false; _Parent.Dispatcher.Invoke(() => { var Res = MessageBox.Show(arg.Message, arg.Title, btn, ico); Result = (Res == MessageBoxResult.OK || Res == MessageBoxResult.Yes); }); return Result; } private ssortingng ShowSsortingngInputDialog(ssortingng title, ssortingng description, ssortingng value = "", int maxLength = 100) { ssortingng Result = null; _Parent.Dispatcher.Invoke(() => { //InputBox is a WPF Window I created for taking simple //ssortingng values from the user. This also shows that you can //any custom dialog using this approach. InputBox input = new InputBox(); input.Title = title; input.Owner = _Parent; if (input.ShowDialog(description, value, maxLength).Value) Result=input.Value; else Result=null; }); return Result; } //Call this somewhere at application startup so that the dialog boxes //appear as child windows. public void SetParentWindow(Window parent) { _Parent = parent; } } } 

Je réfléchissais à un problème similaire en demandant à quoi ressemblerait le modèle de vue d’une tâche ou d’un dialog .

Ma solution actuelle ressemble à ceci:

 public class SelectionTaskModel : ViewModel where TChoosable : ViewModel { public SelectionTaskModel(ICollection choices); public ReadOnlyCollection Choices { get; } public void Choose(TChoosable choosen); public void Abort(); } 

Lorsque le modèle de vue décide qu’une entrée utilisateur est requirejse, il extrait une instance de SelectionTaskModel avec les choix possibles pour l’utilisateur. L’infrastructure prend en charge l’affichage de la vue correspondante, qui, en temps voulu, appellera la fonction Choose() avec le choix de l’utilisateur.

J’ai eu du mal avec le même problème. J’ai trouvé un moyen d’intercommunication entre View et ViewModel. Vous pouvez lancer l’envoi d’un message depuis ViewModel à la vue pour lui demander d’afficher une boîte de message et il vous enverra un rapport avec le résultat. Ensuite, le ViewModel peut répondre au résultat renvoyé par la vue.

Je le démontre sur mon blog :

Je suis peut-être de l’avis très impopulaire que le fait de ne compter que sur XAML est à vos yeux extrêmement ressortingctif. Je pense que nous pouvons mieux séparer View et ViewModel en utilisant le code-behind de la vue pour nous aider à implémenter une messagerie ou une collecte de données spécifique à une vue. En permettant à ViewModel d’exécuter sa propre logique et de renvoyer des résultats tels que des propriétés ou en déclenchant des événements, nous pouvons concevoir une vue capable de faire presque tout ce que nous voulons (boîtes de dialog incluses).

Dans un cas spécifique, j’avais besoin d’une boîte de dialog d’entrée de nom qui prenait simplement le nom d’une famille et définissait une propriété dans ViewModel à utiliser ultérieurement. En raison de la quantité d’informations que je devais collecter, le fait d’avoir des dialogs pour attraper des choses comme des noms simplifiait suffisamment l’expérience utilisateur pour qu’elle ne paraisse pas “écrasante”.

Sachant cela, j’ai utilisé un événement de clic sur le bouton spécifique pour appeler une nouvelle boîte de dialog personnalisée que j’avais conçue à cet effet. Lors de l’appel de la boîte de dialog, j’ai transmis le DataContext du Window parent en tant qu’object dans le constructeur de la boîte de dialog. J’ai ensuite paramétré le DataContext du Dialog via ce paramètre.

  public NameEntryDialog(object dataContext) { InitializeComponent(); DataContext = dataContext; } 

Maintenant que les deux vues avaient un DataContext commun, j’ai pu définir n’importe quelle propriété dans ViewModel en liant les nœuds de ma boîte de dialog. Cela ne nécessitait pas de solutions de contournement ou de failles pour fonctionner correctement.

De même, une fois la vérification des données effectuée dans ViewModel (aucun caractère spécial ou nom de caractère unique, etc.), ViewModel a défini deux propriétés: 1) une propriété validationSuccessful et 2) une propriété ErrorMessage.

Once the dialog box was closed, my code-behind simply inspected those properties and called a MessageBox if validation was not successful (displaying the error message set in the property of my ViewModel). You could also accomplish this by subscribing to events in your ViewModel which, when fired, could call a Dialog or MessageBox in your view. I also had the logic of the ViewModel make the CanExecute property of the follow up “save” command “false” so that the user could not continue until those errors (and others during data collection) were corrected.

By using the code-behind I was able to completely separate the View from the ViewModel, and could also perform testing without the need for a view at all.

This also allows me to design new views which can interact with the same properties in the ViewModel independent of the need for a dialog. I could use a UserControl, a dialog, a single Window, etc. As long as I address all of the properties presented in my ViewModel, the sky is the limit. One thing that I’ve found to be true in almost every convoluted attempt at handling this problem was that the dialog box is eventually called SOMEWHERE (strong emphasis on somewhere) in code. Why not just skip all of that hassle and do it right inside your view?

I’ve written a fairly comprehensive article about this very topic and also developed a pop-in library for MVVM Dialogs. Ssortingct adherence to MVVM is not only possible but very clean when implemented properly, and it can be easily extended to third-party libraries that don’t adhere to it themselves:

https://www.codeproject.com/Articles/820324/Implementing-Dialog-Boxes-in-MVVM

Sorry, but I have to chime in. I have been through several of the suggested solutions, before finding the Prism.Wpf.Interactivity namespace in the Prism project. You can use interaction requests and popup window action to either roll a custom window or for simpler needs there are built in Notification and Confirmation popups. These create true windows and are managed as such. you can pass a context object with any dependencies you need in the dialog. We use this solution at my work since I found it. We have numerous senior devs here and noone has come up with anything better. Our previous solution was the dialog service into an overlay and using a presenter class to make it happen, but you had to have factories for all of the dialog viewmodels, etc.

This isn’t sortingvial but it also isn’t super complicated. And it is built in to Prism and is therefore best (or better) practice IMHO.

Mes 2 centimes!

EDIT: yes I agree this is not a correct MVVM approach and I am now using something similar to what is suggested by blindmeis.

One of the way you could to this is

In your Main View Model (where you open the modal):

 void OpenModal() { ModalWindowViewModel mwvm = new ModalWindowViewModel(); Window mw = new Window(); mw.content = mwvm; mw.ShowDialog() if(mw.DialogResult == true) { // Your Code, you can access property in mwvm if you need. } } 

And in your Modal Window View/ViewModel:

XAML:

   

ViewModel:

 public ICommand OkCommand { get { if (_okCommand == null) { _okCommand = new ActionCommand(DoOk, CanDoOk); } return _okCommand ; } } void DoOk(Window win) {  win.DialogResult = true; win.Close(); } bool CanDoOk(Window win) { return true; } 

or similar to what is posted here WPF MVVM: How to close a window