Mirror

How to detect if a TMemo has been scrolled (Views: 101)


Problem/Question/Abstract:

Is there a way of finding out - during or immediately after the fact - that a control has been scrolled? For instance, say someone moves the scroll thumb in a TMemo. Is there a message I can intercept that says this occurred? I don't really need to know which direction or by how much, just that it happened.

Answer:

Here's unit that shows one way to do this without having to create a descendent. It will let you know about "direct" scrolls, but not about scrolls that happen because of cursor movement through the memo's text, etc. It will also report a scroll if the user merely clicked on the thumb track without moving it (without actually scrolling).

To see the code in action, you will need to fill a TMemo with enough text so that it is necessary to scroll.

unit ScrollCatchU;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    ClearButton: TButton;
    ScrolledLabel: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure ClearButtonClick(Sender: TObject);
  private
    Counter: cardinal;
    procedure Memo1ScrollCatcher(var Message: TMessage);
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

type
  TMyControl = class(TControl);

procedure TForm1.Memo1ScrollCatcher(var Message: TMessage);
begin
  Inc(Counter); {for testing/ diagnostics only ...}
  {Add commented out section below, if you only want the "last" scroll
         message in a sequence}
  if ((Message.Msg = WM_VSCROLL) or (Message.Msg = WM_HSCROLL))
    {  and (TWMVScroll(Message).ScrollCode = SB_ENDSCROLL)   }then
    ScrolledLabel.caption := 'Scrolled';
  {  caption := format('Message: %d   ScrollCode: %d  Counter: %d', [Message.Msg,
                        TWMVScroll(Message).ScrollCode, Counter]);  }
  TMyControl(Memo1).WndProc(Message);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.WindowProc := Memo1ScrollCatcher;
  Counter := 0;
  ClearButtonClick(nil);
end;

procedure TForm1.ClearButtonClick(Sender: TObject);
begin
  ScrolledLabel.Caption := '';
  {Using a manual "reset" for the "Scrolled" display}
end;

end.

This technique can be used with any TControl descendent.

You might want to add the Windows Mouse Wheel message to the ones that routine is looking for. Something like:

{ ... }
if ((Message.Msg = WM_VSCROLL) or (Message.Msg = WM_HSCROLL)
  or (Message.Msg = WM_MOUSEWHEEL))
  {and (TWMVScroll(Message).ScrollCode = SB_ENDSCROLL)}then
  ScrolledLabel.caption := 'Scrolled';
{ ... }

Of course, if you are just fishing for the SB_ENDSCROLL ScrollCode with the other messages, you'll have to separate the test for this message out a little further.

<< Back to main page