pmAuto ModalPopupMode 正确使用或错误解决方法

Ken*_*ssa 4 delphi delphi-xe4

我在使用 TApplication.ModalPopupMode=pmAuto 时遇到问题,我想知道我的问题是否是由我使用 pmAuto 或 delphi 中的错误引起的。

简单用例:

  • Form1(MainForm) 和 Form3 是永久表单。(在 dpr 中创建)
  • Form2 在需要时创建并在之后释放。
  • Form3 包含一个带有 X 个项目的 TComboBox。

动作顺序:

  • Form1 创建并显示 Form2 模式。
  • Form2 显示 form3 模态。
  • 关闭Form3
  • 关闭并免费Form2
  • 显示 Form3 <---- TComboBox 现在包含 0 个项目。

我使用 ComboBox 作为示例,但我猜想在 DestroyWnd 过程中保存信息并在 CreateWnd 过程中恢复信息的任何控件都无法正常工作。我测试了 TListBox,它也显示相同的行为。

  • 当 ModalPopupMode 为 pmAuto 时,不应该混合永久形式和临时形式,这是一个已知的事实吗?
  • 如果没有,是否有任何已知的解决方法可以解决此问题?
  • 如果这是一个错误,最新版本的 Delphi 是否已修复?(我用的是XE4)

Rem*_*eau 6

这并不是真正的错误,只是处理模态时各个窗口如何相互交互的一个怪癖。

Form3首次创建时,TComboBox.CreateWnd()在 DFM 流式传输期间调用。Form3.ShowModal()第一次调用时,如果它是或不是,Form3则调用自身。好的,所以被调用,保存项目,然后被调用,恢复项目。期间重新创建 的窗口并不理想,但这次可以了。RecreateWnd()PopupModepmNoneApplication.ModalPopupModepmNoneTComboBox.DestroyWnd()TComboBox.CreateWnd()TComboBoxShowModal()

Form3.ShowModal()第二次调用时,TComboBox.CreateWnd()会再次调用而无需先前调用TComboBox.DestroyWnd()! 由于项目尚未保存,因此无法恢复。这就是为什么它TComboBox是空的。

但为什么会出现这种情况呢?当Form2被释放时,Form3的窗口仍然与Form2的窗口相关联。第一次调用将的窗口Form3.ShowModal设置为的父/所有者窗口。当您关闭 a 时,它只是被隐藏,它的窗口仍然存在。因此,当和关闭时,它们仍然存在并链接在一起,然后当被销毁时,其所有子窗口和拥有的窗口都被销毁。 收到一条消息,将其重置为 0,而不通知其其余代码窗口正在被销毁。因此,没有机会保存其当前项目,因为没有被调用。 仅当 VCL 本身销毁窗口时调用,而不是当操作系统销毁窗口时调用。Form2Form3TFormForm2Form3Form2TComboBoxWM_NCDESTROYHandleTComboBoxDestroyWnd()DestroyWnd()

现在,你该如何解决这个问题?在释放 之前,您必须销毁 的TComboBox窗口,触发其DestroyWnd()方法Form2。诀窍是,只有在属性中启用TComboBox.DestroyWnd()该标志时才会保存项目。您可以通过几种不同的方法来实现这一目标:csRecreatingTComboBox.ControlState

  1. TWinControl.UpdateRecreatingFlag()直接打电话TWinControl.DestroyHandle()。它们都是protected,因此您可以使用访问器类来访问它们:

    type
      TComboBoxAccess = class(TComboBox)
      end;
    
    Form2 := TForm2.Create(nil);
    try
      Form2.ShowModal;
    finally
      with TComboBoxAccess(Form3.ComboBox1) do
      begin
        UpdateRecreatingFlag(True);
        DestroyHandle;
        UpdateRecreatingFlag(False);
      end;
      Frm.Free;
    end;
    Form3.ShowModal;
    
    Run Code Online (Sandbox Code Playgroud)
  2. 直接打电话TWinControl.RecreateWnd()。它也是protected,因此您可以使用访问器类来访问它:

    type
      TComboBoxAccess = class(TComboBox)
      end;
    
    Form2 := TForm2.Create(nil);
    try
      Form2.ShowModal;
    finally
      TComboBoxAccess(Form3.ComboBox1).RecreateWnd;
      Frm.Free;
    end;
    Form3.ShowModal;
    
    Run Code Online (Sandbox Code Playgroud)

    TComboBox直到下一次需要窗口时(在后续的ShowModal().

  3. 向窗口发送TComboBox一条CM_DESTROYHANDLE消息,让我们TWinControl为您处理一切:

    Form2 := TForm2.Create(nil);
    try
      Form2.ShowModal;
    finally
      if Form3.ComboBox1.HandleAllocated then
        SendMessage(Form3.ComboBox1.Handle, CM_DESTROYHANDLE, 1, 0);
      Frm.Free;
    end;
    Form3.ShowModal;
    
    Run Code Online (Sandbox Code Playgroud)

    CM_DESTROYHANDLETWinControl.DestroyHandle()在销毁子窗口时在内部使用。当TWinControl组件收到该消息时,它会自行调用UpdateRecreatingFlag()DestroyHandle()