delphitlistviewdelphi-11-alexandria

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


In a VCL Application, I am trying to be notified when I END the horizontal scrolling in a TListView with this interposer class code:

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;

However, only WHILE scrolling I get constantly notified by WM_HSCROLL and WM_VSCROLL generating a lot of messages.

But I need to be notified only at the END of the horizontal scrolling! Is this possible?


Solution

  • The comments given to the Q are very relevant.

    First, as Remy Lebeau stated, the WM_HSCROLL message tells you if the operation is done:

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

    However, this only lets you know when a scrolling operation initiated by the horizontal scroll bar is done. Currently, this includes these scroll-bar operations:

    But there are many other ways the list view control can be scrolled, which have nothing to do with the scroll bar:

    Hence, by reacting only to WM_HSCROLL, you will not detect these scrolling events. Almost certainly, you want to react when the scroll position has changed, no matter how it was changed.

    And, as AmigoJack wrote, it is not absolutely clear what "end" means (other than when you release the mouse button after having dragging the scroll bar thumb). For instance, if you scroll using your mouse wheel, is the result a single large scroll operation or several small ones? After all, in any case, even thumb tracking, the control repaints itself at every small step.

    So probably your best option is to use

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

    According to the documentation,

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

    Notice that the documentation says that the notification is sent when the operation ends. Still, you will find that it is sent for every small update while you drag the scroll bar thumb. As mentioned above, this is reasonable: scrolling has indeed been performed after every such small step.