FMX技术问题:调用TForm.Show与TForm.ShowModal时偶现子窗体显示于主窗体下方的异常
我之前在Delphi 10.4.2的项目开发中,也碰到过完全一模一样的间歇性bug——子窗体调用Show/ShowModal后闪一下就躲到主窗体下面,非模态还能靠折腾主窗体找出来,模态直接卡死只能任务管理器强关,那段时间用户投诉快把我搞疯了😅。
这个问题确实有不少开发者遇到过,而且在Embarcadero的官方Quality Portal和开发者论坛上都有大量上报,属于10.4版本里VCL窗体层级管理的典型bug之一。下面是我亲测有效的几个解决方案:
1. 延迟子窗体的显示时机
不要在主窗体的OnCreate、OnShow这类早期事件里直接显示子窗体,这时候主窗体的初始化还没完全完成,很容易出现焦点抢占和层级错乱。可以用TThread.Queue延迟执行显示逻辑,让主窗体彻底就绪后再弹出子窗体:
// 在需要显示子窗体的地方写这段代码 TThread.Queue(nil, procedure begin ChildForm.ShowModal; // 或者 ChildForm.Show; end);
2. 临时调整主窗体的最大化状态
这个bug在主窗体最大化时触发概率极高,所以可以在显示子窗体前临时把主窗体恢复为正常尺寸,显示完成后再还原最大化状态:
var WasMaximized: Boolean; begin WasMaximized := MainForm.WindowState = wsMaximized; if WasMaximized then MainForm.WindowState := wsNormal; try ChildForm.ShowModal; finally if WasMaximized then MainForm.WindowState := wsMaximized; end; end;
亲测这个方法对模态窗体的卡死问题效果立竿见影,能直接避免子窗体被完全遮挡的情况。
3. 升级版本或安装官方补丁
Embarcadero在10.4.3的补丁包中修复了多个相关的UI焦点和窗体层级bug,如果项目允许的话,升级到10.4.3或者更高版本(比如Delphi 11 Alexandria)是最彻底的解决办法。如果暂时不能升级,也可以去官方Quality Portal搜索关键词「ShowModal form hidden behind main form」,找到对应的bug记录和官方提供的补丁文件。
4. 自定义模态显示逻辑(临时应急方案)
如果以上方法都没法用,可以自己模拟模态窗体的效果:先禁用主窗体,把子窗体设置为置顶显示,关闭后再恢复主窗体的状态:
begin MainForm.Enabled := False; try ChildForm.FormStyle := fsStayOnTop; ChildForm.Show; // 循环处理消息直到子窗体关闭 while ChildForm.Visible do Application.ProcessMessages; finally MainForm.Enabled := True; ChildForm.FormStyle := fsNormal; end; end;
这个方法要注意处理消息循环的边界情况,但作为临时应急方案完全可行。
内容的提问来源于stack exchange,提问作者XylemFlow

