FMX Windows平台模态窗体/对话框无法保持置顶问题(Delphi 10.4)
Delphi 10.4 FMX Windows平台模态窗口无法置顶问题的成因与修复方案
问题成因
- FMX作为跨平台框架,窗口管理逻辑基于跨平台抽象层实现,在Windows平台没有完全适配原生窗口的所有权(Owner)绑定机制,和VCL直接调用Windows原生模态逻辑的实现差异很大
- FMX默认无参数的
ShowModal、对话框Execute方法不会正确将当前活动窗体作为父句柄传入,Windows系统维护Z序时无法识别窗口的从属关系,切换外部窗口再切回应用时会直接把主窗体置顶,覆盖从属模态窗口 - 10.4版本的FMX存在已知的模态循环逻辑缺陷,未拦截Windows的
WM_ACTIVATE消息做Z序矫正,进一步放大了该问题
可行修复方案
1. 模态窗口弹出强制指定Owner
所有模态窗体调用ShowModal时必须传入父窗体作为参数:
// 原错误写法 Form2.ShowModal; // 修正后写法 Form2.ShowModal(Self); // Self为父窗体对象
如果是非模态但需要始终在父窗体上方的窗口,除了指定Owner外,可以在Windows平台下手动绑定原生窗口所有者:
uses FMX.Platform.Win, Winapi.Windows; procedure TForm2.FormCreate(Sender: TObject); var OwnerWnd: HWND; begin if TOSVersion.Platform = pfWindows then begin OwnerWnd := FormToHWND(Form1); SetWindowLongPtr(WindowHandleToHWND(Handle), GWL_HWNDPARENT, OwnerWnd); end; end;
2. 通用对话框调用显式传父句柄
所有TOpenDialog、TSaveDialog等通用对话框调用Execute时传入当前窗体的Handle作为参数:
// 原错误写法 SaveDialog1.Execute; // 修正后写法 SaveDialog1.Execute(Handle); // Handle为当前弹出对话框的窗体句柄
3. 提示框统一使用TDialogServiceSync实现
避免使用FMX自带的ShowMessage,统一调用TDialogServiceSync.ShowMessage,调用前确保已经注册了对话框服务:
uses FMX.DialogService, FMX.DialogService.Sync; // 程序初始化时注册服务 if not TDialogService.IsRegistered then TDialogService.RegisterDialogService(TDialogServiceSync.Create); // 调用提示框 TDialogServiceSync.ShowMessage('提示内容');
4. 版本升级或源码补丁
该问题为Delphi 10.4的已知缺陷,在11.0及后续版本中官方已经修复了FMX窗口的Z序管理逻辑,可直接升级版本解决。如果需要停留在10.4版本,可自行修改FMX.Forms单元中Windows平台的模态窗口实现部分,补全Owner句柄绑定逻辑。
内容的提问来源于stack exchange,提问作者XylemFlow
相关产品推荐
相关产品推荐

