0

我正在尝试从其 ViewModel 关闭一个窗口。我正在使用 MVVM 模式。我已经厌倦了使用窗户;

Window parentWindow = Window.GetWindow(this);

但我不能这样做,我如何获得 ViewModel 的窗口,以便我能够关闭窗口。我希望能够在代码中做到这一点。

你能在代码中找到父窗口吗?

4

5 回答 5

3

ViewModels不应View以任何方式引用 MVVM,包括关闭窗口。

相反,View和之间的通信ViewModel通常是通过某种事件或消息系统完成的,例如Microsoft PrismEventAggregatorMVVM Light 的Messenger

例如,View应该订阅侦听类型的事件消息CloseWindow,并且当它收到这些消息之一时,它应该关闭自己。然后,只要它想告诉它关闭,它就必须随时ViewModel广播一条消息。CloseWindowView

如果您有兴趣,可以在我关于ViewModel 之间的通信的博客文章中简要概述 MVVM 中的事件系统和一些示例

于 2012-06-14T16:46:02.600 回答
1

是的,在视图模型中引用视图不是最佳实践。为什么?因为当您对视图模型进行单元测试时,需要您实例化视图,对于小视图并不难做到这一点,但是对于具有复杂依赖树的复杂视图?那不会很好。

对我来说,与视图进行通信的最简单方法是传递IInputElement视图模型构造函数。的好处IInputElement是路由事件主干,它具有路由事件所需的方法RaiseEventAddHandler因此,您可以自由地将事件冒泡/隧道/直接到应用程序上的任何视图或视图模型,而无需任何额外的库。

这是我在视图模型上的简化代码,但请记住这种技术仅适用于视图优先方法

public class MyViewModel : INotifyPropertyChanged
{
    public static readonly RoutedEvent RequestCloseEvent = EventManager.RegisterRoutedEvent("RequestClose",
        RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyViewModel));

    private IInputElement dispatcher;

    public MyViewModel(IInputElement dispatcher)
    {
        this.dispatcher = dispatcher;
    }

    public void CloseApplication()
    {
        dispatcher.RaiseEvent(new RoutedEventArgs(RequestCloseEvent));
    }
}

简单地在您的视图上

DataContext = new MyViewModel(this)
//notice "this" on the constructor

以及您的应用程序的根视图(窗口)

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        AddHandler(MyViewModel.RequestCloseEvent, new RoutedEventHandler(onRequestClose));
    }

    private void onRequestClose(object sender, RoutedEventArgs e)
    {
        if (MessageBox.Show("Are you sure you want to quit?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
        {
            Close();
        }
    }
}

并且因为IInputElement是接口而不是类,您可以轻松地为您的单元测试创​​建一个模拟类

var target = new MyViewModel(new DispatcherMock)

或者你可以使用像 RhinoMocks 这样的模拟库

如需进一步阅读,您可以了解有关如何使用路由事件的更多信息

于 2012-06-17T01:11:58.277 回答
0

您可以在 ViewModel 中定义一个操作

public Action CloseAction { get; set; }

然后,在您的窗口中(例如在 DataContextChanged 中)您可以设置此操作:

((IClosable)viewModel.Content).CloseAction = () => System.Windows.Application.Current.Dispatcher.Invoke(Close());

好吧,所有这些都是更大的依赖注入模式的一部分,但基本原理在这里......接下来,您只需要从 VM 调用操作。

于 2012-06-14T15:25:45.330 回答
0

此任务有一个有用的行为,它不会破坏 MVVM,这是一种在 Expression Blend 3 中引入的行为,允许 View 挂钩到完全在 ViewModel 中定义的命令。

此行为演示了一种允许 ViewModel 管理 Model-View-ViewModel 应用程序中 View 的关闭事件的简单技术。

这允许您在视图 (UserControl) 中连接一个行为,该行为将提供对控件窗口的控制,允许 ViewModel 控制是否可以通过标准 ICommands 关闭窗口。

使用行为允许 ViewModel 管理 MV-VM 中的视图生命周期

http://gallery.expression.microsoft.com/WindowCloseBehavior/

于 2012-06-14T17:58:31.897 回答
0

如果确实需要,让 ViewModel 执行此操作。

例如,模型说,不再有有效数据

将该信息传递给 ViewModel

ViewModel 认识到,它不能再显示任何东西

然后关闭窗口。

空视图是表示没有更多数据的正常方式

于 2012-06-14T15:14:43.290 回答