在WPF的MVVM模式中,处理对话框是比较复杂的操作之一。由于视图模型不知道视图的任何信息,因此对话框通信可能会很有趣。我可以公开一个ICommand,当视图调用它时,就会出现一个对话框。
有人知道处理对话框结果的好方法吗?我说的是windows对话框,比如MessageBox。
其中一种方法是在视图模型上设置一个事件,当需要对话框时,视图会订阅该事件。
public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;
这是可以的,但这意味着视图需要代码,这是我想要避免的。
有两种好方法可以做到这一点,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)当事件触发时,让视图打开你的视图,它可能在你页面的资源中,或者你可以通过约定在其他地方定位它(比如在对话框服务方法中)。
这种方法更灵活,但需要做更多的工作才能使用。我不怎么用它。例如,一个很好的优点是能够将视图物理地放置在一个选项卡中。我已经使用一种算法将其放置在当前用户控件的边界中,或者如果不够大,则遍历可视树,直到找到足够大的容器。
这允许对话框靠近它们实际使用的地方,只使应用程序中与当前活动相关的部分变暗,并让用户在应用程序中移动而不必手动将对话框推开,甚至在不同的选项卡或子视图上打开多个准模态对话框。
一个好的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" />
您可能希望添加一个设置显示的样式。我在我的文章中解释过。我希望这对你有所帮助。
有两种好方法可以做到这一点,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)当事件触发时,让视图打开你的视图,它可能在你页面的资源中,或者你可以通过约定在其他地方定位它(比如在对话框服务方法中)。
这种方法更灵活,但需要做更多的工作才能使用。我不怎么用它。例如,一个很好的优点是能够将视图物理地放置在一个选项卡中。我已经使用一种算法将其放置在当前用户控件的边界中,或者如果不够大,则遍历可视树,直到找到足够大的容器。
这允许对话框靠近它们实际使用的地方,只使应用程序中与当前活动相关的部分变暗,并让用户在应用程序中移动而不必手动将对话框推开,甚至在不同的选项卡或子视图上打开多个准模态对话框。
编辑: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方法,我现在使用类似于由blindmeis建议的东西。
其中一种方法是
在你的主视图模型中(打开模态的地方):
void OpenModal()
{
ModalWindowViewModel mwvm = new ModalWindowViewModel();
Window mw = new Window();
mw.content = mwvm;
mw.ShowDialog()
if(mw.DialogResult == true)
{
// Your Code, you can access property in mwvm if you need.
}
}
在你的模态窗口View/ViewModel中:
XAML:
<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
<Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>
ViewModel:
public ICommand OkCommand
{
get
{
if (_okCommand == null)
{
_okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
}
return _okCommand ;
}
}
void DoOk(Window win)
{
<!--Your Code-->
win.DialogResult = true;
win.Close();
}
bool CanDoOk(Window win) { return true; }
或者类似于这里发布的WPF MVVM:如何关闭一个窗口