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
相关产品推荐
相关产品推荐

