Jump to content
Mike Torrettinni

Prevent WM_VSCROLL on TScrollBox to be executed twice in a row

Recommended Posts

Posted (edited)

I'm looking at this simple example to scroll 2 Scroll boxes at the same time: hhttps://stackoverflow.com/a/17788516/5198394

If I click just 1x on scrollbar button to scroll down on Scrollbox1 , why is message WM_VSCROLL 'executed' twice?

 

If I debug the method:

 

procedure TScrollBox.WMVScroll(var Message: TWMVScroll);
begin
   inherited;
   if Assigned(FOnScrollVert) then  FOnScrollVert(Self);
end;

the Self is Scrollbox1 in both calls:

 

image.thumb.png.ec24ac4d5e7626fd3ce62ec834221529.png

 

Each Scrollbox has 2 oversized panels so that scrollbars are visible from the start:

 

image.thumb.png.bc2b7d46a8c8cec8c08dc2da41522111.png

 

Any ideas why is VMVScroll method called twice? How to prevent it?

 

Shouldn't 1 click on scrollbar button send 1 WM_VSCROLL  or WM_HSCROLL  message?

 

 

Edited by Mike Torrettinni
fixed link

Share this post


Link to post

I can make it not execute MyScrollVert twice, with a check if VertScrollBar.Position has changed after inherited:

 

procedure TScrollBox.WMVScroll(var Message: TWMVScroll);
begin
  fScrollPrevPos := Self.VertScrollBar.Position;
  inherited;
  if fScrollPrevPos <> Self.VertScrollBar.Position then
    if Assigned(FOnScrollVert) then  FOnScrollVert(Self);
end;

but this is just a fix on something I don't know how to prevent in first place.

 

Share this post


Link to post

Yes, I guess this is how scroll messaging works, when pressing on scroll button:

1. Scroll line down (button pressed)

2. End scroll (button released)

 

Which is probably fine for most applications, but I synchronize 3 or 4 scroll boxes at the same time and it was executing custom scroll 2x. So, annoying for anything that is customized, but I guess it is the right way.

 

So, now I have:

 

procedure TScrollBox.WMVScroll(var Message: TWMVScroll);
begin
  inherited;
  if Message.ScrollCode <> SB_ENDSCROLL then
    if Assigned(FOnScrollVert) then  FOnScrollVert(Self);
end;

 

Until better solution comes along 🙂

Share this post


Link to post
8 hours ago, Mike Torrettinni said:

Yes, I guess this is how scroll messaging works, when pressing on scroll button:

1. Scroll line down (button pressed)

2. End scroll (button released)

 

Which is probably fine for most applications, but I synchronize 3 or 4 scroll boxes at the same time and it was executing custom scroll 2x. So, annoying for anything that is customized, but I guess it is the right way.

 

So, now I have:

 


procedure TScrollBox.WMVScroll(var Message: TWMVScroll);
begin
  inherited;
  if Message.ScrollCode <> SB_ENDSCROLL then
    if Assigned(FOnScrollVert) then  FOnScrollVert(Self);
end;

 

Until better solution comes along 🙂

This looks badly wrong to me. What problem are you trying to fix?

Share this post


Link to post

The problem is that method assigned to FOnScrollVert is called 2x. So, the code in the method had to be wrapped with IF's that determine if scroll has occurred - to limit the flicker. The method was originally not designed to be executed 2x for 1 scrolled line.

Share this post


Link to post

There is a reason for the two calls. See msg.ScrollCode and https://docs.microsoft.com/en-us/windows/win32/controls/wm-vscroll

 

Try this.

 

  TScrollBox = class(Vcl.Forms.TScrollBox)
    procedure WMVScroll(var msg: TWMVScroll); message WM_VSCROLL;
  end;

procedure TForm1.ScrollBoxMouseWheel(Sender: TObject; Shift: TShiftState; WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);

  procedure SetPos(ScrollBox: TScrollBox);
  var
    NewPos: Integer;
  begin
    NewPos := ScrollBox.VertScrollBar.Position - WheelDelta div 5; // sensitivity
    NewPos := Max(NewPos, 0);
    NewPos := Min(NewPos, ScrollBox.VertScrollBar.Range);
    ScrollBox.VertScrollBar.Position := NewPos;
  end;


begin
  SetPos(ScrollBox1);
  SetPos(ScrollBox2);
  Handled := True;
end;

var
  InScroll: Boolean;


procedure TScrollBox.WMVScroll(var msg: TWMVScroll);
begin
  inherited;
  if not InScroll then
  begin
    InScroll := True;
    try
      if Self = Form1.ScrollBox1 then
        Form1.ScrollBox2.Dispatch(msg)
      else
        Form1.ScrollBox1.Dispatch(msg);
    finally
      InScroll := False;
    end;
  end;
end;

 

Share this post


Link to post

That was never my intention to suppress standard windows messages.

I answered the question: "I'm looking at this simple example to scroll 2 Scroll boxes at the same time."

Does it scroll 2 scrollboxes the same time? (Vertically, I did not implement the horizontal scroll message)

Share this post


Link to post
Posted (edited)

If you were looking for "How to implement OnScroll events" you can trigger it on SB_ENDSCROLL and not on NOT SB_ENDSCROLL.

But in this case your question was misleading and wasting our time.

Edited by Attila Kovacs

Share this post


Link to post
3 minutes ago, Attila Kovacs said:

If you were looking for "How to implement OnScroll events" you can trigger it on SB_ENDSCROLL and not on NOT SB_ENDSCROLL.

But in this case your question was misleading and wasting our time.

Sorry, thank you trying.

Share this post


Link to post
17 minutes ago, Attila Kovacs said:

you can trigger it on SB_ENDSCROLL and not on NOT SB_ENDSCROLL. 

Yes, I think you missed my final solution where I use it.

Share this post


Link to post

It's quite possible to solve your problem, but what you are proposing is not the solution. Solve your problem by responding to the underlying messages. If you have issues with flicker, they can doubtless be solved. 

Share this post


Link to post

Yes, I solved it by checking if scroll has actually occurred and not assuming that if FOnScrollVert is executed it automatically means the scrollbar has moved. Not a problem, just annoying that OnScroll is execute also when no-scroll 🙂

Share this post


Link to post
Posted (edited)

No I didn't miss it, and if it works for you then fine. Just fyi, if you drag the thumb, there will be messages coming (more than 2) and if you release the mouse button SB_ENDSCROLL fires.

So depending on what you need,

"OnScrolling" (Message.ScrollCode <> SB_ENDSCROLL)

or

"OnScroll" (Message.ScrollCode = SB_ENDSCROLL) you can call your event if assigned.

 

May I ask what do you have in those events?

 

 

Edited by Attila Kovacs

Share this post


Link to post

I have up to 4 scroll boxes with a few controls in each scroll box and a few controls outside scroll boxes that are aligned, and need to reposition on each scroll. So, on each scroll message, a lot of calculations and repainting is done.

Share this post


Link to post

I see. Then you could also check how Align "alCustom" works. It could save you some repainting/flickering.

Share this post


Link to post
6 hours ago, Attila Kovacs said:

I see. Then you could also check how Align "alCustom" works. It could save you some repainting/flickering.

Thank you, tried, but it doesn't improve the flicker, jittering effect of moving controls when scrolling fast. Learned something new today! 🙂

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×