任何避免ShowDialog()阻止Messenger的解决方案?

Bat*_*tuu 1 c# wpf modal-dialog mvvm-light

我正在开发一个基于mvvm light toolkit的项目.我有一个MainView和一个DetailsView相应的ViewModels.两个虚拟机都注册了NotificationMessage.

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

收到"ShowDetails"消息时,MainViewModel调用创建"DetailsView"的服务

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

DetailsView用途ViewModelLocator,以获得现有DetailsViewModal的DataContext的.

DetailsViewModel应该接收"ShowDetails"消息来更新其内部状态或要求的一些数据,也.

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

现在的问题是:因为我希望它DetailsView是一个模态窗口,我会调用ShowDialog()它.这似乎阻止了信使,直到DetailsView再次关闭.因此,DetailsViewModal在模态窗口关闭后接收消息.有没有解决方法可以解决这个问题?

如果我能在DetailsViewModal之前注册,我认为它会起作用MainViewModel.这将改变MessageHandler调用的顺序,并在阻塞之前发生VM更新ShowDialog().但是MainViewModel它首先被创建和注册,因为它就是它的本质.它DetailsViewModel是在ViewModalLocator第一次需要时创建的,因此它总是失去竞争.

bug*_*d87 5

不幸的是,我无法重现您的具体问题.我在MainWindowView Loaded事件处理程序中触发了一个单独的线程; 除了不断发送特定消息之外什么也没做的线程.然后我在我的SecondWindowView上调用ShowDialog(),其视图模型已注册以侦听此特定消息.第二个窗口的视图模型中的消息处理程序重复执行.事实上,甚至在调用ShowDailog()之前就调用了处理程序,因为我的视图模型已经由ViewModelLocator在应用程序启动时创建.我需要看一些代码来更好地了解你的情况发生了什么(即你正在创建详细信息窗口的服务,或者我可以编译以重现问题的东西).

您可以尝试以下方法,而不是您的子窗口.在应用程序的某处定义以下类:

public class ShowChildWindowMessage : MessageBase { }
public class HideChildWindowMessage : MessageBase { }
public class DisplayDetailsMessage : MessageBase { }
Run Code Online (Sandbox Code Playgroud)

现在创建以下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
    }
}
Run Code Online (Sandbox Code Playgroud)

Refresh()方法将在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一步是将ChildWtent属性从ChildWindowVM类绑定到ChildWindowView类.这是在您的ChildWindowView的xaml中完成的:

<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>
Run Code Online (Sandbox Code Playgroud)

现在,只需拨打电话即可在应用程序的任何位置显示您的详细信息

Messenger.Default.Send(new DisplayDetailsMessage());
Run Code Online (Sandbox Code Playgroud)

您可以通过调用以编程方式关闭窗口

Messenger.Default.Send(new HideChildWindowMessage());
Run Code Online (Sandbox Code Playgroud)

您还可以从MessageBase派生任意数量的类,并在ChildWindowVM类中注册它们.在每个消息处理程序中,您可以通过将CurrentContent属性设置为适当的视图模型来指定要显示的内容.

还有一件事,实际上.如果您确实希望在子窗口中看到任何有用的内容,则需要在视图和视图模型之间指定模板绑定.这可以通过应用程序资源中的xaml完成:

<DataTemplate DataType="{x:Type viewmodels:DetailsViewModel}">
    <views:DetailsView />
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

不要忘记定义名称空间(即"viewmodels"和"views").