VCL TStringGrid触摸平移问题:惯性未结束时无法响应二次操作
问题描述
我有一个包含数百行数据的TStringGrid,需要在触摸屏设备上支持触摸平移(手指上下滑动)浏览数据。已设置以下属性:
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRowMoving, goEditing, goRowSelect, goThumbTracking, goFixedRowClick, goFixedHotTrack, goFixedRowDefAlign] TabOrder = 0 Touch.InteractiveGestures = [igPan] Touch.InteractiveGestureOptions = [igoPanSingleFingerVertical, igoPanInertia] Touch.ParentTabletOptions = False Touch.TabletOptions = []
未使用GestureManager。当前触摸拖动和点击操作正常,但平移存在问题:
- 初始操作正常,网格滚动带惯性;等待惯性超时后二次操作也正常。
- 若在惯性计时器超时前发起二次操作,网格会停止滚动,需等计时器结束才会再次响应。
现有OnGesture事件代码:
procedure TForm2.GrdVehiclesGesture(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean); var InertiaActiveChanged: Boolean; begin if EventInfo.GestureID = igiPan then begin InertiaActiveChanged := False; if not FInertiaActive and (gfBegin in EventInfo.Flags) then begin FInertiaActive := True; SendDebug('Inertia starting'); InertiaActiveChanged := True; end; if FInertiaActive and (gfEnd in EventInfo.Flags) then begin FInertiaActive := False; SendDebug('Inertia stopping'); InertiaActiveChanged := True; end; if not FInertiaActive and not InertiaActiveChanged then begin SendDebug('Panning outside inertia'); Handled := True; end; end else SendDebug(IntToStr(EventInfo.GestureID) + ' ' + IntToStr(EventInfo.Location.Y)); end;
调试信息显示:平移时输出Inertia starting,数秒后输出Inertia stopping;若在停止前发起二次操作,会输出Panning outside inertia且网格不滚动。
需要实现:无论惯性计时器是否激活,网格都能响应平移操作。
解决方案
问题出在原OnGesture事件的逻辑里——当惯性激活(FInertiaActive=True)时,新的平移手势没有被标记为Handled:=True,导致网格忽略操作;同时没有主动终止当前惯性,让新触摸无法接管。
修改后的代码如下:
procedure TForm2.GrdVehiclesGesture(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean); begin if EventInfo.GestureID = igiPan then begin // 检测到新平移开始,终止当前惯性(如果存在) if gfBegin in EventInfo.Flags then begin if FInertiaActive then begin SendDebug('Interrupting inertia for new pan'); // 发送消息强制终止惯性滚动 SendMessage(TStringGrid(Sender).Handle, WM_VSCROLL, SB_ENDSCROLL, 0); end; FInertiaActive := True; SendDebug('Inertia starting'); end // 平移结束,标记惯性停止 else if gfEnd in EventInfo.Flags then begin FInertiaActive := False; SendDebug('Inertia stopping'); end; // 所有平移手势都标记为已处理,确保网格响应 Handled := True; end else SendDebug(IntToStr(EventInfo.GestureID) + ' ' + IntToStr(EventInfo.Location.Y)); end;
关键修改说明
- 主动终止惯性:当检测到新的
gfBegin手势时,通过SendMessage发送WM_VSCROLL+SB_ENDSCROLL,强制停止当前惯性滚动,让新触摸操作直接接管。 - 统一标记Handled:移除原代码中仅在非惯性阶段标记
Handled:=True的逻辑,所有平移手势都标记为已处理,确保网格响应每一次触摸操作。 - 简化状态判断:把
FInertiaActive的状态更新逻辑简化,直接根据gfBegin和gfEnd标志处理,避免复杂判断带来的逻辑漏洞。
修改后,无论当前是否处于惯性滚动阶段,用户的二次触摸平移都会立即生效,不会出现等待计时器结束的情况。
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

