2013-03-05 45 views
6

我正在开发遵循MVVM模式的WPF应用程序。为了显示模态对话框,我试图按照以下文章的建议。 http://www.codeproject.com/Articles/36745/Showing-Dialogs-When-Using-the-MVVM-Pattern?fid=1541292&fr=26#xx0xx使用服务定位器的MVVM模式对话框

但是在这些文章中,我观察到,DialogService接口的ShowDialog方法是从MainWindowViewModel调用的。

我在申请中的情况稍有不同。 MainWindow.xaml包含一个用户控件,声称包含一个Add按钮的ChildView。 MainWindowViewModel包含另一个ViewModel,它说与ChildView绑定的ChildVM。 ChildVM包含AddCommand,我需要在AddExecute方法 对应的AddCommand被调用时显示模态对话框。 我该怎么做到这一点?

编辑代码

 private Window FindOwnerWindow(object viewModel) 
      { 
        FrameworkElement view = null; 

     // Windows and UserControls are registered as view. 
     // So all the active windows and userControls are contained in views 
     foreach (FrameworkElement viewIterator in views) 
     { 
      // Check whether the view is an Window 
      // If the view is an window and dataContext of the window, matches 
      // with the viewModel, then set view = viewIterator 
      Window viewWindow = viewIterator as Window; 
      if (null != viewWindow) 
      { 
       if (true == ReferenceEquals(viewWindow.DataContext, viewModel)) 
       { 
        view = viewWindow; 
        break; 
       } 

      } 
      else 
      { 
       // Check whether the view is an UserControl 
       // If the view is an UserControl and Content of the userControl, matches 
       // with the viewModel, then set view = userControl 
       // In case the view is an user control, then find the Window that contains the 
       // user control and set it as owner 
       System.Windows.Controls.UserControl userControl = viewIterator as System.Windows.Controls.UserControl; 
       if (null != userControl) 
       { 
        if (true == ReferenceEquals(userControl.Content, viewModel)) 
        { 
         view = userControl; 
         break; 
        } 

       } 
      } 
     } 
     if (view == null) 
     { 
      throw new ArgumentException("Viewmodel is not referenced by any registered View."); 
     } 

     // Get owner window 
     Window owner = view as Window; 
     if (owner == null) 
     { 
      owner = Window.GetWindow(view); 
     } 

     // Make sure owner window was found 
     if (owner == null) 
     { 
      throw new InvalidOperationException("View is not contained within a Window."); 
     } 

     return owner; 
     } 
+0

嗨@Anirban,按照本文从http://www.codeproject.com/Articles/332615/WPF-Master-Details-MVVM-Application看看如何模态对话框的作品。我已经使用这篇文章来帮助我创建我的应用程序。希望这可以帮助! – 2013-03-05 11:05:53

+0

该方法与我提供的链接相同。 – 2013-03-05 13:55:15

+0

你有解决方案吗? – Marc 2013-03-06 07:50:27

回答

4

好吧,如果我得到你的权利,你想从MainWindowViewModel不打开模态对话框,而是从不同的ChildViewModel?

看一看您已链接了CodeProject上的文章的MainWindowViewModel的构造函数:

视图模型具有以下签名的构造函数:

public MainWindowViewModel(
      IDialogService dialogService, 
      IPersonService personService, 
      Func<IOpenFileDialog> openFileDialogFactory) 

这意味着,对于建设需要显示模式对话框的服务,另一个服务(personService),在这里并不重要,在特定对话框中打开文件的工厂openFileDialogFactory。

为了使用这项服务,这是文章,一个简单的服务定位器实现的默认的构造的核心部分被定义,它使用服务定位来获取视图模型所需要的服务的实例:

public MainWindowViewModel() 
      : this(
      ServiceLocator.Resolve<IDialogService>(), 
      ServiceLocator.Resolve<IPersonService>(), 
      () => ServiceLocator.Resolve<IOpenFileDialog>()) 
     {} 

这是可能的,因为ServiceLocator是静态的。或者,您可以使用ServiceLocator为构造函数中的服务设置本地字段。上述方法更好,因为它允许您自己设置服务,如果您不想使用ServiceLocator。

您可以在自己的ChildViewModel中完全相同。

public ChildViewModel(IDialogService dialogService) 
{ 
    _dialogService = dialogService; 
} 

创建一个默认的构造函数,它要求从服务定位器解决服务实例上面的构造:

public ChildViewModel() : this(ServiceLocator.Resolve<IDialogService>()) {} 

现在你可以在你的ChildViewModel从任何地方使用的服务是这样的:

_dialogService.ShowDialog<WhateverDialog>(this, vmForDialog); 

为了找到您的视图的所有者窗口,这不是视图本身,您需要修改DialogSer的FindOwnerWindow方法以找到视图的父窗口,而不是期望窗口作为视图本身。您可以使用VisualTreeHelper这样做:

private Window FindOwnerWindow(object viewModel) 
    { 
     var view = views.SingleOrDefault(v => ReferenceEquals(v.DataContext, viewModel)); 

     if (view == null) 
     { 
      throw new ArgumentException("Viewmodel is not referenced by any registered View."); 
     } 

     DependencyObject owner = view; 

     // Iterate through parents until a window is found, 
     // if the view is not a window itself 
     while (!(owner is Window)) 
     { 
      owner = VisualTreeHelper.GetParent(owner); 
      if (owner == null) 
       throw new Exception("No window found owning the view."); 
     } 

     // Make sure owner window was found 
     if (owner == null) 
     { 
      throw new InvalidOperationException("View is not contained within a Window."); 
     } 

     return (Window) owner; 
    } 

你仍然需要,虽然注册用户控件,设置在用户控件的附加属性:

<UserControl x:Class="ChildView" 
      ... 
      Service:DialogService.IsRegisteredView="True"> 
    ... 
</UserControl> 

据我所知,这个工程。

其他信息:

要完成同样的事情,我用的是PRISM框架,自带了很多的功能,正是这种类型的解耦,控制(IoC)和依赖注入的反转(DI )。也许值得为你看看它。

希望这会有所帮助!

编辑考虑评论。

+0

问题在于,child viewModel对应于用户控件而不是像MainWindoViewModel这样的窗口。 _dialogService.ShowDialog (this,vmForDialog)..当dialogService试图找出DataContext为“this”的Window时,它失败。请查看DialogService实现的FindOwnerWindow方法。 – 2013-03-05 14:38:20

+0

我希望我已经解决了这个问题。在我的应用程序中,任何用户控件的相应视图模型都与用户控件的内容相关联,而不是DataContext。所以我需要修改FindOwnerWindow方法。请看看我编辑的帖子。 – 2013-03-06 08:11:02

0

看看你是否喜欢这个想法......我使用的是Castle Windsor和Prism,所以你的里程可能会有所不同,但其概念应该与另一个MVVM和IoC相同。

你开始你的MainViewModel.cs那个希望开一个模态对话框

var view = ServiceLocator.Current.GetInstance<SomeDialogView>(); 
view.ShowDialog(); 

,但当然这不是尊重你在MainView.xaml

WindowStartupLocation="CenterOwner" 

讨厌鬼设置!

但等待,不能ServiceLocator只是给我的MainView?

var view = ServiceLocator.Current.GetInstance<SomeDialogView>(); 
view.Owner = ServiceLocator.Current.GetInstance<MainView>(); // sure, why not? 
view.ShowDialog(); 

由于我的IoC寄存器视图具有“瞬态生命周期”,因此此行会引发IoC配置异常。在温莎城堡,这意味着每个请求都提供了一个全新的实例,并且我需要 MainView实例本身,而不是一个未显示的新实例。

但是通过简单地从每个视图变更注册为“瞬态”

container.Register(Classes.FromAssemblyNamed("WhateverAssemblyTheyreIn") 
    .InNamespace("WhateverNamespaceTheyreIn") 
    .LifestyleTransient()); 

要稍微更判别通过使用流利除非()和如果()

container.Register(Classes.FromAssemblyNamed("WhateverAssemblyTheyreIn") 
    .InNamespace("WhateverNamespaceTheyreIn") 
    .Unless(type => type == typeof(MainView)) 
    .LifestyleTransient()); // all as before but MainView. 

container.Register(Classes.FromAssemblyNamed("WhateverAssemblyTheyreIn") 
    .InNamespace("WhateverNamespaceTheyreIn") 
    .If(type => type == typeof(MainView)) 
    .LifestyleSingleton()); // set MainView to singleton! 

提供的MainView 我们想要的!

HTH