【问题标题】:Prism: DialogService - Activate existing non-modal dialogPrism:DialogService - 激活现有的非模态对话框
【发布时间】:2020-08-28 18:44:43
【问题描述】:

我们正在努力将我们的 INotification/IConfirmation 对话框迁移到 Prism 的 DialogService。我们遇到的一个问题是支持我们对PopupWindowAction 进行的自定义,如果该操作被第二次调用,则可以选择将现有的非模态对话框“置于前面”。

这可以通过DialogService 完成吗?

具体来说,如果非模态窗口被最小化或处于非活动状态(在另一个窗口之后),我们如何激活它?我们目前正在使用类似以下的代码,它可以识别特定的INotification 之前已显示并简单地激活它。

if (BringToFrontIfExisting)
{
    if (NotificationWindowMap.TryGetValue(notification, out Window window))
    {
        if (window.WindowState == WindowState.Minimized)
        {
            window.WindowState = WindowState.Normal;
        }

        window.Activate();

        return;
    }
}

我们考虑扩展DialogService;不幸的是,我们似乎需要扩展ShowDialogInternal,它不是虚拟的。 IDialogWindow 也不会暴露 WindowStateActivate

似乎我们可以通过注册对话框实例并在DialogService 外部管理此激活来在外部完成此操作。虽然我想在这里尽可能多地使用 Prism 并尽量减少 ViewModel 的复杂性。

【问题讨论】:

    标签: c# wpf prism


    【解决方案1】:

    这可以通过DialogService来实现吗?

    不,Prism 的DialogService 不支持此功能。

    [...] 不幸的是,我们似乎需要扩展不是虚拟的 ShowDialogInternal。

    正如您已经注意到的,没有虚拟方法,因此您不能从它派生并覆盖它的行为。您必须创建自己的实现,但至少您可以复制 default implementation from GitHub 并使其适应您的需求。

    IDialogWindow 也不公开 WindowState 或 Activate。

    IDialogWindow 接口有一个限制或设计选择,因为它不提供与Window 相同的方法或属性。如果您可以保证您的对话主机是派生自Window 的类型,您可以将自定义对话服务中的IDialogWindow 实例强制转换为Window

    更通用的变体是创建自定义对话框窗口界面:

    • 创建自己的ICustomDialogWindow 接口,继承IDialogWindow 以实现兼容性
    • 为其添加缺少的属性和方法,例如Activate
    • 通过例如创建CustomWindowWindow 派生并实现ICustomDialogWindow
    • 将此窗口类型注册为容器中新的默认对话宿主窗口
      containerRegistry.RegisterDialogWindow<ConfirmationWindow>();
      

    现在您可以将对话窗口实例投射到自定义对话服务中的ICustomDialogWindow 接口和Activate 它。或者,您也可以在对话服务本身中实现激活行为,然后该接口仅用于公开窗口所需的方法和属性以执行此操作。在具体对话框窗口类中实现激活更加灵活,因为它可以专门用于底层类型或在无法实现通用实现时应用。

    如果您已经使用 Prism 8 并允许多个对话主机,您可以在所有窗口主机中实现ICustomDialogWindow 接口,如果主机不提供或仅根据类型提供不同的行为,则抛出异常,例如IDialogWindow 不支持激活。一般来说,你必须自己创建对话宿主窗口类型来实现接口,所以这应该不是问题。

    具体来说,如果非模态窗口被最小化或处于非活动状态(在另一个窗口之后),我们如何激活它?

    您提供的代码在这种情况下应该可以正常工作。您只需要决定是要在对话服务还是自定义窗口宿主中实现它。

    似乎我们可以通过注册对话框实例并在 DialogService 外部管理此激活来在外部完成此操作。

    然后您将完全解决对话服务,因为它不会公开已创建对话的实例。您必须从应用程序窗口集合中找到它们并自己处理激活,这几乎是一个对话服务的一半,因此您可以首先创建一个自定义服务。

    【讨论】:

    • 谢谢,这证实了我的怀疑。我可以在 Prism 上提交增强请求以使 ShowDialogInternal(或类似)虚拟化。我相信我可以在不完全替换服务的情况下完成此操作。
    【解决方案2】:

    以下是我为解决我最初的问题而提出的解决方案。从本质上讲,DialogServiceEx 支持选择性地激活现有的无模式对话框,而不是显示第二个副本。此外,对话框所有者也是可选的;这允许对话框独立于所有者(可以独立最小化等)

    注意:该服务不是线程安全的。假设对话框只会显示在 UI 线程上。

    public class DialogServiceEx : DialogService, IDialogServiceEx
    {
        private readonly Dictionary<string, IDialogWindow> _reusableDialogWindows = new Dictionary<string, IDialogWindow>();
    
        public DialogServiceEx(IContainerExtension containerExtension)
            : base(containerExtension)
        {
        }
    
        public void Show(
            string name,
            IDialogParameters parameters,
            Action<IDialogResult> callback,
            bool reuseExistingWindow,
            bool setOwner = true)
                => ShowDialogInternalEx(name, parameters, callback, null, reuseExistingWindow, setOwner);
    
        public void Show(
            string name,
            IDialogParameters parameters,
            Action<IDialogResult> callback,
            string windowName,
            bool reuseExistingWindow,
            bool setOwner = true)
                => ShowDialogInternalEx(name, parameters, callback, windowName, reuseExistingWindow, setOwner);
    
        private void ShowDialogInternalEx(
            string name,
            IDialogParameters parameters,
            Action<IDialogResult> callback,
            string windowName,
            bool reuseExistingWindow,
            bool setOwner)
        {
            string dialogKey = $"{name}-{windowName}";
            if (reuseExistingWindow &&
                _reusableDialogWindows.TryGetValue($"{name}-{windowName}", out IDialogWindow dialogWindow))
            {
                dialogWindow.Show();
                if (dialogWindow is Window window)
                {
                    // NOTE: IDialogWindow should always be a Window under WPF.
    
                    if (window.WindowState == WindowState.Minimized)
                    {
                        window.WindowState = WindowState.Normal;
                    }
    
                    window.Activate();
                    return;
                }
            }
    
            dialogWindow = CreateDialogWindow(windowName);
    
            if (reuseExistingWindow)
            {
                _reusableDialogWindows.Add(dialogKey, dialogWindow);
                dialogWindow.Closed +=
                    (_, __) =>
                    {
                        Debug.Assert(_reusableDialogWindows.ContainsKey(dialogKey), "Expect single-threaded access only.");
                        _reusableDialogWindows.Remove(dialogKey);
                    };
            }
    
            ConfigureDialogWindowEvents(dialogWindow, callback);
            ConfigureDialogWindowContent(name, dialogWindow, parameters ?? new DialogParameters());
    
            if (setOwner == false)
            {
                dialogWindow.Owner = null;
            }
    
            ShowDialogWindow(dialogWindow, false);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      相关资源
      最近更新 更多