Prism IDialogService 显示非模态对话框充当模态

Prism IDialogService Show Non-Modal Dialog acts as Modal

在 WPF Prism 7.2 中,我遵循了 IDialogService instructions shown here

我有一个 PlotsDialogPanel UserControl 和一个 ContentControl ,如下所示:

<UserControl x:Class="PlotModule.Dialogs.Views.PlotsDialogPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             xmlns:local="clr-namespace:PlotModule.Dialogs.Views"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <prism:Dialog.WindowStyle>
        <Style TargetType="Window">
            <Setter Property="prism:Dialog.WindowStartupLocation" Value="CenterScreen" />
            <Setter Property="ResizeMode" Value="CanResizeWithGrip"/>
            <Setter Property="ShowInTaskbar" Value="True"/>
        </Style>
    </prism:Dialog.WindowStyle>

    <Grid>
        <ContentControl prism:RegionManager.RegionName="PlotsDialogDisplayRegion" />
    </Grid>
</UserControl>

在我的 PlotModule RegisterTypes 方法中,我注册了对话框:

public void RegisterTypes(IContainerRegistry containerRegistry)
{
   containerRegistry.RegisterDialog<PlotsDialogPanel, PlotsDialogPanelViewModel>();
}

但是当对话框显示在事件处理程序中时,它充当模态,它始终位于父级的最顶层。

private void MainMenuEventHandler(string inParameter)
{
   _DialogService.Show("PlotsDialogPanel", new DialogParameters(), r => {});
}

我在这里看不出我做错了什么,关于为什么对话框表现为模态的任何想法?其他一切都按预期工作,对话框显示并关闭,IDialogAware::OnDialogOpenedOnDialogClosed 按预期运行。

它实际上不是模态的,只是最上面的window。使用 ShowDialog 方法显示模态对话框,它不仅会出现在最顶部 window,而且还会禁止用户与其所有者进行任何交互 window,直到被关闭。

一个对话框位于另一个 window 之上的原因是它有一个所有者 window 分配给它的 Owner 属性。默认情况下,Prism 将始终将应用程序中的第一个 active window 指定为对话框的所有者,除非您设置了一个 not null 的所有者 明确在对话主机上 window。

请参阅 GitHub 上 DialogService 的实施以供参考。此行设置对话框的 Owner

if (window.Owner == null)
   window.Owner = Application.Current.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);

由于这是 DialogService 的实现方式,您必须创建自己的对话服务并更改其行为。例如,您可以提供重载以不设置所有者。实现原始 IDialogService 接口以提供与默认实现的兼容性。

public interface ICustomDialogService : IDialogService
{
   public void ShowOrphan(string name, IDialogParameters parameters, Action<IDialogResult> callback);
}

创建从 ICustomDialogService 派生的类型 CustomDialogService 并从 GitHub 复制 original implementation。通过将参数传递给 ConfigureDialogWindowProperties 方法来调整原始方法,以确定是否设置 Owner 并采取相应措施。

public class CustomDialogService : ICustomDialogService
{
   // ...other public members.

   public void ShowOrphan(string name, IDialogParameters parameters, Action<IDialogResult> callback)
   {
      // Appended a new parameter "isOrphan"
      ShowDialogInternal(name, parameters, callback, false, true);
   }

   // ...other private members.

   // Appended a new parameter "isOrphan"
   void ConfigureDialogWindowProperties(IDialogWindow window, FrameworkElement dialogContent, IDialogAware viewModel, bool isOrphan)
   {
      // ...other code.

      if (isOrphan)
         return;

      if (window.Owner == null)
         window.Owner = System.Windows.Application.Current.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);
   }
}

使用您的界面和原始界面注册自定义对话框服务,以便您可以同时使用两者。

containerRegistry.RegisterSingleton <CustomDialogService>();
containerRegistry.Register<IDialogService, CustomDialogService>();
containerRegistry.Register<ICustomDialogService, CustomDialogService>();

可能还有其他解决方法,例如创建忽略 Owner 属性 的自定义对话主机 window 或其他 hack,但我认为创建自定义对话服务是最干净的接近这里。