在WPF的MVVM模式中,处理对话框是比较复杂的操作之一。由于视图模型不知道视图的任何信息,因此对话框通信可能会很有趣。我可以公开一个ICommand,当视图调用它时,就会出现一个对话框。

有人知道处理对话框结果的好方法吗?我说的是windows对话框,比如MessageBox。

其中一种方法是在视图模型上设置一个事件,当需要对话框时,视图会订阅该事件。

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

这是可以的,但这意味着视图需要代码,这是我想要避免的。


当前回答

编辑:10多年后,我可以看出使用Mediator或任何其他消息传递模式在很多层面上都是一个非常糟糕的主意。不要这样做,只需实现Jeffrey的答案或在视图模型中注入一个IDialogService即可。


你应该找个中间人。 Mediator是一种常见的设计模式,在某些实现中也称为Messenger。 它是一种Register/Notify类型的范例,允许ViewModel和Views通过低耦合消息传递机制进行通信。

你应该看看谷歌WPF门徒组,然后搜索调解员。 你会很高兴得到答案的……

但是你可以这样开始:

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

享受吧!

编辑:你可以看到这个问题的答案与MVVM轻工具包在这里:

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

其他回答

我认为视图可以有代码来处理来自视图模型的事件。

根据事件/场景的不同,它还可以具有订阅视图模型事件的事件触发器,以及响应中要调用的一个或多个操作。

一个好的MVVM对话框应该:

仅用XAML声明。 从数据绑定中获取它的所有行为。

不幸的是,WPF不提供这些特性。显示对话框需要对ShowDialog()进行代码隐藏调用。支持对话框的Window类不能在XAML中声明,因此它不能轻易地绑定到DataContext。

为了解决这个问题,我写了一个XAML存根控件,它位于逻辑树中,将数据绑定传递给一个Window,并处理显示和隐藏对话框的问题。你可以在这里找到它:http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

它使用起来非常简单,不需要对ViewModel进行任何奇怪的更改,也不需要事件或消息。基本调用如下所示:

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

您可能希望添加一个设置显示的样式。我在我的文章中解释过。我希望这对你有所帮助。

在学习(仍在学习)MVVM时,我真的为这个概念挣扎了一段时间。我的决定,以及我认为其他人已经决定但我不清楚的是:

我最初的想法是ViewModel不应该被允许直接调用对话框,因为它没有业务决定对话框应该如何显示。正因为如此,我开始思考如何像在MVP(即View.ShowSaveFileDialog())一样传递消息。然而,我认为这是错误的方法。

ViewModel直接调用对话框是可以的。然而,当您测试ViewModel时,这意味着对话框要么在测试期间弹出,要么全部失败(从未真正尝试过这个)。

因此,在测试时需要使用对话框的“测试”版本。这意味着,对于您拥有的任何对话,您都需要创建一个Interface,并模拟对话响应,或者创建一个具有默认行为的测试模拟。

您应该已经在使用某种服务定位器或IoC,您可以对其进行配置,以便根据上下文提供正确的版本。

使用这种方法,你的ViewModel仍然是可测试的,根据你如何模拟对话框,你可以控制行为。

希望这能有所帮助。

我认为处理对话框应该是视图的责任,视图需要有代码来支持这一点。

如果你改变ViewModel - View交互来处理对话框,那么ViewModel依赖于该实现。处理这个问题最简单的方法是让视图负责执行任务。如果这意味着显示一个对话框,那么很好,但也可以是状态栏中的状态消息等。

我的观点是,MVVM模式的全部观点是将业务逻辑从GUI中分离出来,因此您不应该在业务层(ViewModel)中混合GUI逻辑(以显示对话框)。

我知道这是一个老问题,但当我做这个搜索时,我发现了很多相关的问题,但我没有找到一个真正明确的回答。所以我做了我自己的对话框/消息框/popin的实现,我分享它! 我认为这是“MVVM证明”,我试着让它简单和适当,但我是WPF的新手,所以请随意评论,甚至提出拉请求。

https://github.com/Plasma-Paris/Plasma.WpfUtils

你可以这样使用它:

public RelayCommand YesNoMessageBoxCommand { get; private set; }
async void YesNoMessageBox()
{
    var result = await _Service.ShowMessage("This is the content of the message box", "This is the title", System.Windows.MessageBoxButton.YesNo);
    if (result == System.Windows.MessageBoxResult.Yes)
        // [...]
}

或者像这样,如果你想要更复杂的popin:

var result = await _Service.ShowCustomMessageBox(new MyMessageBoxViewModel { /* What you want */ });

它展示了这样的东西: