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

FireMonkey中拦截窗口移动位置失败的问题求助

FireMonkey窗体位置捕获问题解决方案

问题根源

FireMonkey(FMX)与VCL的消息机制完全不同:VCL直接映射Win32窗口消息,而FMX是跨平台框架,底层窗口消息被框架内部封装处理,你重写的WndMethod只能捕获FMX内部消息,无法获取Windows原生的WM_MOVE,这就是移动窗体时无响应的原因。

优先推荐:跨平台通用方案

因为你需要移植到Mac,以下两种方法在Windows和Mac平台都能正常工作:

方案1:重写SetBounds方法

窗体位置变更最终会调用SetBounds,重写该方法即可捕获位置变化:

// 声明部分
protected
  procedure SetBounds(ALeft, ATop, AWidth, AHeight: Integer); override;

// 实现部分
procedure TMYForm.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
  inherited; // 先让窗体完成位置更新
  Edit_X.Text := 'x - ' + Left.ToString;
  Edit_Y.Text := 'y - ' + Top.ToString;
end;

方案2:监听Position属性变更

利用FMX的属性通知机制,监控窗体Position属性的变化:

// 声明部分
private
  procedure OnPositionChanged(const AObject: TObject; const APropertyName: string);

// 实现部分
procedure TMYForm.FormCreate(Sender: TObject);
begin
  Self.AddPropertyObserver('Position', @OnPositionChanged);
end;

procedure TMYForm.FormDestroy(Sender: TObject);
begin
  Self.RemovePropertyObserver('Position', @OnPositionChanged);
end;

procedure TMYForm.OnPositionChanged(const AObject: TObject; const APropertyName: string);
begin
  Edit_X.Text := 'x - ' + Position.X.ToString;
  Edit_Y.Text := 'y - ' + Position.Y.ToString;
end;

Windows平台专用方案(仅适配Windows)

如果只需要在Windows平台捕获WM_MOVE,需要对FMX底层窗口进行子类化:

// 声明部分
private
  FOldWndProc: TFNHookProc;
  procedure WndProcHook(AHWND: HWND; AMsg: UINT; AWParam: WPARAM; ALParam: LPARAM; var AResult: LRESULT);
protected
  procedure CreateWnd; override;
  procedure DestroyWnd; override;

// 实现部分
procedure TMYForm.CreateWnd;
begin
  inherited;
  if FHWND <> 0 then
  begin
    // 子类化底层窗口
    FOldWndProc := TFNHookProc(SetWindowLongPtr(FHWND, GWLP_WNDPROC, LONG_PTR(@WndProcHook)));
  end;
end;

procedure TMYForm.DestroyWnd;
begin
  if (FHWND <> 0) and Assigned(FOldWndProc) then
  begin
    // 恢复原窗口过程
    SetWindowLongPtr(FHWND, GWLP_WNDPROC, LONG_PTR(FOldWndProc));
    FOldWndProc := nil;
  end;
  inherited;
end;

procedure TMYForm.WndProcHook(AHWND: HWND; AMsg: UINT; AWParam: WPARAM; ALParam: LPARAM; var AResult: LRESULT);
begin
  // 先调用原窗口过程处理消息
  AResult := CallWindowProc(FOldWndProc, AHwnd, AMsg, AWParam, ALParam);
  if AMsg = WM_MOVE then
  begin
    // 从WM_MOVE的LPARAM中直接获取原生窗口位置
    Edit_X.Text := 'x - ' + IntToStr(LOWORD(ALParam));
    Edit_Y.Text := 'y - ' + IntToStr(HIWORD(ALParam));
  end;
end;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 14:46:01