如何在TListView中水平滚动结束时得到通知?

How to be notified at the END of Horizontal Scrolling in TListView?

在 VCL 应用程序中,我试图在使用此插入器 class 代码在 TListView 中结束水平滚动时收到通知:

type  
  TListView = class(Vcl.ComCtrls.TListView)
  private
    procedure WMNotify(var AMessage: TWMNotify); message WM_NOTIFY; // used for other purposes
    procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
    procedure WMVScroll(var Msg: TWMHScroll); message WM_VSCROLL;
    procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
  protected
    procedure CreateWnd; override;  
  end;

implementation

procedure TListView.CNCommand(var Message: TWMCommand);
begin
   case Message.NotifyCode of
    EN_VSCROLL: CodeSite.Send('TListView.CNCommand: EN_VSCROLL'); // does not work
    EN_HSCROLL: CodeSite.Send('TListView.CNCommand: EN_HSCROLL'); // does not work
   end;
   inherited ;
end;

procedure TListView.WMHScroll(var Msg: TWMHScroll);
begin
   CodeSite.Send('TListView.WMHScroll: WM_HSCROLL'); // does work
   inherited;
end;

procedure TListView.WMVScroll(var Msg: TWMHScroll);
begin
   CodeSite.Send('TListView.WMVScroll: WM_VSCROLL'); // does work
   inherited;
end;

但是,只有在滚动时,我才会不断收到 WM_HSCROLLWM_VSCROLL 的通知,生成大量消息。

但我只需要在水平滚动结束时得到通知!这可能吗?

给Q的评论很中肯。

首先,正如 Remy Lebeau 所说,WM_HSCROLL 消息会告诉您操作是否完成:

procedure TListView.WMHScroll(var Msg: TWMHScroll);
begin
  inherited;
  if Msg.ScrollCode = SB_ENDSCROLL then
    ShowMessage('End scroll')
end;

但是,这让您知道水平滚动条发起的滚动操作何时完成。目前,这包括这些 scroll-bar 操作:

  • 拇指松开
  • 单击滚动条向左或向右按​​钮
  • 点击滚动条空白区域(页面滚动)
  • 已选择滚动条上下文菜单项

但列表视图控件还有很多其他方式可以滚动,与滚动条无关:

  • 使用鼠标的水平滚轮(如果只有水平滚动条而没有垂直滚动条,则使用标准垂直滚轮)
  • 使用键盘的左右箭头键(或 Ctrl+Left/Right 滚动页面)
  • 使用 MultiSelect = True,用鼠标制作一个选择矩形(开始拖动到任何 list-view 项目之外)

因此,仅对 WM_HSCROLL 作出反应,您将不会检测到这些滚动事件。几乎可以肯定的是,无论 如何 发生变化,您都希望在滚动位置发生变化时做出反应。

而且,正如 AmigoJack 所写,“结束”的含义并不完全清楚(除非您在拖动滚动条拇指后松开鼠标按钮)。例如,如果您使用鼠标滚轮滚动,结果是单个大滚动操作还是几个小滚动操作?毕竟,在任何情况下,即使是拇指跟踪,控件也会在每一个小步骤重新绘制自己。

所以最好的选择可能是使用

procedure TListView.CNNotify(var Message: TMessage);
begin
  inherited;
  if PNMHDR(Message.lParam).code = LVN_ENDSCROLL then
    // Scrolled
end;

根据the documentation,

Notifies a list-view control's parent window when a scrolling operation ends.

请注意,文档说通知是在操作 结束 时发送的。尽管如此,当您拖动滚动条滑块时,您会发现每次小更新都会发送它。如上所述,这是合理的:滚动 确实在每一个这样的小步骤之后执行。