PRB: Scroll Bar Continues Scrolling After Mouse Button Released (102552)
The information in this article applies to:
- Microsoft Platform Software Development Kit (SDK) 1.0
- Microsoft Windows Software Development Kit (SDK) 3.1
This article was previously published under Q102552 SYMPTOMS
The scroll bar continuously scrolls even after the left mouse button
is released. The type of scroll bar is irrelevant to this problem,
that is, the same problem occurs regardless of whether the scroll bar
is part of the window or is a scroll bar control.
CAUSE
This problem occurs usually when a message retrieval loop is executed
as the result of actions taken for scrolling upon receiving one of the
scroll bar notification messages.
When scrolling, an internal message retrieval loop is started in
Windows. The task of this message loop is to keep track of scrolling
and to send the appropriate scroll bar notification messages,
WM_HSCROLL and WM_VSCROLL. Scrolling is terminated once WM_LBUTTONUP
is received. If another message loop is started during scrolling, the
WM_LBUTTONUP is retrieved by that message loop, and because an
application does not have access to the scroll bar's internal message
retrieval loop, WM_LBUTTONUP cannot be dispatched correctly.
Therefore, the WM_LBUTTONUP is never received by the internal message
retriever, and scrolling is never ended.
The application that is scrolling does not have to retrieve messages
explicitly to cause this problem. Calling any of the following
functions or processing any message that has a message retrieval loop,
while scrolling, can cause the WM_LBUTTONUP to be lost. The functions
listed below fall into this category:
DialogBox()
DialogBoxIndirect()
DialogBoxIndirectParam()
DialogBoxParam()
GetMessage()
MessageBox()
PeekMessage()
RESOLUTION
While Scrolling, the WM_LBUTTONUP message should not be retrieved from
the queue by any message retrieval loop other than the scroll bar's
internal one.
An application may come across this problem as follows:
- An application implements a message retrieval loop to implement
background processing, for example, background processing while
performing a time consuming paint.
- An application implements a message retrieval loop to implement
communication with another application or DLL. For example, in
order to scroll, the application needs to receive data from
elsewhere.
Possible Workarounds
Two possible workarounds are listed below. The first workaround is
used by many exiting applications and by Windows; however, under rare
circumstances the first workaround may not be a feasible one. In this
case, the second workaround may be used. However, if possible, please
try to avoid implementing message retrieval completely while
scrolling.
- Use timer-message-based processing. Break down the complicated
processing into smaller tasks and keep track of where each task
starts and ends, then perform each task based on a timer message.
When all components of the processing are complete, kill the timer.
See below for an example of this workaround.
- Implement a message retrieval loop, but make sure WM_LBUTTONUP is
not retrieved by it. This can be accomplished by using filters. See
below for some examples of this workaround.
Example Demonstrating Workaround 1
An application has a complex paint procedure. Calling ScrollWindow(), to
scroll, generates paint messages. Background processing takes place
while painting.
- When you receive the WM_PAINT message do the following:
- Call BeginPaint().
- Copy the invalidated rect to a global rect variable (for
example, grcPaint) to be used in step 2. The global rect
grcPaint would be a union of the previously obtained rect
(grcPaint) and the new invalidated rect (ps.rcPaint). The code
for this will resemble the following:
RECT grcPaint; // Should be initialized before getting the
// first paint message.
:
:
UnionRect(&grcPaint, &ps.rcPaint,&grcPaint); - Call ValidateRect() with ps.rcPaint.
- Call EndPaint().
- Set a Timer.
This way, no more WM_PAINT messages are generated, because there
are no invalid regions, and a timer is set up, which will generate
WM_TIMER messages.
- Upon receiving a WM_TIMER message, check the global rect variable;
if it is not empty, take a section and paint it. Then adjust the
global rect variable so it no longer includes the painted region.
- Once the global rect variable is empty then kill the timer.
Example Demonstrating Workaround 2
An application needs to obtain some data through DDE or some other
mechanism from another application, which is then displayed in the
window. In order to scroll, the application needs to request and then
obtain the data from a server application.
There are three different filters that can be used to set up a
PeekMessage() and get the information. The filters can be set up by
using the uFilterFirst and uFilterLast parameters of PeekMessage().
uFilterFirst specifies the fist message in the range to be checked and
uFilterLast specifies the last message in the range to be checked. For
more information on PeekMessage() and its parameters, see the Windows
SDK "Programmer's Reference, Volume 2: Functions" for version 3.1 and
"Reference, Volume 1" for version 3.0.
- Check and retrieve only the related message(s) for obtaining the
needed data.
- Check for WM_LBUTTONUP without removing it form the queue; if it is
in the queue, break. Otherwise, retrieve and dispatch all messages.
- Retrieve all messages less than WM_LBUTTONUP and greater than
WM_LBUTTONUP, but do not retrieve WM_LBUTTONUP.
Modification Type: | Minor | Last Reviewed: | 7/11/2005 |
---|
Keywords: | kbCtrl kbprb kbScrollBar KB102552 kbAudDeveloper |
---|
|