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

如何使TFiremonkeyContainer响应鼠标事件并获取坐标?

解决TFiremonkeyContainer鼠标事件不触发的问题

核心原因

TFiremonkeyContainer内部承载的FMX窗口会拦截大部分鼠标消息,导致VCL层面的OnMouseDown/OnMouseMove等事件无法触发;而拖拽事件是控件已做过VCL-FMX兼容处理的,所以能正常工作。

解决方法

方法一:重写容器的WndProc拦截鼠标消息

通过自定义继承自TFiremonkeyContainer的控件,重写WndProc直接捕获Windows鼠标消息,绕过FMX的消息拦截:

type
  TCustomFiremonkeyContainer = class(vintagedave.TFiremonkeyContainer)
  protected
    procedure WndProc(var Message: TMessage); override;
  public
    // 自定义对应鼠标事件
    OnCustomMouseDown: TMouseEvent;
    OnCustomMouseMove: TMouseMoveEvent;
    OnCustomMouseUp: TMouseEvent;
    OnCustomMouseEnter: TNotifyEvent;
    OnCustomMouseLeave: TNotifyEvent;
  end;

procedure TCustomFiremonkeyContainer.WndProc(var Message: TMessage);
var
  MousePos: TPoint;
begin
  inherited; // 先让原控件处理FMX消息

  case Message.Msg of
    // 处理鼠标按下
    WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN:
      begin
        MousePos := ScreenToClient(SmallPointToPoint(TWMMouse(Message).Pos));
        if Assigned(OnCustomMouseDown) then
          OnCustomMouseDown(Self, TMouseButton(Message.Msg - WM_LBUTTONDOWN), [], MousePos.X, MousePos.Y);
      end;
    // 处理鼠标移动
    WM_MOUSEMOVE:
      begin
        MousePos := ScreenToClient(SmallPointToPoint(TWMMouseMove(Message).Pos));
        if Assigned(OnCustomMouseMove) then
          OnCustomMouseMove(Self, [], MousePos.X, MousePos.Y);
        // 初始化鼠标进入/离开跟踪
        TrackMouseEvent(Self.Handle, TME_ENTER or TME_LEAVE);
      end;
    // 处理鼠标抬起
    WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP:
      begin
        MousePos := ScreenToClient(SmallPointToPoint(TWMMouse(Message).Pos));
        if Assigned(OnCustomMouseUp) then
          OnCustomMouseUp(Self, TMouseButton(Message.Msg - WM_LBUTTONUP), [], MousePos.X, MousePos.Y);
      end;
    // 处理鼠标进入
    WM_MOUSEENTER:
      begin
        if Assigned(OnCustomMouseEnter) then
          OnCustomMouseEnter(Self);
        TrackMouseEvent(Self.Handle, TME_ENTER or TME_LEAVE);
      end;
    // 处理鼠标离开
    WM_MOUSELEAVE:
      begin
        if Assigned(OnCustomMouseLeave) then
          OnCustomMouseLeave(Self);
        TrackMouseEvent(Self.Handle, TME_ENTER or TME_LEAVE);
      end;
  end;
end;

使用时,在窗体上放置这个自定义容器,直接绑定OnCustomMouseDown等事件即可获取鼠标坐标。

方法二:利用FMX内部控件的鼠标事件

直接在容器承载的FMX根控件上绑定鼠标事件,从FMX层面获取坐标后再同步到VCL逻辑:

procedure TMainForm.FormCreate(Sender: TObject);
var
  FMXRootControl: TControl;
begin
  // 获取FMX容器的根控件
  if FiremonkeyContainer1.FMXForm.Root is TControl then
  begin
    FMXRootControl := TControl(FiremonkeyContainer1.FMXForm.Root);
    FMXRootControl.OnMouseDown := FMXMouseDownHandler;
    FMXRootControl.OnMouseMove := FMXMouseMoveHandler;
    FMXRootControl.OnMouseUp := FMXMouseUpHandler;
  end;
end;

procedure TMainForm.FMXMouseDownHandler(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
var
  VCLPoint: TPoint;
begin
  // 把FMX坐标转换为VCL容器内的坐标
  VCLPoint := FiremonkeyContainer1.ScreenToClient(
    FiremonkeyContainer1.FMXToScreen(PointF(X, Y))
  );
  // 这里处理你的逻辑,比如记录按下坐标
end;

方法三:修改原控件的消息处理逻辑

直接修改TFiremonkeyContainer的源代码,在其WndProc方法中,处理完FMX消息后主动触发VCL的鼠标事件。比如在原控件的WndProc末尾添加类似方法一的消息判断代码,调用MouseDown/MouseMove等原生VCL事件方法。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:33:36