2009-11-03 20 views
25

Hat jemand Beispiele dafür, einen Fensterdialog mit MVVM (Prism) anzuzeigen? - Zum Beispiel ein Fenster mit Konfigurationseinstellungen, wenn ein Befehl ausgeführt wird.WPF MVVM-Dialogbeispiel

Alle Beispiele die ich gesehen habe den Mediator Muster verwenden, die in Ordnung ist, aber sie haben auch alle einen Verweis auf die Ansicht in der Ansicht Modell, das nicht ideal ist (wir verwenden Datatemplates)

Dank

Antwort

12

Die Art, wie ich dies tue, benutzt auch das Mediatormuster. Wenn das ViewModel einen Dialog anzeigen möchte, wird eine Nachricht gesendet, die im Hauptfenster der Anwendung angezeigt wird. Die Nachricht enthält eine Instanz des ViewModel, das vom Dialog verwendet wird.

Das Hauptfenster erstellt dann eine Instanz des Dialogfensters, übergibt das Ansichtsmodell an es und zeigt das Dialogfeld an. Das Ergebnis des Dialogs wird in der ursprünglichen Nachricht an den Aufrufer zurückgegeben.

Es sieht ungefähr so ​​aus:

Ihrer Ansicht nach Modell:

DialogViewModel viewModel = new DialogViewModel(...); 
ShowDialogMessage message = new ShowDialogMessage(viewModel); 

_messenger.Broadcast(message); 

if (message.Result == true) 
{ 
    ... 
} 

Im Hauptfenster Code-Behind:

void RecieveShowDialogMessage(ShowDialogMessage message) 
{ 
    DialogWindow w = new DialogWindow(); 
    w.DataContext = message.ViewModel; 
    message.Result = w.ShowDialog(); 
} 

Ich hoffe, das ist genug, Ihnen die Idee zu geben. ..

+0

Danke für die Antwort. Könnten Sie bitte das folgende Szenario erläutern? Angenommen, Sie zeigen ein Ansichtenmodell im Dialogfenster mit den Befehlen Speichern und Abbrechen an. Wenn der Benutzer in der Ansicht auf Speichern klickt (an den Befehl SaveCommand gebunden), möchten Sie vielleicht eine Überprüfung durchführen und die Konfiguration speichern. Nur wenn dies erfolgreich ist, wird der Dialog geschlossen. Ich kann anscheinend nicht verstehen, wie die VM das DialogResult der Ansicht einstellen würde (daher den Dialog schließen). Danke nochmal. – Oll

+0

Es gibt viele Möglichkeiten, die VM mit der Ansicht zu interagieren, ohne die Abstraktion zu unterbrechen. Zum Beispiel hat die Basisklasse des Dialogansichtsmodells in der Regel einige Basiseigenschaften und Methoden, um das Anzeigen von Dialogen etwas einfacher zu machen (Ich habe normalerweise eine Schnittstelle für ein Dialoganzeigemodell, das Dinge wie eine Methode enthält, die auch beim Laden von Ansichten ausgeführt werden soll als das Ergebnis des Dialogs, Befehle commit/abort, etc). Eine einfache Möglichkeit, das Ansichtsmodell dazu zu bringen, die Ansicht zum Schließen anzuweisen, besteht darin, eine Eigenschaft verfügbar zu machen (z. B. CanClose). – Egor

+0

Erstellen Sie dann in Ihrem Dialogfeld eine entsprechende Abhängigkeitseigenschaft, und wenn die Änderungen des Datenkontexts eine Bindung zwischen den beiden festlegen, können Sie mit jeder Logik im geänderten Ereignishandler Ihrer gebundenen Eigenschaft umgehen. Andererseits ist es noch einfacher, ein "close" -Ereignis in Ihrem Dialog-ViewModel einfach zu exponieren, und im Dialog-Datenkontext ändert der Handler dieses Ereignis, der Handler kann das entsprechende Dialog-Ergebnis zuweisen und das Fenster schließen. – Egor

0

Sie könnten an der folgenden Beispielanwendung interessiert sein:

http://compositeextensions.codeplex.com

Es verwendet Prism2 mit dem PresentationModel (aka MVVM) Muster. Die Beispielanwendung enthält einen modalen Dialog.

2

Wie ich oben Ihren Kommentar verstanden habe, geht es bei der Frage nicht so sehr darum, die Dialoge zu zeigen, als ob sie versteckt würden. Es gibt zwei Möglichkeiten, dieses Problem zu lösen:

  1. Verwenden Sie Standard-Dialogfenster, um die Ansicht zu implementieren. Dies würde eine lose gekoppelte Art der Kommunikation zwischen View und ViewModel erfordern, damit ViewModel die View darüber informieren kann, dass das Schließen möglich ist, ohne einen Verweis auf eine Ansicht zu haben.

    Es gibt mehrere Frameworks, die es erlauben würden - Prism's Event Aggregatoren wären einer davon. In diesem Szenario würde View ein Ereignis (z. B. MyDialogResultValidated) abonnieren, und beim Empfangen des Ereignisses würde es das DialogResult accordingly setzen. ViewModel (in seinem SaveCommand) würde das Ereignis auslösen, wenn die Validierung erfolgreich war.

  2. Verwenden Sie kein Standarddialogfenster zum Implementieren der Ansicht. Dies würde eine Überlagerung erfordern, die die Modalität effektiv emulieren würde.

    In diesem Szenario wird die Sichtbarkeit der Ansicht und des Overlays an die IsVisible-Eigenschaft von ViewModel gebunden, die entsprechend von der SaveCommand-Implementierung oder wann immer ViewModel die Ansicht anzeigen muss.

Der erste Ansatz erfordern würde ein Stück Code in Code-behind hat, erfordert globales Ereignis Zugabe (n) und (wohl) weniger MVVM-ish.Der zweite Ansatz würde das Implementieren (oder Verwenden einer anderen Implementierung) des Overlays erfordern, erfordert jedoch keinen Code im Code-Behind, erfordert kein globales Ereignis und ist (diskutierbar) mehr MVVM-ish .

21

Ich würde einen Dienst verwenden, um den Dialog anzuzeigen. Der Dienst kann dann auch Ansichten mit Ansichtsmodellen verknüpfen.

public interface IDialogService { 
    void RegisterView<TView, TViewModel>() where TViewModel:IDialogViewModel; 
    bool? ShowDialog(IDialogViewModel viewModel); 
} 

public interface IDialogViewModel { 
    bool CanClose(); 
    void Close(); 
} 


RegisterView verbindet nur die Aussicht Typ mit dem Viewmodel-Typ. Sie können diese Links in der Modulinitialisierung einrichten. Dies ist einfacher als zu versuchen, Module dazu zu bringen, Datamemplates in der obersten Ebene Ihrer Anwendung zu registrieren.

ShowDialog Zeigt das ViewModel, das angezeigt werden soll. Es gibt true, false und null für close genau wie die Window.ShowDialog Methode zurück. Die Implementierung erstellt einfach eine neue Ansicht des Typs TView von Ihrem Container, verbindet sie mit dem bereitgestellten ViewModel und zeigt sie an.

IDialogViewModel bietet einen Mechanismus für das ViewModel zur Verifizierung und zum Abbrechen des Schließens des Dialogfelds.

Ich habe ein Standarddialogfenster mit einer Inhaltskontrolle darin. Wenn ShowDialog aufgerufen wird, erstellt es einen neuen Standarddialog, fügt die Ansicht dem Inhaltssteuerelement hinzu, verbindet das ViewModel und zeigt es an. Der Standarddialog hat bereits die Schaltflächen [OK] und [Abbrechen] mit der entsprechenden Logik, um die richtigen Methoden von IDialogViewModel aufzurufen.

+0

Hallo Cameron! Wie informiert die Ansicht den Dienst, ob OK oder Abbrechen gedrückt wurde? –

+0

Der Standard Weg. Die Code-Implementierung von 'ShowDialog' würde nur die Ansicht finden, die angezeigt werden muss, und dann" ShowDialog "in dieser Ansicht aufrufen und das Ergebnis zurückgeben. –

+0

Wie bewirken Eigenschaftenänderungen innerhalb des IDIalogViewModel die Auswertung von CanExecute der Befehle, die an die Schaltflächen [OK] & [Abbrechen] im Standarddialogfeld gebunden sind? InvalidateRequerySuggested? – jan

0

Es ist nicht Prism, aber dieses hat einen Optionen-Dialog, der vollständig MVVM ist.

2

Ich bin einverstanden, dass die Verwendung von Service zur Anzeige von Dialog nach MVVM-Muster die einfachste Lösung ist. Aber, ich fragte mich auch, ob es drei Assemblys in meinem Projekt Model, ViewModel, View und nach MVVM Muster Assembly ViewModel hat einen Verweis auf Model, und View zu Model und ViewModel wo sollte ich DialogService-Klasse? Wenn ich einen in die ViewModel-Assembly stelle, habe ich keine Chance, eine DialogView-Instanz zu erstellen. Auf der anderen Seite, wenn ich DialogService in der View-Assembly platzieren möchte, wie sollte ich es in meine ViewModel-Klasse einfügen?

Also, ich würde Blick auf Advanced MVVM scenarios with PrismTeil recoment: Mit Interaktion Anfrage Objekte

Als Beispiel für diesen Ansatz:

DialogViewModelBase

public abstract class DialogViewModelBase : ViewModelBase 
{ 
    private ICommand _ok; 

    public ICommand Ok 
    { 
     get { return _ok ?? (_ok = new DelegateCommand(OkExecute, CanOkExecute)); } 
    } 

    protected virtual bool CanOkExecute() 
    { 
     return true; 
    } 

    protected virtual void OkExecute() 
    { 
     _isSaved = true; 
     Close = true; 
    } 

    private ICommand _cancel; 

    public ICommand Cancel 
    { 
     get 
     { 
      return _cancel ?? (_cancel = new DelegateCommand(CancelExecute, CanCancelExecute)); 
     } 
    } 

    protected virtual bool CanCancelExecute() 
    { 
     return true; 
    } 

    protected virtual void CancelExecute() 
    { 
     Close = true; 
    } 

    private bool _isSaved = false; 
    public bool IsSaved 
    { 
     get { return _isSaved; } 
    } 

    private bool _close = false; 

    public bool Close 
    { 
     get { return _close; } 
     set 
     { 
      _close = value; 
      RaisePropertyChanged(() => Close); 
     } 
    } 
} 

CreateUserStoryViewModel:

public class CreateUserStoryViewModel : DialogViewModelBase 
{ 
    private string _name = String.Empty; 

    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      RaisePropertyChanged(() => Name); 
     } 
    } 
} 

CreateUserStoryRequest

private InteractionRequest<Notification> _createUserStoryRequest; 
public InteractionRequest<Notification> CreateUserStoryRequest 
{ 
    get 
    { 
     return _createUserStoryRequest ?? (_createUserStoryRequest = new InteractionRequest<Notification>()); 
    } 
} 

CreateUserStory Befehl

private void CreateUserStoryExecute() 
{ 
    CreateUserStoryRequest.Raise(new Notification() 
    { 
     Content = new CreateUserStoryViewModel(), 
     Title = "Create User Story" 
    }, 
    notification => 
       { 
         CreateUserStoryViewModel createUserStoryViewModel = 
           (CreateUserStoryViewModel)notification.Content; 
         if (createUserStoryViewModel.IsSaved) 
         { 
         _domainContext.CreateUserStory(
new UserStory(){ Name = createUserStoryViewModel.Name, }); 
         } 
       }); 
} 

XAML:

<!--where xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
      xmlns:ir="clr-namespace:Microsoft.Practices.Prism.Interactivity.InteractionRequest;assembly=Microsoft.Practices.Prism.Interactivity"--> 

<i:Interaction.Triggers> 
    <ir:InteractionRequestTrigger SourceObject="{Binding CreateUserStoryRequest}"> 
    <ir:PopupChildWindowAction> 
     <ir:PopupChildWindowAction.ChildWindow> 
     <view:CreateUserStory /> 
     </ir:PopupChildWindowAction.ChildWindow> 
    </ir:PopupChildWindowAction> 
    </ir:InteractionRequestTrigger> 
</i:Interaction.Triggers> 
+0

Vladimir: Ich habe den Ansatz versucht, aber es funktioniert nicht für mich.Können Sie sich was ist _domainContext. Mein Szenario öffnet einen modalen Dialog mit Textfeld, Label und PDFviewer. Ich muss den Datacontext von ModalDialog ändern [d. Übergeben dieser Werte als Viewmodel] entsprechend dem Button (Trigger) click. Jede Idee oder Referenz wäre hilfreich ... Danke –

+0

_domainContext in meinem Beispiel ist eine Art Repository. Es ist nur ein Beispiel und Sie können es durch WCF-Service-Aufruf oder Speichern auf der Festplatte ersetzen. Dieses Beispiel funktioniert für Ihr Szenario. Sie müssen CreateUserStoryViewModel durch Ihr eigenes ModalDialogViewModel mit Eigenschaften Title, Text etc. ersetzen.und binden Sie diese Eigenschaften an das Label und den PdfViewer von ModalDialogView (ModalDialogView sollte statt meiner CreateUserStory-Ansicht verwendet werden). –

+0

Dies scheint nicht mit WPF zu funktionieren. Das Element existiert nicht. Wie auch immer in Silverlight. –