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

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

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

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

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


当前回答

一个好的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" />

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

其他回答

我建议放弃20世纪90年代的模态对话框,而是实现一个覆盖控件(画布+绝对定位),可见性绑定到虚拟机中的布尔值。更接近ajax类型控件。

这非常有用:

<BooleanToVisibilityConverter x:Key="booltoVis" />

如:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

下面是我如何实现一个用户控件。单击“x”关闭后面用户控件代码中的一行代码中的控件。(因为我有我的视图在一个。exe和ViewModels在一个dll,我不觉得代码操纵UI。)

有两种好方法可以做到这一点,1)对话框服务(简单,干净),2)视图辅助。视图辅助提供了一些简洁的特性,但通常不值得这样做。

对话框的服务

A)一个对话服务接口,比如via构造函数或一些依赖容器:

接口IDialogService { 任务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)要使用,只需在某个dialogviewmodel派生类的实例上调用await this.DialogService.ShowDialog(myDlgVm)。等待返回后,查看你在对话框VM中添加的属性,以确定发生了什么;你甚至不需要回调。

查看帮助

这让你的视图监听视图模型上的事件。如果您愿意的话,可以将这些都打包到一个Blend Behavior中,以避免背后的代码和资源使用(FMI,子类化“Behavior”类,以查看类固醇上的一种可混合的附加属性)。现在,我们将手动在每个视图上执行此操作:

a)创建一个带有自定义有效负载(DialogViewModel派生类)的OpenXXXXXDialogEvent。

b)让视图在OnDataContextChanged事件中订阅该事件。如果旧值!= null并且在窗口的Unloaded事件中,请务必隐藏并取消订阅。

c)当事件触发时,让视图打开你的视图,它可能在你页面的资源中,或者你可以通过约定在其他地方定位它(比如在对话框服务方法中)。

这种方法更灵活,但需要做更多的工作才能使用。我不怎么用它。例如,一个很好的优点是能够将视图物理地放置在一个选项卡中。我已经使用一种算法将其放置在当前用户控件的边界中,或者如果不够大,则遍历可视树,直到找到足够大的容器。

这允许对话框靠近它们实际使用的地方,只使应用程序中与当前活动相关的部分变暗,并让用户在应用程序中移动而不必手动将对话框推开,甚至在不同的选项卡或子视图上打开多个准模态对话框。

一个有趣的替代方法是使用控制器来显示视图(对话框)。

WPF应用程序框架(WAF)说明了这是如何工作的。

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

如果你改变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 */ });

它展示了这样的东西: