2010-08-02 59 views
43

我在我的WPF應用程序中使用MVVM Light工具包。我想知道從現有窗口打開新窗口的最佳方法是什麼。我有這MainViewModel,這是我的應用程序的MainWindow負責。現在在MainView,點擊一個按鈕,我想打開第二個窗口。我有RelayCommmand綁定到ButtonCommand。在RelayCommand的方法,我可以創造一個新的窗口對象,調用Show(),是這樣的:如何使用MVVM Light Toolkit打開一個新窗口

var view2 = new view2() 
view2.Show() 

,但我不認爲視圖模型應該是負責創建新view2對象。我已閱讀此帖WPF MVVM Get Parent from VIEW MODEL,其中Bugnion建議將消息​​從viewmodel1傳遞到view1,然後view1應創建新的view2。但我不知道他通過將消息傳遞給view1是什麼意思? view1應該如何處理消息?在它的代碼背後或什麼?

問候, 納比勒

+0

請參閱http://stackoverflow.com/questions/16993433/mvvm-light-wpf-binding-multiple-instances-of-a-window-to-a-viewmodel/16994523#16994523 – reggaeguitar 2014-04-24 22:07:08

回答

50

傳遞從ViewModel1到視圖1的消息的裝置使用messaging capabilities in the MVVM Light Toolkit

例如,您的ViewModel1可以有一個名爲ShowView2Command的命令,然後它會發送一條消息來顯示視圖。

public class ViewModel1 : ViewModelBase 
{ 
    public RelayCommand ShowView2Command { private set; get; } 

    public ViewModel1() : base() 
    { 
     ShowView2Command = new RelayCommand(ShowView2CommandExecute); 
    } 

    public void ShowView2CommandExecute() 
    { 
     Messenger.Default.Send(new NotificationMessage("ShowView2")); 
    } 
} 

View1將註冊爲在其代碼後面接收消息,並在收到正確消息時顯示View2。

public partial class View1 : UserControl 
{ 
    public View1() 
    { 
     InitializeComponent(); 
     Messenger.Default.Register<NotificationMessage>(this, NotificationMessageReceived); 
    } 

    private void NotificationMessageReceived(NotificationMessage msg) 
    { 
     if (msg.Notification == "ShowView2") 
     { 
      var view2 = new view2(); 
      view2.Show(); 
     } 
    } 
} 
+0

感謝您的迴應馬特。除了使用消息傳遞之外,是否還有其他方法/最佳實踐在mvvm中打開新視圖? – nabeelfarid 2010-08-03 08:16:31

+8

我見過人們使用的另一種方法是使用用於打開視圖的類的服務風格。您的ViewModel將與此服務的界面一起工作,以顯示ChildWindow,MessageBox或其他任何內容。這是那些在視圖的代碼隱藏中想要零代碼的人的特別喜愛。此外,它可以測試一些,因爲你可以模擬服務並聲明顯示你的視圖的方法被調用。 – 2010-08-03 13:03:22

+2

是的,我看到人們也在談論它。但我不明白這種方法是,當你從一個視圖模型使用一些服務打開一個子窗口讓我們說IDialogService.OpenChild(),你將如何設置子窗口的所有者,作爲調用IDialogService的視圖模型。 OpenChild()不知道或沒有對其自己的視圖的引用? – nabeelfarid 2010-08-04 11:57:37

2

除非我在這裏錯過了點 - 如果我是背後使用的代碼,那麼爲什麼不直接實現button_click事件,並打開第二個看法?

什麼比尼翁似乎暗示是廠景 - >右鍵點擊 - >繼電器命令 - > viewmodel1 - >信息 - >廠景 - > view1.cs - >打開查看2.

你要犧牲的可測性無論如何編寫代碼隱藏,爲什麼要採取這樣一個漫長的路線?

+4

漫長的路線將確保: 當您測試您的視圖模型時,您至少可以測試廣播的消息/請求以打開新視圖。您可以將消息請求代碼包裝在IDialogService中,以便在測試期間使其可嘲弄。 – nabeelfarid 2010-08-25 09:23:11

+2

我同意普拉茨的觀點,採取如此漫長的路線有點瘋狂。 – Vincent 2010-10-28 17:53:11

+1

對於有少量窗口/視圖的小應用程序,該方法後面的代碼很好。如果你有一個主窗口只是偶爾打開第二個窗口來顯示一些細節,那麼增加的複雜性看起來有點過分。如果應用程序變大,後面的代碼將不能很好地擴展,測試會受到影響。 – srock 2013-09-05 14:37:29

3

你可以這樣做,就像你需要創建一些事件,並在視圖中註冊那些事件,然後在model.and中調用這些事件並打開彈出窗口。

像這樣的例子

public class Mainclass : MainView 
{ 
    public delegate abc RegisterPopUp(abc A); 
    public RegisterPopUp POpUpEvent ; 

    public RelayCommand ShowCommand { private set; get; } 


    public void ShowCommand() 
    { 
     ShowCommand("Your parameter"); 
    } 
} 

認爲MainView mn=new MainView();

註冊時喜歡這裏thake mn.POpUpEvent +=比標籤按鈕雙倍時間內單擊

,並在寄存器中彈出方法適合開放代碼彈出窗口。

4

你爲什麼走這條路?這很簡單。如果用toggleButton或超鏈接或任何其他數量的按鈕式控件代替按鈕,則不需要更新「後面的代碼」 - 它是MVVM模式的基本原理。在新的toggleButton(或其他)中,您仍然會綁定到同一個確切的Command。

例如,我正在爲希望擁有2個UI的客戶創建一個項目 - 在演示方面,每種方式都會有根本的不同。水平製表符與垂直RadPanelBar(認爲是Accordion)進行導航。這兩種視圖都可以指向相同的viewModel - 當用戶單擊視圖1中的工作訂單選項卡時,它將激發面板欄中工作訂單標題中觸發的相同「WorkOrderCommand」。

在代碼隱藏模型中,您必須編寫兩個單獨的事件。在這裏你只需要編碼一個。

此外,它允許設計師使用Blend創建他們想要的任何佈局。只要他們具有鉤子(EventToCommand控件),我自己(作爲開發人員)就不會在乎最終產品的外觀。

鬆耦合非常強大。

2

您可以使用通用接口將視圖特定功能抽象爲服務。在視圖層中,您可以提供這些服務的具體實例,並使用IoC容器和依賴注入技術構建視圖模型。

在你的情況下,你可以構建一個接口IWindowManager或類似的具有所需方法的接口。這可以在你的視圖層中實現。我最近寫了一篇小博客文章,演示瞭如何將對話行爲從視圖模型中抽象出來。類似apporach可以用於像導航任何用戶界面相關的服務,消息框等

此鏈接可能會有所幫助你http://nileshgule.blogspot.com/2011/05/silverlight-use-dialogservice-to.html

許多人還使用擊發這些都對訂閱視圖模型事件的方法view.cs文件,然後從那裏執行MessageBox或任何其他UI相關的操作。我個人喜歡注入服務的方法,因爲那樣你就可以提供同一服務的多個實現。一個簡單的例子就是如何在Silverlight和Windows Phone 7應用程序中處理導航。您可以使用相同的視圖模型,但根據應用程序類型注入不同的導航服務實現。

0

我發現最好的方法來解決這個問題,從ViewModel打開和關閉窗口。如this鏈路所暗示的,

  1. 創建DialogCloser
 
    public static class DialogCloser 
    { 
     public static readonly DependencyProperty DialogResultProperty = DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(DialogCloser), new PropertyMetadata(DialogResultChanged)); 

     private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      var window = d as Window; 
      if (window != null) window.Close(); 
     } 

     public static void SetDialogResult(Window target, bool? value) 
     { 
      target.SetValue(DialogResultProperty, value); 
     } 
    } 
  • 創建一個基視圖模型從GalaSoft.MvvmLight.ViewModelBase與有額外的成員繼承。完成後,使用此視圖模型作爲其他視圖模型的基礎。
  •  
        bool? _closeWindowFlag; 
        public bool? CloseWindowFlag 
        { 
         get { return _closeWindowFlag; } 
         set 
         { 
          _closeWindowFlag = value; 
          RaisePropertyChanged("CloseWindowFlag"); 
         } 
        } 
    
        public virtual void CloseWindow(bool? result = true) 
        { 
         Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
         new Action(() => 
         { 
          CloseWindowFlag = CloseWindowFlag == null ? true : !CloseWindowFlag; 
         })); 
        }
  • 在視圖中,綁定與在基視圖模型的CloseWindowFlag屬性DialogCloser.DialogResult依賴項屬性。
  • 然後,您可以從視圖模型中打開/關閉/隱藏窗口。