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

时间:2015-07-29 16:14:46

标签: delphi delphi-xe6

我有两个"无模式" 表单:

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

enter image description here

你可以看到:

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

现在显示模态表单

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

enter image description here

模态表单构造如下:

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;

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

protected
   procedure SetOwnerForm(const Value: TForm);
public
   property OwnerForm: TForm read GetOwnerForm write SetOwnerForm;
end;

强制处理娱乐:

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

    if Self.HandleAllocated then
        Self.RecreateWnd;
end;

然后第二次通过CreateParams

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

    if FOwnerForm <> nil then
        Params.WndParent := FOwnerForm.Handle;
end;

问题是:

  • 一旦显示此拥有的模式表单,我就无法与 MainForm
  • 进行交互
  • 我无法使用任务栏按钮
  • 最小化 MainForm
  • 我无法使用任务栏按钮
  • 最小化模态或其拥有的父级
  • 如果我使用最小化按钮最小化模态窗体, MainForm 会消失
  • 我可以使用任务栏按钮激活 MainForm ;但我无法与之互动

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

奖金:自.NET 1.0起,WinForms已正确处理此问题。

关于模态对话框的内容存在很多混淆。在您继续使用其所有者之前必须与其进行交互时,对话框是模态的。来自Windows Interface Design Guidelines

  

对话框有两种基本类型:

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

Windows具有&#34;所有者&#34; 的概念。当一个窗口&#34;拥有&#34; 时,它将始终显示在其所有者之上。当窗口为&#34; modal&#34; 时,表示所有者被禁用,直到模态任务完成。

您可以在ProgressDialog API中看到此效果:

HRESULT StartProgressDialog(
  [in] HWND     hwndParent,
       IUnknown *punkEnableModless,
       DWORD    dwFlags,
       LPCVOID  pvReserved
);
     

hwndParent [in]
  输入: HWND
  对话框的父窗口的句柄。

     

dwFlags中
  键入: DWORD
  的 PROGDLG_MODAL
  进度对话框将模态为 hwndParent 指定的窗口。默认情况下,进度对话框是无模式的。

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

  • 在帖子中
  • 过程
  • 或系统

但我希望正确的行为。我想这样做:

  • Windows做什么
  • Office应用程序执行的操作
  • 超越比较的事情
  • WinForms做什么
  • WPF做什么
  • 我曾经使用的每个应用程序
  • 以及任何用户期望的内容

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

1 个答案:

答案 0 :(得分:7)

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

您必须巧妙地显示此表单,以使其按您希望的方式运行。执行以下操作:

  1. 禁用无模式所有者表单。
  2. 通过调用Show
  3. 来显示“模态”表单
  4. 关闭“模态”表单后,启用无模式所有者。在“模态”窗体的窗口被销毁之前,请确保已启用所有者,如下所述。
  5. 你可以在第2步和第3步之间运行自己的模态消息循环,就像ShowModal那样,但这可能有点过分。我只是将表单显示为无模式,但禁用其所有者使其与该所有者“模态”。

    这个过程有点微妙。查看ShowModal的来源获取灵感。此外,雷蒙德关于情态的史诗系列文章是必不可少的阅读。我在此处链接到所有内容:Why does a MessageBox not block the Application on a synchronized thread?

    Raymond的更多内容:The correct order for disabling and enabling windows

      

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

         

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

         

    销毁模态对话框的正确顺序是

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

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

         

    没有闪烁。没有闯入者。