wake-up-neo.net

Wie haben Sie die MessageBox.Show () -Funktionalität erfolgreich in MVVM implementiert?

Ich habe eine WPF-Anwendung die MessageBox.Show () im ViewModel aufruft (um zu überprüfen, ob der Benutzer wirklich löschen möchte). Dies funktioniert tatsächlich , aber widerspricht der MVVM-Struktur da das ViewModel nicht explizit bestimmen sollte, was in der Ansicht geschieht.

Nun überlege ich mir wie kann ich die MessageBox.Show () -Funktionalität am besten implementieren in meiner MVVM-Anwendung die folgenden Optionen:

  1. Ich könnte eine Nachricht mit dem Text "Bist du sicher ...?" zusammen mit zwei Schaltflächen Ja und Nein alle in einem Rahmen in meinem XAML, und erstellen Sie einen Auslöser für die Vorlage, so dass sie basierend auf einer ViewModelProperty mit dem Namen AreYourSureDialogueBoxIsVisible reduziert/sichtbar ist AreYourSureDialogueBoxIsVisible to "true", und behandeln Sie auch die beiden Schaltflächen über DelegateCommand zurück in meinem ViewModel.

  2. Ich könnte auch irgendwie versuchen, dies mit Triggern in XAML zu behandeln, so dass die Schaltfläche "Löschen" tatsächlich nur ein Randelement mit der Meldung und den Schaltflächen darin anzeigt und die Schaltfläche "Ja" das tatsächliche Löschen ausführt.

Beide Lösungen scheinen zu komplex zu sein für ein paar Zeilen Code mit MessageBox.Show ().

Inwiefern haben Sie erfolgreich Dialogfelder in Ihre MVVM-Anwendungen implementiert?

41
Edward Tanguay

Von den beiden, die Sie erwähnen, bevorzuge ich Option # 2. Mit der Schaltfläche "Löschen" auf der Seite wird nur das Dialogfeld "Löschen bestätigen" angezeigt. Mit dem "Confirm Delete Dialog" wird der Löschvorgang tatsächlich gestartet.

Haben Sie Karl Shifflett's WPF-Produktreihe mit Demos und Demos ausgecheckt? Ich weiß, dass er so etwas tut. Ich werde versuchen mich zu erinnern wo.

BEARBEITEN: Schauen Sie sich Demo # 11 "Datenvalidierung in MVVM" an (EditContactItemsControlSelectionViewModel.DeleteCommand). Karl ruft ein Popup aus dem ViewModal (What !? :-) auf. Ich mag deine Idee eigentlich besser. Scheint einfacher zu Unit-Tests.

5
Aaron Hoffman

Dienste zur Rettung. Mit Onyx (Haftungsausschluss, ich bin der Autor) ist dies so einfach wie:

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

In einer laufenden Anwendung wird MessageBox.Show ("Hallo, Welt!") Indirekt aufgerufen. Beim Testen kann der IDisplayMessage-Dienst verspottet und dem ViewModel zur Verfügung gestellt werden, um das zu tun, was Sie während des Tests tun möchten.

12
wekempf

Um Dean Chalks Antwort jetzt zu erläutern, dass sein Link kaput ist:

In der Datei App.xaml.cs verbinden wir den Bestätigungsdialog mit dem Viewmodel.

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;
    ...
}

In der Ansicht (MainWindowView.xaml) haben wir eine Schaltfläche, die einen Befehl im ViewModel aufruft

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

Das Ansichtsmodell (MainWindowViewModel.cs) verwendet einen Delegatenbefehl, um die Meldung "Sind Sie sicher?" Dialog und führen Sie die Aktion aus. In diesem Beispiel handelt es sich um eine SimpleCommand, die this ähnelt, aber jede Implementierung von ICommand sollte dies tun.

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

Ich erstelle einfach eine Schnittstelle (IMessageDisplay oder ähnliches), die in die VM injiziert wird und über Methoden wie eine MessageBox (ShowMessage () usw.) verfügt. Sie können dies mit einer Standard-Messagebox oder etwas mehr WPF-spezifischem implementieren (ich verwende dieses auf CodePlex einen Typ namens Prajeesh).

Auf diese Weise ist alles getrennt und überprüfbar.

3
Grant Crofton

Was ist mit dem Auslösen eines Ereignisses wie "MessageBoxRequested" im Codebehind der View (sowieso nur View-Code, daher sehe ich kein Problem damit, dass sich dieser Code im Codebehind befindet).

Nur falls noch jemand liest und unzufrieden ist:

Ich wollte nur "MessageBoxes vom Typ Benachrichtigung" behandeln (dh die DialogResult ist mir egal), aber das Problem, das ich mit den meisten Lösungen habe, die ich gelesen habe, ist, dass sie Sie indirekt dazu zwingen, Ihre View-Implementierung zu wählen ( Das heißt, derzeit habe ich einen MessageBox.Show, aber wenn ich mich später dazu entscheide, direkt mit der Sichtbarkeit eines ausgeblendeten Bedienfelds direkt in meiner Ansicht zu arbeiten, passt das nicht sehr gut zu einer INotification-Schnittstelle, die an ViewModel übergeben wird.

Also ging ich schnell und schmutzig:

Das ViewModel verfügt über eine string NotificationMessage-Eigenschaft, bei der Änderungen an PropertyChanged gemeldet werden.

Die Ansicht abonniert PropertyChanged, und wenn sie sieht, dass die NotificationMessage-Eigenschaft durchkommt, macht sie, was sie will.

OK, das heißt, die View hat Code-Behind und der Name von PropertyChanged ist hartcodiert, aber er wäre in der XAML trotzdem hartcodiert. Und das heißt, ich vermeide alle Dinge wie Konverter für Sichtbarkeit und Eigenschaften, um zu sagen, ob die Benachrichtigung noch sichtbar ist oder nicht.

(Zugegeben, dies ist nur für einen begrenzten Anwendungsfall (Feuer und Vergessen), ich habe nicht viel darüber nachgedacht, wie ich es erweitern möchte.)

1
Benjol

Ich habe ein Verhalten implementiert, das eine Nachricht vom ViewModel abhört. Es basiert auf der Lösung von Laurent Bugnion, aber da es keinen Code verwendet und wiederverwendbar ist, denke ich, dass es eleganter ist.

Schau es dir hier an

1
Elad Katz

Ich habe eine einfache MessageBox-Wrapper-Steuerung erstellt, die wir in einer reinen MVVM-Lösung verwenden können und dennoch die Funktion zum Testen von Einheiten ermöglichen. Die Details befinden sich in meinem Blog http://geekswithblogs.net/mukapu/archive/2010/03/12/de/user-prompts-messagebox-with-mvvm.aspx

mukapu

1
mukapu

WPF- und Silverlight-MessageBoxen

MVVM unterstützt

http://slwpfmessagebox.codeplex.com/

1
Salar

Es gibt so viele Antworten zu diesem Thema, die vom Erstellen einer benutzerdefinierten Klasse bis zur Verwendung von Bibliotheken von Drittanbietern variieren. Ich würde sagen, verwenden Sie eine Bibliothek von Drittanbietern, wenn Sie mit Nice-Visuals coole Pop-Ups wünschen. 

Wenn Sie jedoch nur das reguläre Meldungsfeld von Microsoft für Ihre WPF-App verwenden möchten, finden Sie hier eine MVVM-/Gerätetest-freundliche Implementierung:

Anfangs dachte ich, ich würde einfach von Message Box erben und sie mit einer Schnittstelle umschließen, aber ich konnte aufgrund von Message Box keinen öffentlichen Konstruktor haben, daher ist hier die "einfache" Lösung:

Decompiling Message Box in Visual Studio: Sie können alle Methodenüberladungen sehen. Ich habe geprüft, welche ich wollte, eine neue Klasse erstellt und die Methoden hinzugefügt, sie mit einer Schnittstelle und einem ta-da! Nun können Sie mit ninject das Interface und die Klasse binden, es injizieren und mit Moq zum Komponententest e.t.c.

Erstellen Sie eine Schnittstelle (nur einige Überladungen hinzugefügt, da ich sie nicht alle brauche):

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);
    }

Dann haben wir die Klasse, die davon erben wird:

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);
        }
    }

Jetzt verwenden Sie dies einfach, wenn Sie e.t.c und den Boom injizieren. Sie haben eine dünne Abstraktion, die den Trick vollbringt ... was je nach Verwendungszweck in Ordnung ist. Mein Fall ist eine einfache App, die nur dazu gedacht ist, ein paar Dinge zu tun, also macht es keinen Sinn, eine Lösung zu entwickeln. Hoffe das hilft jemandem.

0
JohnChris

Ich würde es einfach von der VM werfen. Ich möchte nicht den Dienst eines anderen nutzen oder meinen eigenen schreiben, nur um eine Messagebox zu werfen.

0
Halmut

Ich bin vor kurzem auf dieses Problem gestoßen, bei dem ich MessageBox.Show in ViewModels durch einen vollständig MVVM-Reklamationsnachrichtenmechanismus ersetzen musste.

Um dies zu erreichen, habe ich InteractionRequest<Notification> und InteractionRequest<Confirmation> zusammen mit den Interaktionstriggern verwendet und meine eigenen Ansichten für das Meldungsfeld geschrieben.

Was ich implementiert habe, ist veröffentlicht hier

0
A J Qarshi