如何从无模式窗体中显示模态对话框?

Ian*_*oyd 9 delphi delphi-xe6

我有两种"无模式"形式:

  • 一个是特殊的MainForm
  • 另一种是无模式

在此输入图像描述

你可以看到:

  • 两者都存在于任务栏上
  • 两者都有一个任务栏按钮
  • 两者都可以独立最小化
  • 两者都可以独立恢复
  • 两者都不总是在另一方的顶层(拥有)

现在显示一个模态形式

从这种无模式的形式,我想展示一个模态:

在此输入图像描述

模态形式被构造为:

var
    frmExchangeConfirm: TfrmExchangeConfirm;
begin
    frmExchangeConfirm := TfrmExchangeConfirm.Create(Application);
    try
        //Setting popupMode and popupParent still makes the MainForm disabled
//      frmExchangeConfirm.PopupMode := pmExplicit;
//      frmExchangeConfirm.PopupParent := Self; //owned by us

        frmExchangeConfirm.OwnerForm := Self; //tell the form which owner to use
        frmExchangeConfirm.ShowModal;
    finally
        frmExchangeConfirm.Free;
    end;
Run Code Online (Sandbox Code Playgroud)

模态表单被告知通过新OwnerForm属性使用哪个所有者:

protected
   procedure SetOwnerForm(const Value: TForm);
public
   property OwnerForm: TForm read GetOwnerForm write SetOwnerForm;
end;
Run Code Online (Sandbox Code Playgroud)

这迫使手柄娱乐:

procedure TfrmExchangeConfirm.SetOwnerForm(const Value: TForm);
begin
    FOwnerForm := Value;

    if Self.HandleAllocated then
        Self.RecreateWnd;
end;
Run Code Online (Sandbox Code Playgroud)

然后第二次通过CreateParams:

procedure TfrmExchangeConfirm.CreateParams(var Params: TCreateParams);
begin
    inherited;

    if FOwnerForm <> nil then
        Params.WndParent := FOwnerForm.Handle;
end;
Run Code Online (Sandbox Code Playgroud)

问题是:

  • 一旦显示了这种拥有的模态形式,我就无法与MainForm交互
  • 我无法使用任务栏按钮最小化MainForm
  • 我无法使用任务栏按钮最小化Modal或其拥有的父级
  • 如果我使用最小化按钮最小化模态形式,MainForm将消失
  • 我可以使用任务栏按钮激活MainForm ; 但我无法与之互动

在过去的十年里,我已经问了7次这个问题.我最后一次承诺将MainForm作为主要形式将解决所有问题.

好处:自.NET 1.0以来,WinForms已经正确处理了这个问题.

关于模态对话框的内容存在很多困惑.在您继续使用其所有者之前必须与其进行交互时,对话框是模态的.从Windows界面设计指南:

对话框有两种基本类型:

  • 模态对话框要求用户在继续使用所有者窗口之前完成并关闭.这些对话框最适用于需要在继续之前完成的关键或不常见的一次性任务.
  • 无模式对话框允许用户根据需要在对话框和所有者窗口之间切换.这些对话框最适用于频繁,重复,正在进行的任务.

Windows具有"所有者"的概念.当窗口"拥有"时,它将始终显示在其所有者之上.当窗口是"模态"时,意味着所有者被禁用,直到模态任务完成.

你在ProgressDialogAPI中看到这个效果:

HRESULT StartProgressDialog(
  [in] HWND     hwndParent,
       IUnknown *punkEnableModless,
       DWORD    dwFlags,
       LPCVOID  pvReserved
);
Run Code Online (Sandbox Code Playgroud)

hwndParent [in]
类型:HWND
对话框父窗口的句柄.

dwFlags
类型:DWORD
PROGDLG_MODAL
进度对话框将模块化hwndParent指定的窗口.默认情况下,进度对话框是无模式的.

当然,你可能是卑鄙的,并禁用所有其他窗口

  • 在线程中
  • 这个过程
  • 或者系统

但我希望有正确的行为.我想要做:

  • Windows的功能
  • Office应用程序的功能
  • 超越比较的东西
  • WinForms做了什么
  • 什么是WPF
  • 我用过的每一个应用程序都做了什么
  • 以及任何用户期望的内容

自1998年以来,我一直想在我的Delphi应用程序中使用它; 当实现Delphi 3没有正确支持Windows 95和任务栏时.

Dav*_*nan 7

ShowModal禁用同一线程中的所有其他顶级窗口.这包括你的主要表格.

您必须精心显示此表单,以使其按您希望的方式运行.请执行下列操作:

  1. 禁用无模式所有者表单.
  2. 通过调用显示"模态"表单Show.
  3. 关闭"模态"表单后,启用无模式所有者.在"模态"窗体的窗口被销毁之前,请确保已启用所有者,如下所述.

你可以在第2步和第3步之间运行你自己的模态消息循环,ShowModal但这可能是过度的.我只是将表单显示为无模式,但禁用其所有者使其与该所有者"模态".

这个过程有点微妙.寻找ShowModal灵感来源.此外,雷蒙德关于情态的史诗系列文章是必不可少的阅读.我在这里链接到它:为什么MessageBox不会在同步线程上阻止应用程序?

Raymond的更多内容:禁用和启用Windows的正确顺序:

当您销毁模式对话框时,您正在通过前景激活来销毁窗口.窗口管理器现在需要找到其他人来激活.它试图将它提供给对话框的所有者,但是所有者仍然被禁用,因此窗口管理器会跳过它并查找其他窗口,某些未被禁用的窗口.

这就是你得到奇怪的闯入者窗口的原因.

破坏模态对话框的正确顺序是

  • 重新启用所有者.
  • 销毁模态对话框.

这次,当模态对话框被销毁时,窗口管理器会向所有者查找,而这次它已启用,因此它继承了激活.

没有闪烁.没有闯入者.

  • 雷蒙德的旧文章; 特别是当我向Sertac评论模态用户界面是什么时,我会想到要小心禁用和启用哪个命令的问题.我想从根本上说Delphi不会做Windows模式对话框的概念.我真的很讨厌自己必须实现这一切; 我只是假设通过伟大的Delphi 2009转换,所有这些错误都得到了解决.:( (2认同)
  • 写一个类助手.通过一个开放的窗口阵列来禁用.使用与ShowModal相同的代码.使用代码替换DisableTaskWindows/EnableTaskWindows以禁用/启用提供的窗口.任务完成. (2认同)