2009-07-08 10 views
38

Ich habe eine WPF-Anwendung , die MessageBox.Show() zurück in das ViewModel aufruft (um zu überprüfen, ob der Benutzer wirklich löschen möchte). Dies funktioniert tatsächlich, aber geht gegen den Strich von MVVM, da das ViewModel nicht explizit bestimmen sollte, was auf der View passiert.Wie haben Sie die Funktion MessageBox.Show() in MVVM erfolgreich implementiert?

So, jetzt denke ich wie kann ich die MessageBox.Show() Funktionalität in meiner MVVM Anwendung am besten umsetzen, Optionen:

  1. ich eine Nachricht mit dem Text haben könnte „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 es auf einer ViewModelProperty namens AreYourSureDialogueBoxIsVisible reduziert/sichtbar ist, und dann, wenn ich dieses Dialogfeld benötigen, AreYourSureDialogueBoxIsVisible zuweisen "true" und handhabe auch die beiden Buttons per DelegateCommand 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 einige Border-Element mit der Nachricht und Schaltflächen erscheinen, und die Ja-Taste löschte tatsächlich.

Beiden Lösungen scheinen zu komplex zu sein für das, was verwendet, um ein paar Zeilen Code mit MessageBox.Show zu sein().

Inwiefern haben Sie Dialogboxen erfolgreich in Ihren MVVM-Anwendungen implementiert?

+0

ähnliche Frage: http://stackoverflow.com/questions/315180/model-view-presenter-and-modal-dialog-boxes-how-to –

Antwort

5

Von den beiden, die Sie erwähnen, bevorzuge ich Option # 2. Die Löschen-Schaltfläche auf der Seite macht nur den Dialog "Löschen bestätigen" angezeigt. Der Dialog "Löschen bestätigen" startet den Löschvorgang.

Haben Sie Karl Shifflett WPF Line Of Business Slides and Demos ausgecheckt? Ich weiß, dass er so etwas macht. Ich werde versuchen, mich zu erinnern, wo.

EDIT: Überprüfen Sie Demo # 11 "Datenüberprüfung in MVVM" (EditContactItemsControlSelectionViewModel.DeleteCommand). Karl ruft ein Popup aus dem ViewModal (What !? :-) auf. Ich mag deine Idee eigentlich besser. Scheint einfacher zu Unit Test.

+0

ich diese 1 mit Lösung # endlich gelöst. Es öffnet kein Fenster, sondern fügt ein Border-Element am oberen Bildschirmrand ein, indem es von Visibility = collapsed auf Visibility = Visible gesetzt wird. Ich habe die DelegateCommands erstellt: TurnOnDialogueBoxDelete, DeleteItem und CancelDialogueBoxDelete. Ich habe die ViewModelProperties: DialogueBoxDeleteStatus, ItemIdMarkedForDeletion und DialogueBoxDeleteText erstellt. Es ist also nicht so einfach wie eine einfache MessageBox.Show(), aber es funktioniert gut. Ich bin sicher, dass irgendwann davon abstrahiert werden kann, wenn andere Dialogfelder benötigt werden. –

+0

Und es kann Einheit getestet werden. :-) –

11

Dienstleistungen zur Rettung. Mit Onyx (Disclaimer, ich bin der Autor) dies so einfach wie:

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

In einer laufenden Anwendung, wird dies MessageBox.Show indirekt nennen („Hallo, Welt!“). Während des Tests kann der IDisplayMessage-Dienst für das ViewModel gemondet und bereitgestellt werden, um das zu tun, was Sie während des Tests erreichen möchten.

+0

Ich frage mich, ob indirekter Zugriff auf View immer noch nach MVVM-Praxis betrachtet wird. – VCD

+0

Es war nie von den meisten, aber ich stimme einfach nicht zu. Es gibt hier keine enge Verbindung, also würde ich jemanden herausfordern, * warum * hier ein Designproblem zu zeigen. Was im obigen Beispiel ein Anti-Pattern ist, ist die Verwendung des Service-Locators anstelle der Abhängigkeitsinjektion. Unabhängig davon hängt das IDisplayMessage-Konzept, die eigentliche Antwort hier, weder vom Service-Locator noch indirekt vom Zugriff auf eine View ab. – wekempf

0

Ich würde es einfach von der VM werfen. Ich möchte nicht den Dienst von jemand anderem benutzen oder mein eigenes schreiben, nur um eine Nachricht zu werfen.

+6

Ja, aber wenn Sie Ihr Ansichtsmodell in einem Komponententest testen, was wird passieren? Wird Ihr Test das Warten auf einen Klick auf "Ja" oder "Nein" blockieren? – esylvestre

1

Ich schaffe nur eine Schnittstelle (IMessageDisplay oder ähnliches), die in die VM eingespritzt wird, und es hat Methoden wie eine MessageBox (Showmessage() usw.). Sie können das mit einer Standard-Message-Box oder etwas WPF-spezifischer implementieren (ich verwende this one on CodePlex einen Kerl namens Prajeesh).

So ist alles getrennt und testbar.

1

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

Check it out here

3

Was ist in der Code-Behind der eine Veranstaltung wie "MessageBoxRequested" behandelt Anheben (ohnehin ist es nur anzeigen Code, den ich so sehe kein Problem mit diesem Code auf dem Codebehind).

+0

Wahr, MVVM ist für mich die Verbindung zwischen dem Modell und der Ansicht. Aber die Ansicht sollte sich auch auf sich selbst beziehen. – jeuxjeux20

1

Nur falls jemand noch liest und unzufrieden:

I ‚Benachrichtigung‘ Typ Messageboxes (dh ich nicht kümmern uns um die DialogResult) nur zu handhaben wollte, aber das Problem, dass ich mit Die meisten Lösungen, über die ich gelesen habe, sind, dass sie dich indirekt zwangen, deine View-Implementierung zu wählen (das heißt, momentan habe ich eine MessageBox.Show, aber wenn ich mich später dazu entscheide, mit der Sichtbarkeit eines versteckten Panels direkt in meiner View herumzuspielen, das wird nicht sehr gut mit einer INotification Schnittstelle, die an das ViewModel übergeben wird, zusammenpassen).

Also ging ich für quick and dirty:

Das Ansichtsmodell hat eine string NotificationMessage Eigenschaft, mit zu PropertyChanged mitgeteilten Änderungen.

Die Ansicht abonniert PropertyChanged, und wenn es die NotificationMessage Eigenschaft durchkommen sieht, tut was immer es will.

OK, das heißt, die View hat Code-Behind, und der Name PropertyChanged ist hart-codiert, aber es wäre ohnehin im XAML fest codiert. Und das bedeutet, ich vermeide alle Dinge wie Konverter für Sichtbarkeit und Eigenschaften, um zu sagen, ob die Benachrichtigung noch sichtbar ist oder nicht.

(Zugegebenermaßen ist dies nur für einen begrenzten Anwendungsfall (Feuer und vergessen), habe ich nicht gegeben viel Gedanken gemacht, wie ich will, kann er verlängern.)

0

ich auf dieses Problem vor kurzem kam, wo ich musste die MessageBox.Show in den ViewModels mit einigen vollständig MVVM Beschwerde Nachricht Box Mechanismus ersetzen.

Um dies zu erreichen, habe ich InteractionRequest<Notification> und InteractionRequest<Confirmation> zusammen mit Interaktionsauslösern und schrieb meine eigenen Ansichten für die Nachrichtenbox.

Was ich umgesetzt habe veröffentlicht here

3

auf Dean Chalk Antwort erweitern jetzt, dass seine Verbindung kaput ist:

In den App.xaml.cs Datei wir den Bestätigungsdialog mit dem Ansichtsmodell anschließen .

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 in das Ansichtsmodell

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

Viewmodel (MainWindowViewModel.cs) verwendet einen Delegierten Befehl zu zeigen, ruft die " Bist du sicher?" Dialog und führen Sie die Aktion aus. In diesem Beispiel ist es ein SimpleCommand ähnlich wie this, aber jede Implementierung von ICommand sollte 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 
Verwandte Themen