2013-03-05 44 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