views:

14236

answers:

12

In the MVVM pattern for WPF, handling dialogs is one of the more complex operations. As your view model does not know anything about the view, dialog communication can be interesting. I can expose an ICommand that when the view invokes it, a dialog can appear.

Does anyone know of a good way to handle results from dialogs? I am speaking about windows dialogs such as MessageBox.

One of the ways we did this was have an event on the viewmodel that the view would subscribe to when a dialog was required.

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

This is OK, but it means that the view requires code which is something I would like to stay away from.

+1  A: 

I think that the handling of a dialog should be the responsibility of the view, and the view needs to have code to support that.

If you change the ViewModel - View interaction to handle dialogs then the ViewModel is dependant on that implementation. The simplest way to deal with this problem is to make the View responsible for performing the task. If that means showing a dialog then fine, but could also be a status message in the status bar etc.

My point is that the whole point of the MVVM pattern is to separate business logic from the GUI, so you shouldn't be mixing GUI logic (to display a dialog) in the business layer (the ViewModel).

Cameron MacFarland
The VM would never handle the dialog, in my example it would simpy have an event that would require the dialog to fire up and pass back info in some form of EventArgs. If the view is responsible, how does it pass back info to the VM?
Ray Booysen
Say the VM needs to delete something. The VM calls a method on the View Delete which returns a boolean. The View can then either delete the item directly and return true, or show a confirmation dialog and return true/false depending on the users answer.
Cameron MacFarland
The VM knows nothing about the dialog but only asked the view to delete something, which the view either confirmed or denied.
Cameron MacFarland
I always thought that the point of MVVM was Model: business logic, ViewModel: GUI logic and View: no logic. Which is somehow contradicted by your last paragraph. Please explain!
David Schmitt
It asks the view to delete something? Surely the other way around.
Ray Booysen
I'm a beginner with regard to applying the MVVM pattern however I think it is a mistake to think of the ViewModel as a business layer. Instead my understanding is that the ViewModel holds presentation logic and tracks state necessary for the UI. In my view the Model is the place where business logic and the domain model exists.
jpierson
First it has to be determined if asking for pre-delete confirmation is business logic or view logic. If it is business logic, the DeleteFile method in the model must not do it, but rather return the confirmation question object. This will include a reference to the delegate that does the actual deletion. If it isn't business logic, the VM must build a VM of the question in the DeleteFileCommand, with two ICommand members. One for yes and one for no. There are probably arguments for both views, and in RL most of use will probably encounter both.
Guge
A: 

I was pondering a similar problem when asking how the view model for a task or dialog should look like.

My current solution looks like this:

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

When the view model decides that user input is required, it pulls up a instance of SelectionTaskModel with the possible choices for the user. The infrastructure takes care of bringing up the corresponding view, which in proper time will call the Choose() function with the user's choice.

David Schmitt
A: 

Hi Ray,

I struggled with the same problem. I have come up with a way to intercommunicate between the View and the ViewModel. You can initiate sending a message from the ViewModel to the View to tell it to show a messagebox and it will report back with the result. Then the ViewModel can respond to the result returned from the View.

Here is my blog explaining this:

http://jacokarsten.wordpress.com/2009/03/27/mvvm-intercommunication-between-view-and-viewmodel/

Please let me know what you think.

Jaco

+16  A: 

Hi everyone,

You should use a mediator for this. Mediator is a common design pattern also known as Messenger in some of its implementations. It's a paradigm of type Register/Notify and enables your ViewModel and Views to communicate through a low-coupled messaging mecanism.

You should check out the google WPF Disciples group, and just search for Mediator. You will be much happy with the answers...

You can however start with this:

http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

Enjoy !

Edit: you can see the answer to this problem with the MVVM Light Toolkit here:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

Roubachof
Marlon grech has just posted a brand new implementation of the mediator:http://marlongrech.wordpress.com/2009/04/16/mediator-v2-for-mvvm-wpf-and-silverlight-applications/
Roubachof
Just a remark : the Mediator pattern was not introduced by the WPF Disciples, it's a classical GoF pattern... (http://www.dofactory.com/Patterns/PatternMediator.aspx). Nice answer otherwise ;)
Thomas Levesque
True! My mistake...
Roubachof
+8  A: 

A good MVVM dialog should:

  1. Be declared with only XAML.
  2. Get all of it's behavior from databinding.

Unfortunately, WPF doesn't provide these features. Showing a dialog requires a code-behind call to ShowDialog(). The Window class, which supports dialogs, can't be declared in XAML so it can't easily be databound to the DataContext.

To solve this, I wrote a XAML stub control that sits in the logical tree and relays databinding to a Window and handles showing and hiding the dialog. You can find it here: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

It's really simply to use and doesn't require any strange changs to your ViewModel and doesn't require events or messages. The basic call looks like this:

<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

You probably want to add a style that sets Showing. I explain it in my article. I hope this helps you.

That's a really interesting approach to the problem of showing dialog windows in MVVM.
dthrasher
A: 

I think the view could have code to handle the event from the view model.

Depending on the event/scenario, it could also have an event trigger that subscribes to view model events, and one or more actions to invoke in response.

NikhilK
+15  A: 
Jeffrey Knight
Very nice! Upvoted :)
Thomas Bratt
Yeah i like this idea also but would like to see some example of this control in terms of how to show it, and retrieve dialog result from it etc. Especially in MVVM scenario in Silverlight.
Roboblob
How do you prevent the user from interacting with controls underneath this dialog overlay?
Andrew Garrison
I'll try to put together a demo/sample code.
Jeffrey Knight
Great idea.I created a Popup and controlled visibility by binding to IsOpen.
Zamboni
"How do you prevent the user from interacting with controls ..." ? :private void Window_PreviewKeyDown .... e.Handled = true;
Jeffrey Knight
I don't know if Jeffrey posted his solution anywhere else, but I gave a shot at this.I made my own UserControl and implemented it exactly like Jeffrey's XAML.To get a dialog result, I use RelayCommand and use CommandParameters to pass on a Yes/No result
snurre
+2  A: 

My current solution solves most of the issues you mentioned yet its completely abstracted from platform specific things and can be reused. Also i used no code-behind only binding with DelegateCommands that implement ICommand. Dialog is basically a View - a separate control that has its own ViewModel and it is shown from the ViewModel of the main screen but triggered from the UI via DelagateCommand binding.

See full Silverlight 4 solution here Modal dialogs with MVVM and Silverlight 4

Roboblob
A: 

I really struggled with this concept for a while when learning (still learning) MVVM. What I decided, and what I think others already decided but which wasn't clear to me is this:

My original thought was that a ViewModel should not be allowed to call a dialog box directly as it has no business deciding how a dialog should appear. Beacause of this I started thinking about how I could pass messages much like I would have in MVP (i.e. View.ShowSaveFileDialog()). However, I think this is the wrong approach.

It is OK for a ViewModel to call a dialog directly. However, when you are testing a ViewModel , that means that the dialog will either pop up during your test, or fail all together (never really tried this).

So, what needs to happen is while testing is to use a "test" version of your dialog. This means that for ever dialog you have, you need to create an Interface and either mock out the dialog response or create a testing mock that will have a default behaviour.

You should already be using some sort of Service Locator or IoC that you can configure to provide you the correct version depending on the context.

Using this approach, your ViewModel is still testable and depending on how you mock out your dialogs, you can control the behaviour.

Hope this helps.

Discofunk
A: 

Hi, I had the same situation and wrapped up the MessageBox into a designer invisible control. The details are in my blog

http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

The same can be extended to any modal dialogs, file browse control etc.

mukapu
A: 

An interesting alternative is to use Controllers which are responsible to show the views (dialogs).

How this works is shown by the WPF Application Framework (WAF).

jbe
A: 

I rolled my own window loader described in an answer to this question:

http://stackoverflow.com/questions/1828043/managing-multiple-wpf-views-in-an-application/1828258#1828258

MarkB