显示模态对话框并获取结果

3

我有一个静态的WindowService类,可以帮助我创建新的窗口和模态对话框。 到目前为止,我的代码如下:

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

我的viewmodel会触发一个事件,而视图会订阅它。在视图中的事件处理程序中,它会调用WindowService.ShowNewWindow(this,The type here)。这个方法运行良好。
我的模态对话框创建方法也会以类似的方式工作。唯一的区别是信息将返回到视图(在事件处理程序中),所以视图必须显式地将该信息传递给view model。这违反了MVVM模式,我不知道如何使viewmodel在事件触发后等待视图返回值。
有没有更好的方法来解决这个问题?

我相信你把事情复杂化了。你忽略了重要的一点:唯一的区别是信息将返回到视图(在事件处理程序中),因此视图必须明确地通过代码将该信息传递给视图模型。为什么会这样呢? - netaholic
因为我正在事件处理程序中调用服务方法。 - wingerse
1个回答

3
啊,这个老生常谈的话题。
有许多不同的方法可以实现此目标,但是以下是我的建议。
主要思路是确保您的视图(View)和视图模型(View Model)互相不知道对方的存在。 因此,您的视图(View)不应该订阅视图模型中的事件,而您的视图模型也不应该直接调用服务并提供一个视图类型(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();
    }
}

该类继承自并指定了一种实现方式,该实现方式接受一个,该表示我们想要打开的所需

非常感谢。这帮了我很多 :) - wingerse
但是现在我应该如何关闭视图并打开新视图呢? - wingerse
您可以使用 Application.Current.WindowsApplication.Current.MainWindow 来获取当前正在显示的窗口。某些 ICommand 类的变体需要处理这些内容。这并不是完美的,但为了完成此任务,可以承担一些技术债务。如果您真的想进一步进行,考虑创建一个 自定义控件,该控件可以具有 WindowToClose 依赖属性,您的命令可以使用此属性来关闭所需的窗口。同样,您将不得不进行实验并查看哪种方法最有效。 - Mike Eason
1
谢谢,我修改了命令以接受一个对象作为命令参数。这个对象将包含发送者视图、新视图类型和传递给新视图构造函数的其他信息。它运行得很好 :) - wingerse
我正在尝试您的解决方案,并创建了一个自定义的“MarkupExtension”来包含类型、窗口所有者和其他内容,我将其作为“CommandParameter”传递。我只是想知道为什么ViewModel要决定它是窗口还是对话框(通过创建的命令实例)? - Anders
视图模型不知道它将成为窗口还是对话框,由想要使用视图模型的人决定如何呈现它。例如,您可能希望打开窗口以阻止线程直到返回,在这种情况下,您希望它成为对话框。我演示的命令只是从视图模型中打开窗口/对话框的一种方式,您的视图模型的行为方式取决于调用这些命令的视图模型是否希望视图模型作为窗口或对话框运行。我希望这有些意义。 - Mike Eason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接