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

简单用例:


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


动作顺序:


Form1创建并显示Form2模态。
Form2显示form3模态。
关闭表格3
关闭并免费使用Form2
显示Form3

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


是否已知的事实是,当ModalPopupMode为pmAuto时,不应混合使用永久形式和临时形式?
如果不是,是否有解决此问题的已知方法?
如果是错误,是否已在最新版本的Delphi中修复了? (我正在使用XE4)

最佳答案

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

首次创建Form3时,在DFM流传输期间将调用TComboBox.CreateWnd()。首次调用Form3.ShowModal()时,如果Form3RecreateWnd()并且PopupMode不是pmNone,则Application.ModalPopupMode会自行调用pmNone。好的,所以调用TComboBox.DestroyWnd()保存项目,然后调用TComboBox.CreateWnd()还原项目。在TComboBox期间重新创建ShowModal()的窗口不是很理想,但是这次可以。

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

但是为什么会这样呢?释放Form2时,Form3的窗口仍与Form2的窗口关联。对Form3.ShowModal的第一次调用将Form2的窗口设置为Form3的父/所有者窗口。关闭TForm时,它只是隐藏的,它的窗口仍然存在。因此,当Form2Form3关闭时,它们仍然存在并链接在一起,然后当Form2被销毁时,其所有子窗口和拥有的窗口都将被销毁。 TComboBox收到一条WM_NCDESTROY消息,将其Handle重置为0,而没有通知其余代码该窗口已被破坏。因此,TComboBox没有机会保存其当前项目,因为未调用DestroyWnd()。仅当VCL本身正在破坏窗口时才调用DestroyWnd(),而不是在OS破坏窗口时调用。

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


直接调用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;

直接致电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;


实际上,直到下一次需要使用TComboBox窗口时,才在随后的ShowModal()中创建该窗口。
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;


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

关于delphi - pmAuto ModalPopupMode正确使用或错误解决方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32099167/

10-12 19:23