代码之家  ›  专栏  ›  技术社区  ›  Budda

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

  •  54
  • Budda  · 技术社区  · 16 年前

    在邻居的岗位上: How should the ViewModel close the form? 我已经发布了我的愿景:如何使用MVVM关闭窗口。现在我有一个问题:如何打开它们。

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

    第一个(可能是最简单的)。事件处理程序“showbutton\u 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
                }
            }
    
    1. 如果我们“显示”按钮状态应该改变(启用/禁用),我们需要添加逻辑来管理按钮状态;
    2. 源代码与“老式”WinForms和MFC源非常相似-我不确定这是好是坏,请给出建议。
    3. 我错过了什么?

    另一种方法:

    在主窗口视图模型中,我们将实现“showcommand”属性,该属性将返回命令的ICommand接口。逗号轮流:

    • 将引发“ShowDialogEvent”;
    • 将管理按钮状态。

    这种方法将更适合MVVM,但需要额外的编码:ViewModel类不能“显示对话框”,因此MainWindowViewModel只会引发“ShowDialogEvent”,即MainWindowView,我们需要在它的MainWindow加载方法中添加事件处理程序,如下所示:

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

    (ShowDialog-类似于“ModifyButton单击”方法。)

    所以我的问题是: 1。你看到其他方法了吗? 2。你认为清单中的一个是好的还是坏的?(为什么?)

    欢迎有其他想法。

    谢谢。

    6 回复  |  直到 8 年前
        1
  •  16
  •   Benny Jobigan    15 年前

    我最近也在考虑这个问题。我有个主意如果你用的话 Unity 在您的项目中作为“容器”或依赖项注入的任何内容。我想通常你会忽略 App.OnStartup() 并创建模型、视图模型和视图,并为每个模型提供适当的引用。使用Unity,可以为容器提供模型的引用,然后使用容器“解析”视图。Unity容器注入视图模型,因此您永远不会直接实例化它。一旦你的观点得到解决,你就给 Show() 在上面。

    在我观看的一个示例视频中,Unity容器被创建为 OnStartup . 如果在应用程序类中将其创建为公共静态只读属性,会怎么样?然后可以在主视图模型中使用它来创建新窗口,自动注入新视图所需的任何资源。类似的东西 App.Container.Resolve<MyChildView>().ShowDialog(); .

    我想您可以在测试中模拟调用Unity容器的结果。或者,也许您可以编写类似 ShowMyChildView() 在应用程序类中,这基本上就是我上面描述的那样。可能很容易嘲笑 App.ShowMyChildView() 因为它只会返回 bool? 嗯?

    好吧,这可能并不比仅仅使用 new MyChildView() 但我有点想法。我想我会分享。=)

        2
  •  17
  •   arconaut    15 年前

    一些MVVM框架(例如 MVVM Light )利用 Mediator pattern . 因此,要打开一个新窗口(或创建任何视图),某些视图特定的代码将订阅来自中介的消息,而ViewModel将发送这些消息。

    这样地:

    亚述

    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(...));
    

    我更喜欢在singleton类中进行订阅,该类的“寿命”与应用程序的ui部分一样长。 总而言之:ViewModel传递类似“我需要创建一个视图”的消息,并且UI监听这些消息并对它们进行操作。

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

        3
  •  4
  •   Liero    8 年前

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

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

    Benny Jobigan的Anwer:

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

    这实际上不能解决任何问题。您正以一种紧密耦合的方式从ViewModel访问视图。唯一的区别是 new MyChildView().ShowDialog() 是你经历了一个间接的过程。我觉得直接打电话给mychildview负责人没有什么好处。

    如果您为视图使用接口,它会更干净:

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

    现在,视图模型没有与视图紧密耦合。然而,我发现为每个视图创建接口是非常不切实际的。

    电弧炉的功率:

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

    这更好。似乎messenger或eventaggregator或其他pub/sub模式是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
    }
    

    DialogService只接受Dialog的ViewModel,因此您的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;}
    }
    

    其中dialogbutton指定dialogresult或icommand或两者。

        4
  •  2
  •   Roboblob    16 年前

    看看我当前的MVVM解决方案,它可以在Silverlight中显示模式对话框。 它解决了您提到的大多数问题,但它完全是从平台特定的事物中抽象出来的,可以重用。另外,我没有使用代码隐藏,只使用实现ICommand的delegateCommands绑定。对话框基本上是一个视图-一个独立的控件,它有自己的ViewModel,从主屏幕的ViewModel显示,但通过DelagateCommand绑定从UI触发。

    在此处查看完整的Silverlight 4解决方案 Modal dialogs with MVVM and Silverlight 4

        5
  •  1
  •   adrianm    16 年前

    我使用一个控制器来处理视图之间传递的所有信息。所有的视图模型都使用控制器中的方法来请求更多的信息,这些信息可以实现为对话框、其他视图等。

    看起来像这样:

    class MainViewModel {
        public MainViewModel(IView view, IModel model, IController controller) {
           mModel = model;
           mController = controller;
           mView = view;
           view.DataContext = this;
        }
    
        public ICommand ShowCommand = new DelegateCommand(o=> {
                      mResult = controller.GetSomeData(mSomeData);
                                                          });
    }
    
    class Controller : IController {
        public void OpenMainView() {
            IView view = new MainView();
            new MainViewModel(view, somemodel, this);
        }
    
        public int GetSomeData(object anyKindOfData) {
          ShowWindow wnd = new ShowWindow(anyKindOfData);
          bool? res = wnd.ShowDialog();
          ...
        }
    }
    
        6
  •  0
  •   jbe    16 年前

    我的方法与Adrianm_S类似。但是,在我的例子中,控制器从不使用具体的视图类型。控制器与视图完全分离,方式与视图模型相同。

    这是如何工作的,可以在 WPF Application Framework (WAF) .

    .

    最好的问候,

    JBE