我在使用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()
时,如果Form3
是RecreateWnd()
并且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
时,它只是隐藏的,它的窗口仍然存在。因此,当Form2
和Form3
关闭时,它们仍然存在并链接在一起,然后当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/