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

在邻居文章中:ViewModel应该如何关闭表单? 我已经发布了我的愿景,即如何使用MVVM使用关闭窗口。 现在我有一个问题:如何打开它们。

我有一个主窗口(主视图)。 如果用户点击“显示”按钮,则应显示“演示”窗口(模式对话框)。 使用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
            }
        }
  • 如果我们“显示”按钮状态应该改变(启用/禁用),我们将需要添加将管理按钮状态的逻辑;
  • 源代码与“旧式”WinForms和MFC源代码很相似 - 我不确定这是好还是坏,请告知。
  • 我错过的其他东西?
  • 另一种方法:

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

  • 将引发“ShowDialogEvent”;
  • 将管理按钮状态。
  • 这种方法将更适合MVVM,但需要额外的编码:ViewModel类不能“显示对话框”,所以MainWindowViewModel只会引发“ShowDialogEvent”,我们需要在MainWindowView的MainWindow_Loaded方法中添加事件处理函数,像这样:

    ((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
    

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

    所以我的问题是:1.你看到其他方法吗? 2.你认为列出的是好还是坏? (为什么?)

    欢迎任何其他想法。

    谢谢。


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

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

    我想你可以在测试中以某种方式将该调用的结果模拟到Unity容器中。 另外,也许你可以在App类中编写像ShowMyChildView()这样的方法,它基本上就是我上面所描述的。 可能很容易嘲笑对App.ShowMyChildView()的调用,因为它只会返回一个bool? ,呃?

    那么,这可能并不比使用new MyChildView()更好,但这是我的一个小想法。 我想我会分享它。 =)


    一些MVVM框架(例如MVVM Light)利用Mediator模式。 因此,要打开一个新窗口(或创建任何视图),某些特定于View的代码将订阅来自调解器的消息,并且ViewModel将发送这些消息。

    喜欢这个:

    Subsription

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

    在ViewModel中

    Messenger.Default.Send(new DialogMessage(...));
    

    我更喜欢在单个类中进行订阅,这个类只要应用程序的UI部分具有“生活”。 总结一下:ViewModel传递消息,如“我需要创建一个视图”,用户界面监听这些消息并对其执行操作。

    虽然没有“理想”的方法,当然。


    我有点晚了,但我发现现有的答案不够。 我会解释为什么。 一般来说:

  • 从View中访问ViewModels是可以的,
  • 从ViewModels访问视图是错误的 ,因为它引入了循环依赖并使ViewModel很难测试。
  • Benny Jobigan的口气:

    App.Container.Resolve<MyChildView>().ShowDialog();
    

    这实际上并没有解决任何问题。 您正在以一种紧密耦合的方式访问ViewModel中的View。 与new MyChildView().ShowDialog()唯一的区别在于你通过一个间接层。 我没有看到直接调用MyChildView ctor的优势。

    如果您使用界面进行视图,它会更干净:

    App.Container.Resolve<IMyChildView>().ShowDialog();`
    

    现在ViewModel并没有与视图紧密结合。 但是我发现为每个视图创建接口是非常不切实际的。

    arconaut's awer:

    Messenger.Default.Send(new DialogMessage(...));
    

    它更好。 似乎Messenger或EventAggregator或另一个pub / sub模式是MVVM中的everyhing的通用解决方案:)缺点是调试或导航到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
    }
    

    DialogService只需要对话框的ViewModel,所以你的视图模型完全独立于视图。 在运行时,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;}
    }
    

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

    链接地址: http://www.djcxy.com/p/56197.html

    上一篇: The best approach to create new window in WPF using MVVM

    下一篇: MVVM: Tutorial from start to finish?