web-dev-qa-db-fra.com

Comment avez-vous implémenté avec succès la fonctionnalité MessageBox.Show () dans MVVM?

J'ai une application WPF qui appelle MessageBox.Show () dans ViewModel (pour vérifier si l'utilisateur souhaite réellement supprimer). Cela fonctionne réellement , mais va à l’encontre de MVVM car le ViewModel ne doit pas déterminer explicitement ce qui se passe dans la vue.

Alors maintenant, je pense comment puis-je implémenter au mieux la fonctionnalité MessageBox.Show () dans mon application MVVM, les options:

  1. Je pourrais avoir un message avec le texte "Es-tu sûr ...?" ainsi que deux boutons Oui et Non dans une bordure de mon XAML, et créez un déclencheur sur le modèle afin qu’il soit réduit/visible en fonction d’un ViewModelProperty appelé AreYourSureDialogueBoxIsVisible , puis, lorsque j’ai besoin de cette boîte de dialogue, attribuez AreYourSureDialogueBoxIsVisible à "true", et gère également les deux boutons via DelegateCommand dans mon ViewModel.

  2. Je pourrais aussi essayer de gérer cela avec des déclencheurs en XAML, de sorte que le bouton Supprimer ne fasse apparaître qu'un élément Border avec le message et les boutons qu'il contient, et que le bouton Oui supprime réellement.

Les deux solutions semblent trop complexes pour ce qui était auparavant quelques lignes de code avec MessageBox.Show ().

De quelle manière avez-vous implémenté avec succès des boîtes de dialogue dans vos applications MVVM?

41
Edward Tanguay

Parmi les deux que vous mentionnez, je préfère l'option n ° 2. Le bouton Supprimer de la page fait simplement apparaître le message "Confirmer la suppression". La boîte de dialogue "Confirmer la suppression" lance la suppression.

Avez-vous consulté les projets diapositives et démonstrations de la gamme WPF de Karl Shifflett ? Je sais qu'il fait quelque chose comme ça. Je vais essayer de me rappeler où.

EDIT: Consultez la démonstration n ° 11 "Validation des données dans MVVM" (EditContactItemsControlSelectionViewModel.DeleteCommand). Karl appelle une fenêtre contextuelle à partir du ViewModal (What !? :-). En fait, j'aime mieux votre idée. Cela semble plus facile à tester à l'unité.

5
Aaron Hoffman

Services à la rescousse. Utiliser Onyx (disclaimer, je suis l’auteur) c’est aussi simple que:

public void Foo()
{
    IDisplayMessage dm = this.View.GetService<IDisplayMessage>();
    dm.Show("Hello, world!");
}

Dans une application en cours d'exécution, cela appellera indirectement MessageBox.Show ("Hello, world!"). Lors du test, le service IDisplayMessage peut être simulé et fourni au ViewModel afin de faire tout ce que vous voulez accomplir pendant le test.

12
wekempf

Pour développer la réponse de Dean Chalk maintenant que son lien est kaput:

Dans le fichier App.xaml.cs, nous associons la boîte de dialogue de confirmation au modèle de vue.

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var confirm = (Func<string, string, bool>)((msg, capt) => MessageBox.Show(msg, capt, MessageBoxButton.YesNo) == MessageBoxResult.Yes);
    var window = new MainWindowView();
    var viewModel = new MainWindowViewModel(confirm);
    window.DataContext = viewModel;
    ...
}

Dans la vue (MainWindowView.xaml), nous avons un bouton qui appelle une commande dans le ViewModel

<Button Command="{Binding Path=DeleteCommand}" />

Le viewmodel (MainWindowViewModel.cs) utilise une commande de délégué pour afficher le message "Êtes-vous sûr?" dialogue et effectuer l'action. Dans cet exemple, il s'agit d'une SimpleCommand semblable à this , mais toute implémentation de ICommand devrait le faire.

private readonly Func<string, string, bool> _confirm;

//constructor
public MainWindowViewModel(Func<string, string, bool> confirm)
{
    _confirm = confirm;
    ...
}

#region Delete Command
private SimpleCommand _deleteCommand;
public ICommand DeleteCommand
{
    get { return _deleteCommand ?? (_deleteCommand = new SimpleCommand(ExecuteDeleteCommand, CanExecuteDeleteCommand)); }
}

public bool CanExecuteDeleteCommand()
{
    //put your logic here whether to allow deletes
    return true;
}

public void ExecuteDeleteCommand()
{
    bool doDelete =_confirm("Are you sure?", "Confirm Delete");
    if (doDelete)
    {
        //delete from database
        ...
    }
}
#endregion
4
JumpingJezza

Je viens de créer une interface (IMessageDisplay ou similaire) qui est injectée dans la machine virtuelle et qui dispose de méthodes telles que MessageBox (ShowMessage (), etc.). Vous pouvez implémenter cela en utilisant une boîte à messages standard, ou quelque chose de plus spécifique à WPF (j'utilise celui-ci sur CodePlex un type appelé Prajeesh).

De cette façon, tout est séparé et testable.

3
Grant Crofton

Qu'en est-il de la création d'un événement tel que "MessageBoxRequested" géré dans le codebehind de la vue (de toute façon, il s'agit d'un code d'affichage uniquement, de sorte que je ne vois aucun problème à ce que ce code soit affiché sur le codebehind).

3

Juste au cas où quelqu'un d'autre est encore en train de lire et insatisfait:

Je voulais juste gérer les MessageBox de type 'notification' (c'est-à-dire que la DialogResult m'importait peu), mais le problème que j'ai avec la plupart des solutions que j'ai lues est qu'elles semblent vous obliger indirectement à choisir l'implémentation de View ( c’est-à-dire qu’il ya actuellement un MessageBox.Show, mais si je décide plus tard de modifier la visibilité d’un panneau caché directement dans ma vue, cela ne maille pas très bien avec une interface INotification transmise au ViewModel).

Alors je suis allé vite et sale:

Le ViewModel a une propriété string NotificationMessage, avec les modifications notifiées à PropertyChanged.

La vue s'abonne à PropertyChanged et, si elle voit la propriété NotificationMessage traverser, fait ce qu'elle veut.

OK, cela signifie donc que la vue a un code en arrière-plan et que le nom de PropertyChanged est codé en dur, mais il le serait quand même dans le XAML. Et cela signifie que j'évite tout ce qui est comme les convertisseurs pour Visibility et les propriétés permettant de dire si la notification est toujours visible ou non.

(Certes, ceci est juste pour un cas d'utilisation limité (feu et oublie), je n'ai pas beaucoup réfléchi à la façon dont je pourrais l'étendre.)

1
Benjol

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 et est plus réutilisable, je pense que c’est plus élégant.

Vérifiez le ici

1
Elad Katz

J'ai créé un contrôle d'encapsulation MessageBox simple à utiliser dans une solution MVVM pure tout en permettant les tests unitaires. Les détails sont dans mon blog http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

mukapu

1
mukapu

WPF & Silverlight MessageBoxes

MVVM pris en charge

http://slwpfmessagebox.codeplex.com/

1
Salar

Il existe de nombreuses réponses à ce sujet qui vont de la création d'une classe personnalisée à l'utilisation de bibliothèques tierces. Je dirais que vous devez utiliser une bibliothèque tierce si vous voulez des pop-ups sympas avec de beaux visuels. 

Mais si vous souhaitez simplement utiliser la boîte de message standard de Microsoft pour votre application WPF, voici une implémentation conviviale MVVM/test:

Au départ, je pensais que je voudrais simplement hériter de la boîte de message et l'envelopper avec une interface, mais je ne pouvais pas, car Message n'a pas de constructeur public. Voici donc la solution "facile":

En décompilant la boîte de message dans Visual Studio, vous pouvez voir toutes les surcharges de méthodes, j'ai vérifié celles que je voulais, puis créé une nouvelle classe et ajouté les méthodes, enveloppées dans une interface et ta-da! Vous pouvez maintenant utiliser ninject pour lier l'interface et la classe, l'injecter et utiliser Moq pour tester l'unité e.t.c.

Créez une interface (n'a ajouté que quelques surcharges car je n'en ai pas besoin toutes):

public interface IMessageBox
    {
        /// <summary>Displays a message box that has a message, title bar caption, and button; and that returns a result.</summary>          
        MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button);

        /// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.</summary>           
        MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon);

        /// <summary>Displays a message box that has a message and title bar caption; and that returns a result.</summary>            
        MessageBoxResult Show(string messageBoxText, string caption);
    }

Ensuite, nous avons la classe qui en héritera:

public class MessageBoxHelper : IMessageBox
    {
        /// <summary>Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.</summary>            
        public MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button,
            MessageBoxImage icon)
        {
            return MessageBox.Show(messageBoxText, caption, button, icon, MessageBoxResult.None,
                MessageBoxOptions.None);
        }

        /// <summary>Displays a message box that has a message, title bar caption, and button; and that returns a result.</summary>            
        public MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button)
        {
            return MessageBox.Show(messageBoxText, caption, button, MessageBoxImage.None, MessageBoxResult.None,
                MessageBoxOptions.None);
        }

        /// <summary>Displays a message box that has a message and title bar caption; and that returns a result.</summary>            
        public MessageBoxResult Show(string messageBoxText, string caption)
        {
            return MessageBox.Show(messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None,
                MessageBoxResult.None, MessageBoxOptions.None);
        }

        /// <summary>Displays a message box that has a message and that returns a result.</summary>           
        public MessageBoxResult Show(string messageBoxText)
        {
            return MessageBox.Show(messageBoxText, string.Empty, MessageBoxButton.OK, MessageBoxImage.None,
                MessageBoxResult.None, MessageBoxOptions.None);
        }
    }

Maintenant, utilisez-le simplement lorsque vous injectez du e.t.c et que vous obtenez un abstraction fragile qui fera l'affaire ... ce qui est bien, selon l'endroit où vous allez l'utiliser. Mon cas est une application simple uniquement destinée à faire quelques choses, donc inutile de concevoir une solution. J'espère que ça aide quelqu'un.

0
JohnChris

Je voudrais juste le jeter à partir de la VM. Je ne veux pas avoir à utiliser le service de quelqu'un d'autre ou à écrire le mien juste pour lancer une boîte de message.

0
Halmut

J'ai récemment rencontré ce problème dans lequel je devais remplacer le MessageBox.Show dans ViewModels par un mécanisme de boîte de message de plainte entièrement MVVM.

Pour ce faire, j'ai utilisé InteractionRequest<Notification> et InteractionRequest<Confirmation> ainsi que des déclencheurs d'interaction et écrit mes propres vues pour la boîte de message.

Ce que j'ai implémenté est publié ici

0
A J Qarshi