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

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

我有一个主窗口(主视图)。 如果用户点击“显示”button,则会显示“演示”窗口(模式对话框)。 什么是使用MVVM模式创build和打开窗口的最佳方式? 我看到两个一般的方法:

第一个(可能是最简单的)。 事件处理程序“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 } } 
  1. 如果我们“显示”button状态应该改变(启用/禁用),我们将需要添加将pipe理button状态的逻辑;
  2. 源代码非常类似于“旧式”的WinForms和MFC源代码 – 我不确定这是好还是坏,请告知。
  3. 还有什么我错过了?

另一种方法:

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

  • 会提高“ShowDialogEvent”;
  • 将pipe理button状态。

这种方法将更适合于MVVM,但将需要额外的编码:ViewModel类不能“显示对话框”,所以MainWindowViewModel将只会引发“ShowDialogEvent”,我们需要在MainWindowView的MainWindowView方法中添加事件处理程序,就像这样:

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

(ShowDialog – 类似于“ModifyButton_Click”方法。)

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

任何其他的想法,欢迎。

谢谢。

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

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

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

那么,这可能不仅仅是使用new MyChildView() ,但它是一个小的想法,我有。 我以为我会分享它。 =)

一些MVVM框架(例如MVVM Light )使用中介器模式 。 因此,要打开一个新窗口(或创build任何视图),某些特定于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传递消息,如“我需要创build一个视图”,用户界面监听这些消息,并对它们进行操作。

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

看看我目前的MVVM解决scheme,在Silverlight中显示modal dialog。 它解决了你提到的大多数问题,但它完全从平台特定的东西中抽象出来,可以被重用。 我也没有使用code-behind绑定与实现ICommand的DelegateCommands。 对话框基本上是一个视图 – 一个单独的控件,它有自己的ViewModel,它从主屏幕的ViewModel显示,但通过DelawareCommand绑定从UI触发。

在这里看到完整的Silverlight 4解决schemeMVVM和Silverlight 4的模式对话框

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

它看起来像这样:

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

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

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

本尼·约比根的口气:

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

这实际上并没有解决任何问题。 您正以惊人的方式访问ViewModel中的View。 new MyChildView().ShowDialog()唯一的区别是你走了一层间接。 我没有看到直接调用MyChildView ctor的任何优势。

如果您使用界面进行查看,则会更干净:

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

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

arconaut的口气:

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

它更好。 似乎像Messenger或EventAggregator或其他pub / sub模式是MVVM中的everyhing的通用解决scheme:)缺点是它很难debugging或导航到DialogMessageHandler 。 这太间接了。 例如,如何读取对话框的输出? 通过修改DialogMessage?

我的解决scheme

你可以像这样从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,所以你的视图完全独立于Views。 在运行时,DialogService可以find适当的视图(例如使用命名约定)并显示它,或者它可以在unit testing中轻松地模拟。

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

 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或两者。

我的方法类似于adrianm的方法。 但是,在我的情况下,控制器从来没有使用具体的视图types。 控制器完全与视图解耦 – 就像ViewModel一样。

WPF应用程序框架(WAF)的ViewModel示例中可以看到如何工作。

最好的祝福,

JBE