2012-09-16 72 views
1

我正在開發基於mvvm light toolkit的項目。我有MainViewDetailsView及其相應的ViewModels,其 。這兩個VM都註冊了NotificationMessage任何避免ShowDialog()阻止Messenger的解決方案?

// MainViewModel.cs and DetailsViewModel.cs 
private void RegisterMessages() 
{ 
    Messenger.Default.Register<NotificationMessage>(this, NotificationMessageHandler); 
} 

當收到 「ShowDetails」 消息時,MainViewModel調用創建 '的DetailsView'

// MainViewModel.cs 
private void NotificationMessageHandler(NotificationMessage msg) 
{ 
    if (msg.Notification == "ShowDetails") 
    { 
     _detailsService.ShowDetails(); // Does something like (new DetailsView).ShowDialog() 
    } 
} 

DetailsView使用ViewModelLocator獲得現有DetailsViewModal作爲DataContext的服務。

DetailsViewModel應該收到「ShowDetails」消息來更新其內部狀態或請求一些數據。

// DetailsViewModel.cs 
private void NotificationMessageHandler(NotificationMessage msg) 
    { 
     if (msg.Notification == "ShowDetails") 
     { 
      UpdateViewModel(); 
     } 
    } 

現在的問題: 因爲我想DetailsView是一個模態窗口,我呼籲它ShowDialog()。這似乎阻止信使直到DetailsView再次關閉。因此DetailsViewModal在模式窗口關閉後收到消息。有沒有解決方案來解決這個問題?

我認爲它會工作,如果我可以在MainViewModel之前註冊DetailsViewModal。這會在阻塞ShowDialog()之前更改MessageHandler調用和VM更新發生的順序。但MainViewModel是由於它是什麼而被創建和註冊的。 DetailsViewModelViewModalLocator在需要時第一次創建,所以它總是失去競賽。

回答

5

不幸的是,我無法重現您的具體問題。我在MainWindowView Loaded事件處理程序中引發了一個單獨的線程;一個什麼都不做,但不斷髮送特定信息的線程。然後我在我的SecondWindowView上調用了ShowDialog(),它的視圖模型被註冊爲偵聽這個特定的消息。第二個窗口視圖模型中的消息處理程序重複執行。事實上,甚至在調用ShowDailog()之前調用該處理程序,因爲我的視圖模型已由ViewModelLocator在應用程序啓動時創建。我需要看到更多的代碼才能更好地瞭解您的情況(即您的服務是創建詳細信息窗口,還是可以編譯以重現此問題的服務)。

您可以嘗試以下方法,而不是您的子窗口。某處定義以下類在應用程序中:

public class ShowChildWindowMessage : MessageBase { } 
public class HideChildWindowMessage : MessageBase { } 
public class DisplayDetailsMessage : MessageBase { } 

現在創建以下ChildWindowVM類,並在您的ViewModelLocator初始化它以同樣的方式MainWindowVM初始化:

public class ChildWindowVM : ViewModelBase 
{ 
    private ViewModelBase m_currentContent; 
    public ViewModelBase CurrentContent 
    { 
     get { return m_currentContent; } 
     set 
     { 
      NotifySetProperty(ref m_currentContent, value,() => CurrentContent); 
      if (m_currentContent != null) 
      { 
       m_currentContent.Refresh(); 
       Messenger.Default.Send(new ShowChildWindowMessage()); 
      } 
     } 
    } 

    public ChildWindowVM() 
    { 
     Messenger.Default.Register<DisplayDetailsMessage>(this, OnDisplayDetails); 
    } 

    private void OnDisplayDetails(DisplayDetailsMessage msg) 
    { 
     CurrentContent = ViewModelLocator.DetailsViewModel; // or whatever view model you want to display 
    } 
} 

刷新()方法是在DetailsViewModel類中定義,並會在顯示窗口之前處理您想要執行的任何初始化。請注意,當設置CurrentContent屬性時,會向MainWindowView觸發一條消息以創建一個用於顯示內容的ChildWindowView實例。

的MainWindowView代碼如下所示:

public partial class MainWindowView : Window 
{ 
    private ChildWindowView m_childWindowView; 

    public MainWindowView() 
    { 
     InitializeComponent(); 
     Closing +=() => ViewModelLocator.CleanUp();  

     Messenger.Default.Register<ShowChildWindowMessage>(this, OnShowChildWindow); 
     Messenger.Default.Register<HideChildWindowMessage>(this, OnHideChildWindow); 
    } 

    private void OnShowChildWindow(ShowChildWindowMessage msg) 
    { 
     m_childWindowView = new ChildWindowView(); 
     m_childWindowView.ShowDialog(); 
    } 

    private void OnHideChildWindow(HideChildWindowMessage msg) 
    { 
     m_childWindowView.Close(); 
    } 
} 

的最後一步是從ChildWindowVM類CurrentContent屬性綁定到你的ChildWindowView類。這是在XAML做了你的ChildWindowView:

<Window x:Class="Garmin.Cartography.AdminBucketTools.ChildWindowView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    DataContext="{Binding Path=ChildWindowVm, Source={StaticResource Locator}}"> 

<Grid> 
    <ContentPresenter Content="{Binding Path=CurrentContent}" /> 
</Grid> 

現在你可以在應用程序的任何地方顯示您的詳細信息,只需致電

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

,您可以通過編程方式關閉窗口打電話

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

你也可以派生出和你一樣多的類想從MessageBase中註冊它們到您的ChildWindowVM類中。在每個消息處理程序中,只需將CurrentContent屬性設置爲適當的視圖模型,即可指定要顯示的內容。

實際上還有一件事。如果您真的想在子窗口中看到任何有用的內容,您需要在視圖和視圖模型之間指定模板綁定。這可以通過XAML應用程序中的資源來完成:

<DataTemplate DataType="{x:Type viewmodels:DetailsViewModel}"> 
    <views:DetailsView /> 
</DataTemplate> 

不要忘記定義的命名空間(即「的ViewModels」和「意見」)。

+0

榮譽給予它如此好的努力,以幫助一個隨機的陌生人:) –

+0

感謝您的廣泛答案。你寫道,虛擬機生活在非UI線程中。似乎很清楚Messenger是否也會這樣做,我的問題將會消失。我如何讓ViewModelLocator在不同的線程上創建虛擬機?順便說一下,我讀到了將VM放到非UI線程時,我會在ObservableCollections中遇到其他問題。 – Batuu

+0

對不起,我沒有正確地說出關於UI線程的內容。我的意思是說你的視圖模型中的代碼可以在單獨的線程中運行。我已經更新了我的答案。此外,就ObservableCollection而言,如果您嘗試從另一個線程更新綁定到視圖的集合,則只會遇到問題。這必須在UI線程中完成,所以只需從其他線程執行:'App.Current.Dispatcher.Invoke(new Action((=)=> {//更新集合});' – bugged87

相關問題