WPF:在MVVM中管理窗口(打开、关闭等)?

5

我在许多地方都看到过这个问题。大多数人都参考了以下两个链接:

我不理解它们中的任何一个。对于MVVM,当涉及到窗口操作时,有些人提到控制器。那些是什么,它们是如何实现的?按照书本的说法,MVVM由模型,视图模型和视图组成 - 控制器在哪里?

如果有人能够提供以下用例的示例,那将非常棒(对于像我一样刚开始学习的人):

  1. 前提条件:打开一个窗口。
  2. 用户单击一个按钮。
  3. 打开一个新窗口并向该窗口传递一些数据,例如一些字符串。
  4. 关闭新窗口(或单击按钮)并将一些数据传递回第一个窗口。
  5. 传递的数据已更改窗口上的某些内容。
2个回答

0

ViewModel 到 ViewModel 的通信通常由事件聚合器模式的实现来处理。

MVVM Light 使用 Messenger 类,Prism 有另一种实现方式,但基本上这是一种在 View Model 之间发送消息而不耦合的方法。

有一些示例和 文章 描述了使用方法。我建议你去看一下。

关于 WPF 中的控制器问题,我不清楚。

关于示例:

- 我有一个带有其 WindowsViewModel 的窗口。这个类应该有一个绑定到按钮的命令。

- 用户点击按钮。执行命令。

- 命令打开一个新窗口。

在这里,你应该创建对话框视图模型,并以某种方式创建窗口。或者用 ViewModel 创建窗口,但 ViewModel 不应该过多地了解 View,否则就无法进行测试。

我们使用类似这样的东西,因为我们有一些要求,但它可以更简单,恰好这是我手头唯一的示例:

bool? ShowDialogImpl<TViewModel>(Action<TViewModel> setup) where TViewModel : ViewModel
{
    return (bool?)DispatcherHelper.UIDispatcher.Invoke(
        (Func<bool?>)(() =>
        {
            var viewModel = viewModelFactory.Get<TViewModel>();
            viewModel.ViewService = this;
            setup(viewModel);
            var window = new Window
            {
                Owner = this,
                SizeToContent = SizeToContent.WidthAndHeight,
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Content = ViewFactory.CreateView<TViewModel>(),
                DataContext = viewModel,
                WindowStyle = WindowStyle.ToolWindow,
                ShowInTaskbar = false
            };
            window.SetBinding(TitleProperty, new Binding("Title"));
            openDialogs.Push(window);
            window.Activated += (sender, args) => window.SizeToContent = SizeToContent.Manual;
            var result = window.ShowDialog();
            openDialogs.Pop();
            viewModelFactory.Release(viewModel);
            return result;
        }));
}

基本上:我们创建一个窗口和一个视图模型。 视图模型是使用容器从工厂创建的。 设置操作委托是我们数据的入口点。

  • 通信:

第一个窗口是网格,第二个对话框用于编辑网格数据。 在窗口中,我们有:

messenger.Register<EntityUpdated<FooClass>>(this, message => UpdateItem(message.Entity));

在对话框中:

messenger.Send(new EntityUpdated<FooClass>(subject));

这样,我们就知道在编辑对话框中更新了什么内容,以便刷新网格。

希望这能帮到你 :)


感谢您的回复。我还不理解事件聚合器模式,所以我认为需要集中精力去理解您的示例。请告诉我,在您的应用程序中,当用户单击打开新窗口的按钮时,您是如何处理的?您调用添加到ViewModel的命令,然后触发一些事件吗?谢谢。 - Boris
是的。我们尽可能地使用命令。与命令绑定的方法负责调用: AddCommand = new RelayCommand(Add); 其中 Add 为: void Add() { ViewService.ShowDialog<StatusCodeEditViewModel>(); } 事件聚合器就像一个中间人,将事件的订阅者与发布者解耦。这样,您可以在没有双方(订阅者和发布者)彼此了解的情况下进行事件处理。 - Marcote
MVVM Light真是太棒了!我已经弄清楚了,而且它运行良好。感谢Markust帮助我解决问题。 - Boris
1
从ViewModel直接打开一个窗口似乎会破坏MVVM的理念。如果这样做,您就不能简单地对视图模型进行单元测试了。我认为您需要像IWindowManager这样的东西,您可以为单元测试进行模拟,但是在非测试模式下提供具体的窗口打开/关闭操作。 - Mike Caron
这是一个对话,但我同意。 - Marcote

0

如果您不打算允许用户在两个窗口都打开时来回切换(即第一个打开第二个,必须关闭第二个才能返回第一个),那么您可以将两个窗口的视图模型设置为相同的视图模型实例,并将第二个窗口设置为模态,而您传递来回的字符串只需是视图模型的属性,并绑定到视图中的某些内容。


很遗憾,我的应用程序将包含非模态的窗口。此外,我打算每个视图使用一个ViewModel。不过感谢您对这个问题的分享。 - Boris

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