Handling Dialogs in WPF with MVVM

2019-01-01 04:44发布

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.

23条回答
何处买醉
2楼-- · 2019-01-01 05:05

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.

查看更多
梦醉为红颜
3楼-- · 2019-01-01 05:06

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

查看更多
临风纵饮
4楼-- · 2019-01-01 05:10

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).

查看更多
只若初见
5楼-- · 2019-01-01 05:11

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.

查看更多
ら面具成の殇う
6楼-- · 2019-01-01 05:12

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.

查看更多
听够珍惜
7楼-- · 2019-01-01 05:13

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.

查看更多
登录 后发表回答