使用MVVM在WPF中创建新窗口的最佳方法

Bud*_*dda 55 .net c# wpf mvvm

在邻居帖子中:ViewModel应如何关闭表单? 我发布了如何使用MVVM关闭Windows的愿景.现在我有一个问题:如何打开它们.

我有一个主窗口(主视图).如果用户单击"显示"按钮,则应显示"演示"窗口(模态对话框).使用MVVM模式创建和打开窗口的首选方法是什么?我看到两种一般方法:

第一个(可能是最简单的).事件处理程序"ShowButton_Click"应该在主窗口的代码中实现,方式如下:

        private void ModifyButton_Click(object sender, RoutedEventArgs e)
        {
            ShowWindow wnd = new ShowWindow(anyKindOfData);
            bool? res = wnd.ShowDialog();
            if (res != null && res.Value)
            {
                //  ... store changes if neecssary
            }
        }
Run Code Online (Sandbox Code Playgroud)
  1. 如果我们"显示"按钮状态应该被更改(启用/禁用),我们将需要添加将管理按钮状态的逻辑;
  2. 源代码非常类似于"旧式"WinForms和MFC源 - 我不确定这是好还是坏,请指教.
  3. 还有其他我错过的东西?

另一种方法:

在MainWindowViewModel中,我们将实现"ShowCommand"属性,该属性将返回命令的ICommand接口.Comman反过来:

  • 将举起"ShowDialogEvent";
  • 将管理按钮状态.

这种方法将更适合MVVM,但需要额外的编码:ViewModel类不能"显示对话框",因此MainWindowViewModel只会引发"ShowDialogEvent",MainWindowView我们需要在其MainWindow_Loaded方法中添加事件处理程序,这样的事情:

((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
Run Code Online (Sandbox Code Playgroud)

(ShowDialog - 类似于'ModifyButton_Click'方法.)

所以我的问题是:1.你看到其他任何方法吗?你认为其中一个是好还是坏?(为什么?)

欢迎任何其他想法.

谢谢.

arc*_*aut 17

一些MVVM框架(例如MVVM Light)使用Mediator模式.因此,要打开一个新窗口(或创建任何View),一些特定于View的代码将订阅来自中介的消息,ViewModel将发送这些消息.

像这样:

Subsription

Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
...
private void ProcessDialogMessage(DialogMessage message)
{
     // Instantiate new view depending on the message details
}
Run Code Online (Sandbox Code Playgroud)

在ViewModel中

Messenger.Default.Send(new DialogMessage(...));
Run Code Online (Sandbox Code Playgroud)

我更喜欢在单例类中进行订阅,只要应用程序的UI部分可以"生存".总结一下:ViewModel传递"我需要创建视图"之类的消息,UI会监听这些消息并对其进行操作.

当然,没有"理想"的方法.

  • 它可以在某些"静态"视图的代码隐藏中,例如主窗口.或者更好地在一个负责实例化视图的单独类中. (2认同)

Ben*_*gan 16

我最近也在考虑这个问题.如果你在你的项目中使用Unity作为"容器"或依赖注入的任何东西,我有一个想法.我想通常你会覆盖App.OnStartup()并创建你的模型,视图模型,并在那里查看,并给每个人提供适当的参考.使用Unity,您可以为容器提供对模型的引用,然后使用容器"解析"视图.Unity容器会注入您的视图模型,因此您永远不会直接实例化它.一旦您的视图得到解决,您Show()就可以调用它.

在我观看的一个示例视频中,Unity容器被创建为一个局部变量OnStartup.如果您在App类中将其创建为公共静态只读属性,该怎么办?然后,您可以在主视图模型中使用它来创建新窗口,自动注入新视图所需的任何资源.有点像App.Container.Resolve<MyChildView>().ShowDialog();.

我想你可以在测试中以某种方式模拟调用Unity容器的结果.或者,也许您可​​以编写ShowMyChildView()App类中的方法,这基本上就像我上面描述的那样.可能很容易模拟一个调用,App.ShowMyChildView()因为它只会返回一个bool?,是吗?

嗯,这可能不仅仅比使用更好new MyChildView(),但这是我的一个小想法.我以为我会分享它.=)


Lie*_*ero 6

我来晚了,但是我发现现有答案还不够。我将解释原因。一般来说:

  • 可以从View访问ViewModels,
  • 从ViewModels访问视图是错误的,因为它引入了循环依赖关系,并使ViewModels难以测试。

Benny Jobigan的答案:

App.Container.Resolve<MyChildView>().ShowDialog();
Run Code Online (Sandbox Code Playgroud)

这实际上并不能解决任何问题。您正在以一种淡淡的耦合方式从ViewModel访问View。唯一的区别new MyChildView().ShowDialog()是您经过了间接层。与直接调用MyChildView ctor相比,我没有任何优势。

如果为视图使用界面,它将更加干净:

App.Container.Resolve<IMyChildView>().ShowDialog();`
Run Code Online (Sandbox Code Playgroud)

现在,ViewModel不再与视图淡化耦合。但是,我发现为每个视图创建界面都是不切实际的。

弓箭手的答案:

Messenger.Default.Send(new DialogMessage(...));
Run Code Online (Sandbox Code Playgroud)

更好 Messenger或EventAggregator或其他发布/订阅模式似乎是MVVM中所有内容的通用解决方案:)缺点是调试或导航到它比较困难DialogMessageHandler。这太间接了,恕我直言。例如,您将如何读取对话框的输出?通过修改DialogMessage?

我的解决方案:

您可以从MainWindowViewModel打开窗口,如下所示:

var childWindowViewModel = new MyChildWindowViewModel(); //you can set parameters here if necessary
var dialogResult = DialogService.ShowModal(childWindowViewModel);
if (dialogResult == true) {
   //you can read user input from childWindowViewModel
}
Run Code Online (Sandbox Code Playgroud)

DialogService只接受对话框的ViewModel,因此您的ViewModel完全独立于View。在运行时,DialogService可以找到适当的视图(例如,使用命名约定)并显示它,或者可以在单元测试中轻松地对其进行模拟。

就我而言,我使用以下接口:

interface IDialogService
{
   void Show(IDialogViewModel dialog);
   void Close(IDialogViewModel dialog); 
   bool? ShowModal(IDialogViewModel dialog);
   MessageBoxResult ShowMessageBox(string message, string caption = null, MessageBoxImage icon = MessageBoxImage.No...);
}

interface IDialogViewModel 
{
    string Caption {get;}
    IEnumerable<DialogButton> Buttons {get;}
}
Run Code Online (Sandbox Code Playgroud)

其中DialogBu​​tton指定DialogResult或ICommand或两者。