WPF MVVM模式下打开新窗口的最佳实践

20
我有一段时间一直在考虑这个问题...如果我们记住打开新窗口的视图模型不知道该视图的存在(如此应该),那么从另一个视图模型中打开新窗口(视图和视图模型)最佳实践是什么?
谢谢。

1
我在这篇帖子中回答了一个非常类似的问题。 - Mike Fuchs
@adabyron,请提供可下载的源代码作为您的解决方案。 - RobinAtTech
5个回答

13

我更喜欢使用通过ViewModel构造函数插入的操作委托。这样做还意味着我们可以在单元测试期间轻松验证:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new MainViewModel(() => (new Window()).Show()); // would be actual window
    }
}

public class MainViewModel
{
    private Action popupAction;
    public MainViewModel(Action popupAction)
    {
        this.popupAction = popupAction;
    }

    public ICommand PopupCommand { get; set; }

    public void PopupCommandAction()
    {
        popupAction();
    }
}

public class SomeUnitTest
{
    public void TestVM()
    {
        var vm = new MainViewModel(() => { });
    }
}

我认为使用更完整的观察者模式会更加清晰,类似于MVP模式中的模式。由于“弹出”这个词在ViewModel中暗示了View在呈现方面正在做一些事情,所以我仍然感到不舒服。 - jpierson
2
+1 分用一种优雅的方式(不使用第三方库),并展示我以前没有见过的东西。 - basarat
这看起来是一个非常优雅的解决方案,绝对点赞并分享。 - Noich

5
我不使用ViewModel来打开另一个视图/ViewModel。这是控制器的责任。ViewModel可以通过事件通知控制器(例如),用户希望看到下一个视图。控制器借助IoC容器创建视图/ViewModel。
如何操作在ViewModel(EmailClient)示例应用程序中展示了WPF应用程序框架(WAF)

3
使用中介者模式,例如mvvmlight的messenger类:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

基本思想是视图模型向其视图发送消息。接收视图的代码如下:
OnMsgRecived() {

  Viewmodel vm = New Viewmodel() - Or use dependency injection to resolve

  View v = new View()
  v.DataContext = vm
  v.Show()
}

这使得发送消息的视图模型能够打开另一个窗口,而无需知道是如何或由谁打开的。

2

我个人更喜欢在我的ViewModel中引发事件,以向视图发出信号,告诉它需要执行某些操作,比如打开一个窗口。不过,我尽量不直接这样做,因为我认为在ViewModel中看到像OpenWindow这样的事件似乎违反了View和ViewModel之间的分离。相反,我可能会有一个属性来改变状态,并相应地引发PropertyChanged事件,视图可以侦听此事件,然后决定是否响应此信号打开一个窗口。在某些情况下,打开窗口与ViewModel中的某些内容无关,仅是View的功能。在这些情况下,我毫不害怕将打开另一个视图的代码放置在View的代码后台部分。

中介者模式使得系统更加松散耦合,同时也允许主应用程序窗口视图或高度嵌套的视图在没有直接访问ViewModels的情况下全局监听应用程序中的消息,以便附加事件处理程序等。为了过滤掉不相关的消息,您可以查看某种消息源值或其他指示消息来源的指示。对于那些熟悉Windows消息及其在未管理和WinForms开发中如何工作的人来说,可能是理解基于广播消息的中介者构建系统的一种方式。


1
我同意事件是这里的最佳选择,这样你就不必让一个"知道"另一个。Prism做得很好,提供了一个良好的事件系统来完成此类操作。 - Vaccano

0

我赞同采用类似中介者的方法,但是OnMsgReceived显然是在视图的代码后台处理的,有没有好的方法可以避免这种情况?


我认为视图的代码后台是打开其他视图的正确位置。我觉得ViewModel直接或间接地生成View都是错误的。相反,最好触发事件或在这种情况下分派消息,并允许侦听器根据信号做出自己的反应。请记住,MVVM的一个重要目标是可测试性,在视图中弹出窗口通常不是自动化测试需要测试的部分。 - jpierson

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