4

I have a VCL TMemo control and need to be notified every time the text is scrolled. There is no OnScroll event and the scroll messages doesn't seem to propagate up to the parent form.

Any idea of how to get the notification? As a last resort I can place an external TScrollBar and update the TMemo in the OnScroll event, but then I have to keep them in sync when I move the cursor or scroll the mouse wheel in TMemo...

Wolf
  • 9,679
  • 7
  • 62
  • 108
Max Kielland
  • 5,627
  • 9
  • 60
  • 95

2 Answers2

3

You can use a interposer class to handle the WM_VSCROLL and WM_HSCROLL messages and the EN_VSCROLL and EN_HSCROLL notification codes (exposed through the WM_COMMAND message).

Try this sample

type
  TMemo  = class(Vcl.StdCtrls.TMemo)
  private
   procedure CNCommand(var Message: TWMCommand); message CN_COMMAND;
   procedure WMVScroll(var Msg: TWMHScroll); message WM_VSCROLL;
   procedure WMHScroll(var Msg: TWMHScroll); message WM_HSCROLL;
  end;

  TForm16 = class(TForm)
    Memo1: TMemo;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form16: TForm16;

implementation

{$R *.dfm}


{ TMemo }

procedure TMemo.CNCommand(var Message: TWMCommand);
begin
   case Message.NotifyCode of
    EN_VSCROLL : OutputDebugString('EN_VSCROLL');
    EN_HSCROLL : OutputDebugString('EN_HSCROLL');
   end;

   inherited ;
end;

procedure TMemo.WMHScroll(var Msg: TWMHScroll);
begin
   OutputDebugString('WM_HSCROLL') ;
   inherited;
end;

procedure TMemo.WMVScroll(var Msg: TWMHScroll);
begin
   OutputDebugString('WM_HSCROLL') ;
   inherited;
end;
RRUZ
  • 134,889
  • 20
  • 356
  • 483
2

You can subclass the Memo's WindowProc property at runtime to catch all of messages sent to the Memo, eg:

private:
    TWndMethod PrevMemoWndProc;
    void __fastcall MemoWndProc(TMessage &Message);

__fastcall TMyForm::TMyForm(TComponent *Owner)
    : TForm(Owner)
{
    PrevMemoWndProc = Memo1->WindowProc;
    Memo1->WindowProc = MemoWndProc;
}

void __fastcall TMyForm::MemoWndProc(TMessage &Message)
{
    switch (Message.Msg)
    {
        case CN_COMMAND:
        {
            switch (reinterpret_cast<TWMCommand&>(Message).NotifyCode)
            {
                case EN_VSCROLL:
                {
                    //...
                    break;
                }

                case EN_HSCROLL:
                {
                    //...
                    break;
                }
            }

            break;
        }

        case WM_HSCROLL:
        {
            //...
            break;
        }

        case WM_VSCROLL:
        {
            //...
            break;
        }
    }

    PrevMemoWndProc(Message);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I do get in messages but none of them are any SCROLL messages. I get Notifycode 0x0602 (WParamHi) if I press the memo scrollbar's Up and down buttons, no other CN_COMMANDS. – Max Kielland Dec 09 '13 at 19:12
  • @MaxKielland: NotifyCode 0x0602 is `EN_VSCROLL` (`EN_HSCROLL` is 0x0601). – Remy Lebeau Dec 09 '13 at 20:37
  • Thank you! The code was optimized away for some reason. When I did a more proper implementation it worked fine. By using the WM_xSCROLL messages I captured all the scrollbar behaviours,not only the arrow buttons. However, is there a way to also capture when the memo scrolls due to cursor movements? The scrollbar is updated but I don't get any scroll bar messages. I tried to listen for SBM_SETPOS but I guess it is sent directly to the embedded scrollbar. In worst case I guess I have to listen to the Key down events. – Max Kielland Dec 09 '13 at 21:01
  • Good example for using `TWinControl`'s `WindowProc` property. – Wolf Mar 12 '15 at 12:54