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

C# 使用MVVM在WPF中创建新窗口的最佳方法,c#,.net,wpf,mvvm,C#,.net,Wpf,Mvvm,在邻居的帖子中: 我已经发布了如何使用MVVM关闭windows的设想。现在我有一个问题:如何打开它们 我有一个主窗口(主视图)。如果用户单击“显示”按钮,则应显示“演示”窗口(模式对话框)。使用MVVM模式创建和打开窗口的首选方法是什么?我认为有两种一般做法: 第一个(可能是最简单的)。事件处理程序“ShowButton\u Click”应以如下方式在主窗口的代码隐藏中实现: private void ModifyButton_Click(object sender, Rout

在邻居的帖子中: 我已经发布了如何使用MVVM关闭windows的设想。现在我有一个问题:如何打开它们

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

    在MainWindowViewModel中,我们将实现“ShowCommand”属性,该属性将返回命令的ICommand接口。科曼依次:

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

    ((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
    
    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();
          ...
        }
    }
    
    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
    }
    
    (ShowDialog-类似于“ModifyButton_Click”方法。)

    因此,我的问题是: 1.你看到其他的方法了吗? 2.你认为其中一个是好的还是坏的?(为什么?)

    欢迎有其他想法


    谢谢。

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

    它看起来像这样:

    ((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
    
    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();
          ...
        }
    }
    
    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
    }
    

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


    请参见此处的完整Silverlight 4解决方案

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

    其工作原理可以在的ViewModel示例中看到

    致以最良好的祝愿


    jbe

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

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

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

    嗯,这可能并不比仅仅使用
    newmychildview()
    好多少,但这是我的一个小主意。我想我会分享的

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

    像这样:

    归属

    Messenger.Default.Register<DialogMessage>(this, ProcessDialogMessage);
    ...
    private void ProcessDialogMessage(DialogMessage message)
    {
         // Instantiate new view depending on the message details
    }
    
    我更喜欢在singleton类中进行订阅,它与应用程序的UI部分一样“存在”。 总而言之:ViewModel传递诸如“我需要创建视图”之类的消息,UI侦听这些消息并对其进行操作


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

    我有点晚了,但我发现现有的答案不够充分。我会解释原因。一般而言:

    • 可以从视图访问ViewModels
    • 从ViewModels访问视图是错误的,因为它引入了循环依赖关系,并且使得ViewModels难以测试
    Benny Jobigan的回答: 现在,ViewModel没有与视图紧密耦合。然而,我发现为每个视图创建接口是不切实际的

    阿科纳特的anwer: 这样更好。看起来Messenger或EventAggregator或其他发布/订阅模式是MVVM中所有内容的通用解决方案:)缺点是调试或导航到
    DialogMessageHandler
    比较困难。这太间接了。例如,如何读取对话框中的输出?通过修改对话框消息

    我的解决方案: 您可以按如下方式从MainWindowViewModel打开窗口:

    ((MainWindowViewModel)DataContext).ShowDialogEvent += ShowDialog;
    
    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();
          ...
        }
    }
    
    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可以找到适当的视图(例如使用命名约定)并显示它,或者可以在单元测试中轻松模拟它

    在我的情况下,我使用以下接口: