显示modal dialog并获得结果

我有一个静态WindowService类,它可以帮助我创建新的窗口和模式对话框。 到目前为止,我所拥有的是:

 ///  /// Opens a new window of type  and closes the  ///  /// The window which should be closed (Usually the current open window) /// The type of the new window to open public static void ShowNewWindow(Window oldWindow, Type newWindowType) { ((Window)Activator.CreateInstance(newWindowType)).Show(); oldWindow.Close(); } 

我的viewmodel引发了一个事件,并且视图已订阅它。 在视图中的事件处理程序中,它调用WindowService.ShowNewWindow(this,The type here) 。 这很好用。
我的modal dialog创建方法也将以类似的方式工作。 唯一的区别是信息将返回到视图(在事件处理程序中),因此视图必须显式地将该信息传递给代码中的视图模型。 这违反了mvvm模式,我不知道如何使viewmodel等待视图在引发事件后返回值。
有更好的方法吗?

啊,这个’栗子’。

如何实现这一点有很多不同的变化,但这是我的两分钱。

这里的主要思想是确保您的ViewView Model彼此不了解,因此您的View不应订阅View Model的事件,并且您的View Model不应直接调用您的服务并提供视图Type


不要使用事件,而是使用命令

我的建议是使用ICommand实现而不是依赖静态服务类,因为你的类总是依赖于这个服务,并且一旦你将视图Type发送到这个服务,那么MVVM模式迷路了。

所以,首先,我们需要某种命令来打开给定Type的窗口,这就是我想出的:

 public class OpenWindowCommand : ICommand { public bool CanExecute(object parameter) { TypeInfo p = (TypeInfo)parameter; return p.BaseType == typeof(Window); } public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public void Execute(object parameter) { if (parameter == null) throw new ArgumentNullException("TargetWindowType"); //Get the type. TypeInfo p = (TypeInfo)parameter; Type t = p.BaseType; if (p.BaseType != typeof(Window)) throw new InvalidOperationException("parameter is not a Window type"); //Create the window. Window wnd = Activator.CreateInstance(t) as Window; OpenWindow(wnd); } protected virtual void OpenWindow(Window wnd) { wnd.Show(); } } 

该类inheritance自ICommand并指定接受Type的实现,该Type表示我们要打开的所需View 。 请注意,我已将方法标记为virtual ,我将在稍后解释该部分。

以下是我们如何在View Model使用此命令:

 public class MainWindowViewModel { public OpenWindowCommand OpenWindowCommand { get; private set; } public MainWindowViewModel() { OpenWindowCommand = new OpenWindowCommand(); } ... } 

现在我们已经创建了命令,我们只需要将Button绑定到它:

  

这里要注意的一件事是我使用x:Type作为CommandParameter ,这是在执行此命令时将创建的Window


但是对话怎么样?

我们上面所取得的只是需求的一半 ,我们现在需要一些能够显示对话框并将结果输出到我们的View Model ,这并不像我们在现有的OpenWindowCommand已经拥有的大部分内容那么棘手。

首先,我们需要创建命令:

 public class ShowDialogCommand : OpenWindowCommand { private Action _PreOpenDialogAction; private Action _PostOpenDialogAction; public ShowDialogCommand(Action postDialogAction) { if (postDialogAction == null) throw new ArgumentNullException("postDialogAction"); _PostOpenDialogAction = postDialogAction; } public ShowDialogCommand(Action postDialogAction, Action preDialogAction) : this(postDialogAction) { if (preDialogAction == null) throw new ArgumentNullException("preDialogAction"); _PreOpenDialogAction = preDialogAction; } protected override void OpenWindow(System.Windows.Window wnd) { //If there is a pre dialog action then invoke that. if (_PreOpenDialogAction != null) _PreOpenDialogAction(); //Show the dialog bool? result = wnd.ShowDialog(); //Invoke the post open dialog action. _PostOpenDialogAction(result); } } 

我们通过inheritance它并使用它的实现来使用我们的OpenWindowCommand ,而不是将它们全部复制到我们的新类中。 该命令采用Action作为View Model方法的引用,您可以选择显示对话框之前之后 (或两者)定义操作。

下一步是更改我们的View Model以便创建这个新命令:

 public class MainWindowViewModel { public OpenWindowCommand OpenWindowCommand { get; private set; } public ShowDialogCommand ShowDialogCommand { get; private set; } public MainWindowViewModel() { OpenWindowCommand = new OpenWindowCommand(); ShowDialogCommand = new ShowDialogCommand(PostOpenDialog); } public void PreOpenDialog() { throw new NotImplementedException(); } public void PostOpenDialog(bool? dialogResult) { throw new NotImplementedException(); } } 

此命令的用法与以前几乎相同,但它只引用了一个不同的命令:

  

而且你拥有它,一切都松散耦合,这里唯一真正的依赖是你的View Model依赖于你的ICommand类。


最后的一些话

我创建的ICommand类充当ViewView Model之间的控制器,以确保它们彼此不了解,并保持强制执行MVVM模式。

就像我在这个答案的开头所说的那样,有很多方法可以实现,但是我希望你现在有点开明。