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

