显示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等待视图在引发事件后返回值。
有更好的方法吗?
啊,这个’栗子’。
如何实现这一点有很多不同的变化,但这是我的两分钱。
这里的主要思想是确保您的View
和View 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
类充当View
和View Model
之间的控制器,以确保它们彼此不了解,并保持强制执行MVVM模式。
就像我在这个答案的开头所说的那样,有很多方法可以实现,但是我希望你现在有点开明。