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.
There are two good ways to do this, 1) a dialog service (easy, clean), and 2) view assisted. View assisted provides some neat features, but is usually not worth it.
DIALOG SERVICE
a) a dialog service interface like via constructor or some dependency container:
interface IDialogService { Task ShowDialogAsync(DialogViewModel dlgVm); }
b) Your implementation of IDialogService should open a window (or inject some control into the active window), create a view corresponding to the name of the given dlgVm type (use container registration or convention or a ContentPresenter with type associated DataTemplates). ShowDialogAsync should create a TaskCompletionSource and return its .Task proptery. The DialogViewModel class itself needs an event you can invoke in the derived class when you want to close, and watch in the dialog view to actually close/hide the dialog and complete the TaskCompletionSource.
b) To use, simply call await this.DialogService.ShowDialog(myDlgVm) on your instance of some DialogViewModel-derived class. After await returns, look at properties you've added on your dialog VM to determine what happened; you don't even need a callback.
VIEW ASSISTED
This has your view listening to an event on the viewmodel. This could all be wrapped up into a Blend Behavior to avoid code behind and resource usage if you're so inclined (FMI, subclass the "Behavior" class to see a sort of Blendable attached property on steroids). For now, we'll do this manually on each view:
a) Create an OpenXXXXXDialogEvent with a custom payload (a DialogViewModel derived class).
b) Have the view subscribe to the event in its OnDataContextChanged event. Be sure to hide and unsubscribe if the old value != null and in the Window's Unloaded event.
c) When the event fires, have the view open your view, which might be in a resource on your page, or you could locate it by convention elsewhere (like in the the dialog service approach).
This approach is more flexible, but requires more work to use. I don't use it much. The one nice advantage are the ability to place the view physically inside a tab, for example. I have used an algorithm to place it in the current user control's bounds, or if not big enough, traverse up the visual tree until a big enough container is found.
This allows dialogs to be close to the place they're actually used, only dim the part of the app related to the current activity, and let the user move around within the app without having to manually push dialogs away, even have multiple quasi-modal dialogs open on different tabs or sub-views.
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
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).
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:
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 theChoose()
function with the user's choice.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.
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.