2012-09-16 73 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

相关问题