You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FMX应用实现聚焦友好弹窗:解决标签功能的焦点与弹窗关闭问题

解决FMX弹窗焦点与关闭逻辑问题(类似Teams标签功能)

1. 基础配置TForm属性

先把弹窗窗体的基础属性调整到位,适配需求:

  • 设置FormStyle为fsStayOnTop,确保弹窗始终悬浮在主窗口上方
  • 设置BorderStyle为bsNone,模拟无框弹窗的样式(按需调整)
  • 调整Position、Size等属性,让弹窗显示在目标Memo控件的合适位置

2. 弹窗显示时不抢占Memo焦点

不要用默认的Show()方法,改用Windows原生API让弹窗显示但不激活,同时强制把焦点还给Memo:

uses Winapi.Windows, FMX.Platform.Win;

procedure TMainForm.ShowTagPopup;
var
  PopupHwnd: HWND;
begin
  if not TagPopupForm.Visible then
  begin
    // 定位弹窗到Memo下方(可根据需求调整坐标计算逻辑)
    TagPopupForm.Left := Memo1.Left + (Memo1.Width - TagPopupForm.Width) div 2;
    TagPopupForm.Top := Memo1.Top + Memo1.Height + 8;
    
    // 获取弹窗的Windows句柄
    PopupHwnd := FmxHandleToHWND(TagPopupForm.Handle);
    // 显示窗体但不激活,避免抢占焦点
    ShowWindow(PopupHwnd, SW_SHOWNOACTIVATE);
    // 强制将焦点放回Memo
    Memo1.SetFocus;
  end;
end;

3. 实现点击外部自动关闭弹窗

通过监听全局鼠标点击事件,判断点击位置是否在弹窗范围内,不在则关闭弹窗:

uses FMX.Platform;

procedure TTagPopupForm.FormCreate(Sender: TObject);
var
  MouseService: IFMXMouseService;
begin
  // 注册全局鼠标点击事件
  if TPlatformServices.Current.SupportsPlatformService(IFMXMouseService, IInterface(MouseService)) then
    MouseService.OnMouseDown := MouseDownHandler;
end;

procedure TTagPopupForm.MouseDownHandler(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
  ScreenClickPos: TPointF;
  PopupScreenRect: TRectF;
begin
  ScreenClickPos := Screen.MousePos;
  PopupScreenRect := Self.AbsoluteRect;
  
  // 点击位置不在弹窗范围内时关闭弹窗,并将焦点放回Memo
  if not PopupScreenRect.Contains(ScreenClickPos) then
  begin
    Self.Close;
    MainForm.Memo1.SetFocus;
  end;
end;

procedure TTagPopupForm.FormDestroy(Sender: TObject);
var
  MouseService: IFMXMouseService;
begin
  // 销毁时取消事件注册,避免内存泄漏
  if TPlatformServices.Current.SupportsPlatformService(IFMXMouseService, IInterface(MouseService)) then
    MouseService.OnMouseDown := nil;
end;

4. 补充优化:避免弹窗内控件焦点切换误关闭

如果遇到弹窗内控件获取焦点时触发窗体Deactivate导致误关闭的情况,可以添加如下逻辑:

procedure TTagPopupForm.FormDeactivate(Sender: TObject);
begin
  // 延迟判断,避免焦点在弹窗内部切换时误触发关闭
  TThread.Queue(nil, procedure
  begin
    if (Screen.FocusedControl = nil) or not Screen.FocusedControl.IsDescendantOf(Self) then
      Self.Close;
  end);
end;

最终效果:

  • 弹窗显示后,Memo保持焦点
  • 点击弹窗内的TEdit时,焦点自动转移到编辑框,弹窗保持打开
  • 点击弹窗外部任意位置,弹窗自动关闭,焦点回到Memo

内容的提问来源于stack exchange,提问作者bas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:50:12