WPF从ViewModel打开一个新视图

这是我的第一个WPF-MVVM应用程序,这是我的结构:

  1. 我的app.xaml一个项目打开应用程序并覆盖OnStartup来解析MainWindow。 (由于参考,我这样做了);
  2. 我的意见的一个项目;
  3. 我的ViewModels的一个项目;
  4. 我的模型的一个项目。

我有以下问题:我在MainWindowView ,然后点击按钮显示另一个视图。 我怎么做从MainWindowViewModel打开另一个视图,而我的View ProjectViewModel Project参考,我不能用View Project引用ViewModel Project View Project

顺便说一下,我正在使用Unity进行dependency injection

那么,你能帮帮我吗?

有几种方法可以解决这个问题。

您可以定义ViewModels项目中定义的对话框/导航/窗口服务接口。 您需要决定ViewModel如何表达他们想要打开的窗口。 我通常使用IDialogViewModel接口,我的一些ViewModel实现,并将ViewModel的实例传递给服务,但你可以使用枚举,字符串,无论你想要什么,所以你的实现可以映射到真正的窗口,这将是打开。

例如:

 public interface IDialogService { bool? ShowDialog(object dialogViewModel); } 

想要打开新Windows的ViewModel将接收该服务的实例,并使用它来表示打开Window的意图。 在您的Views项目中,您将定义一个实现服务接口的类型,并打开Window后面的真实逻辑。

以下示例:

 public class DialogService : IDialogService { private Stack windowStack = new Stack(); public DialogService(Window root) { this.windowStack.Push(root); } public bool? ShowDialog(object dialogViewModel) { Window dialog = MapWindow(dialogViewModel); dialog.DataContext = dialogViewModel; dialog.Owner = this.windowStack.Peek(); this.windowStack.Push(dialog); bool? result; try { result = dialog.ShowDialog(); } finally { this.windowStack.Pop(); } return result; } } 

您的主项目将负责在需要它的ViewModel中创建和注入对话框服务。 在该示例中,App将创建一个将MainWindow传递给它的新对话服务实例。

类似的方法是使用某种forms的消息传递模式( link1 link2 )。 此外,如果你想要简单的东西,你也可以让你的ViewModel在他们想要打开Windows并让Views订阅它们时引发事件。

编辑

我在我的应用程序中使用的完整解决方案通常有点复杂,但基本上就是这个想法。 我有一个基本的DialogWindow,它需要一个实现IDialogViewModel接口的ViewModel作为DataContext。 此接口抽象出您在对话框中期望的一些function,例如接受/取消命令以及关闭事件,因此您也可以从ViewModel关闭窗口。 DialogWindow基本上包含一个ContentPresenter,它将Content属性绑定到DataContext,并在更改DataContext时挂钩close事件(以及其他一些事情)。

每个“对话框”包含一个IDialogViewModel和一个关联的View(UserControl)。 为了映射它们,我只是在App的资源中声明了隐式DataTemplates。 在我已经展示的代码中,唯一的区别是没有方法MapWindow,窗口实例总是一个DialogWindow。

我使用额外的技巧在对话框之间重用布局元素。 方法是将它们包含在DialogWindow中(接受/取消按钮等)。 我喜欢保持DialogWindow干净(所以我可以将它用于“非对话框”对话框)。 我为具有公共接口元素的ContentControl声明了一个模板,当我声明一个View-ViewModel映射模板时,我用一个ContentControl包装View并应用了我的“对话框模板”。 然后,您可以根据需要为DialogWindow提供尽可能多的“主模板”(例如,像“向导之类”)。

直截了当的方法

如果我理解你正确应用程序启动时通过Unity解决MainWindowView,这解决了它对MainWindowViewModel的依赖?

如果这是您正在使用的流程,我建议您继续使用相同的路径,让MainWindowView通过按钮的简单点击处理程序处理新视图的打开。 在此处理程序中,您可以解析新视图,该视图将解析该视图的视图模型,然后您又回到MVVM域中以获取新视图。

该解决方案是直截了当的,并且对于大多数小型应用程

更复杂的应用程序更重的方法

如果你不想要那种视图优先流程,我会建议引入某种协调视图和视图模型的控制器/演示器。 演示者负责决定是否/何时实际打开/关闭视图等。

这是一个非常重的抽象,它更适合更复杂的应用程序,因此请确保从中获得足够的好处以certificate增加的抽象/复杂性。

以下是此方法的代码示例:

 public partial class App { protected override void OnStartup(StartupEventArgs e) { var container = new UnityContainer(); container.RegisterType(); container.RegisterType(); container.RegisterType(); container.RegisterType(); var presenter = container.Resolve(); presenter.ShowView(); } } public interface IMainPresenter { void ShowView(); void OpenSecondView(); } public interface ISecondPresenter { void ShowView(); } public interface ISecondView { void Show(); SecondViewModel ViewModel { get; set; } } public interface IMainView { void Show(); MainViewModel ViewModel { get; set; } } public class MainPresenter : IMainPresenter { private readonly IMainView _mainView; private readonly ISecondPresenter _secondPresenter; public MainPresenter(IMainView mainView, ISecondPresenter secondPresenter) { _mainView = mainView; _secondPresenter = secondPresenter; } public void ShowView() { // Could be resolved through Unity just as well _mainView.ViewModel = new MainViewModel(this); _mainView.Show(); } public void OpenSecondView() { _secondPresenter.ShowView(); } } public class SecondPresenter : ISecondPresenter { private readonly ISecondView _secondView; public SecondPresenter(ISecondView secondView) { _secondView = secondView; } public void ShowView() { // Could be resolved through Unity just as well _secondView.ViewModel = new SecondViewModel(); _secondView.Show(); } } public class MainViewModel { public MainViewModel(MainPresenter mainPresenter) { OpenSecondViewCommand = new DelegateCommand(mainPresenter.OpenSecondView); } public DelegateCommand OpenSecondViewCommand { get; set; } } public class SecondViewModel { }          Second view   

本文介绍了一种类似于之前我在生产中使用过的解决方案。

要从MainWindowView打开新窗口,您需要将Frame组件或整个窗口的引用传递给MainWindowViewModel对象(您可以在将命令绑定到转换按钮或其他内容时执行此操作,将它们作为对象传递),然后您可以导航到新页面,但是如果在转换时你需要在ViewModel做任何特殊的事情,你可以在MainWindowView.cs中使用经典的ButtonClick事件或w / e来为你做导航,这对于基本过渡是可以的。

PS我不确定为什么你为ViewModels / Views / Models使用不同的项目。