La barra de desplazamiento continúa desplazándose después de soltar el botón del mouse


Síntomas


La barra de desplazamiento se desplaza continuamente incluso después de soltar el botón primario del mouse. El tipo de barra de desplazamiento es irrelevante para este problema, es decir, el mismo problema se produce independientemente de si la barra de desplazamiento forma parte de la ventana o es un control de barra de desplazamiento.

Causa


Este problema se produce normalmente cuando un bucle de recuperación de mensajes se ejecuta como resultado de acciones tomadas para desplazarse al recibir uno de los mensajes de notificación de la barra de desplazamiento. Al desplazarse, se inicia un bucle de recuperación de mensajes interno en Windows. La tarea de este bucle de mensajes es realizar un seguimiento del desplazamiento y enviar los mensajes de notificación de la barra de desplazamiento correspondiente, WM_HSCROLL y WM_VSCROLL. El desplazamiento se finaliza una vez que se recibe WM_LBUTTONUP. Si se inicia otro bucle de mensajes durante el desplazamiento, el bucle de mensajes recupera el WM_LBUTTONUP y, dado que una aplicación no tiene acceso al bucle de recuperación de mensajes interno de la barra de desplazamiento, WM_LBUTTONUP no se puede distribuir correctamente. Por lo tanto, el recuperador de mensajes interno no recibirá nunca el WM_LBUTTONUP, y nunca finalizará el desplazamiento. La aplicación que se desplaza no tiene que recuperar los mensajes de forma explícita para causar este problema. Llamar a cualquiera de las siguientes funciones o procesar cualquier mensaje que tenga un bucle de recuperación de mensajes, mientras se desplaza, puede hacer que se pierda el WM_LBUTTONUP. Las siguientes funciones se incluyen en esta categoría:
DialogBox() DialogBoxIndirect() DialogBoxIndirectParam() DialogBoxParam() GetMessage() MessageBox() PeekMessage()

Resolución


Mientras se desplaza, el mensaje de WM_LBUTTONUP no debe recuperarse de la cola por ningún bucle de recuperación de mensajes que no sea el interno de la barra de desplazamiento. Una aplicación puede llegar a solucionar este problema de la siguiente manera:
  • Una aplicación implementa un bucle de recuperación de mensajes para implementar el procesamiento en segundo plano, por ejemplo, el procesamiento en segundo plano mientras se realiza una pintura lenta.
  • Una aplicación implementa un bucle de recuperación de mensajes para implementar la comunicación con otra aplicación o DLL. Por ejemplo, para desplazarse, la aplicación necesita recibir datos desde otro lugar.

Posibles soluciones alternativas

A continuación se enumeran dos soluciones alternativas posibles. La primera solución es utilizada por muchas aplicaciones de salida y por Windows. sin embargo, en raras circunstancias, es posible que la primera solución no sea viable. En este caso, se puede usar la segunda solución alternativa. Sin embargo, si es posible, intente evitar implementar la recuperación de mensajes por completo mientras se desplaza.
  • Use el procesamiento basado en mensajes del temporizador. Divida el procesamiento complicado en tareas más pequeñas y realice un seguimiento de dónde se inicia y termina cada tarea y, a continuación, realice cada tarea basándose en un mensaje del temporizador. Cuando se completen todos los componentes del procesamiento, elimine el temporizador. Vea a continuación un ejemplo de esta solución alternativa.
  • Implemente un bucle de recuperación de mensajes, pero asegúrese de que no se recupera WM_LBUTTONUP. Esto se puede llevar a cabo mediante el uso de filtros. Consulte a continuación para ver algunos ejemplos de esta solución alternativa.

Ejemplo de demostración de solución 1

Una aplicación tiene un procedimiento de pintura complejo. Llamar a ScrollWindow (), para desplazarse, genera mensajes de dibujo. El procesamiento en segundo plano tiene lugar mientras se pinta.
  1. Cuando reciba el mensaje de WM_PAINT, haga lo siguiente:
    1. Llamar a BeginPaint ().
    2. Copie el Rect invalidado en una variable Rect global (por ejemplo, grcPaint) para usarla en el paso 2. La grcPaint de global Rect sería una Unión de la anterior Rect (grcPaint) y la nueva no validada Rect (PS. rcPaint). El código para esto tendrá un aspecto similar al siguiente:
               RECT grcPaint;    // Should be initialized before getting the                           // first paint message.            :            :         UnionRect(&grcPaint, &ps.rcPaint,&grcPaint);
    3. Llama a ValidateRect () con PS. rcPaint.
    4. Llama a EndPaint ().
    5. Establezca un temporizador.
    De esta manera, no se generarán más mensajes de WM_PAINT porque no hay regiones no válidas y se ha configurado un temporizador que generará mensajes de WM_TIMER.
  2. Al recibir un mensaje de WM_TIMER, Compruebe la variable Rect global; Si no está vacío, toma una sección y la pinta. Después, ajuste la variable Rect global para que ya no incluya la región pintada.
  3. Una vez que la variable Rect global está vacía, elimina el temporizador.

Ejemplo de demostración de solución 2

Una aplicación necesita obtener algunos datos a través de un DDE u otro mecanismo de otra aplicación, que se muestra en la ventana. Para desplazarse, la aplicación debe solicitar y, a continuación, obtener los datos de una aplicación de servidor. Hay tres filtros diferentes que se pueden usar para configurar un () de PeekMessage () y obtener la información. Los filtros se pueden configurar mediante los parámetros uFilterFirst y uFilterLast de PeekMessage (). uFilterFirst especifica el último mensaje del rango que se va a activar y uFilterLast especifica el último mensaje del rango que se va a comprobar.
  1. Compruebe y recupere solo los mensajes relacionados para obtener los datos necesarios.
  2. Comprobar WM_LBUTTONUP sin quitar el formulario de la cola; Si está en la cola, interrumpa. De lo contrario, recuperar y enviar todos los mensajes.
  3. Recupere todos los mensajes con menos de WM_LBUTTONUP y mayores que WM_LBUTTONUP, pero no recupere WM_LBUTTONUP.

Más información


Pasos para reproducir el problema

La siguiente es la secuencia de eventos que llevan a la pérdida del mensaje WM_LBUTTONUP:
  1. Haga clic en la barra de desplazamiento con el mouse.
  2. El paso 1 genera un mensaje de WM_NCLBUTTONDOWN.
  3. El paso 2 hace que se inicie un bucle de mensajes Windows Internal. Este bucle de mensaje busca mensajes relacionados con la barra de desplazamiento. El propósito de este bucle de mensajes es generar mensajes WM_HSCROLL o WM_VSCROLL apropiados. El bucle de mensajes y el desplazamiento finalizan una vez que se recibe WM_LBUTTONUP.
  4. Al recibir el WM_HSCROLL o el mensaje de WM_VSCROLL, la aplicación se envía directamente a un bucle de recuperación de mensajes o llama a funciones que producen la recuperación de mensajes.
  5. El bucle de mensajes mencionado en el paso 4 quita WM_LBUTTONUP de la cola. A continuación se enviará el WM_LBUTTONUP.
  6. Como resultado del paso 5 WM_LBUTTONUP mensaje se distribuye en otro lugar y el bucle de recuperación de mensajes interno, mencionado en el paso 3, nunca lo recibe. El bucle de mensajes en el paso 3 está buscando el WM_LBUTTONUP para detener el desplazamiento. Como no se recibe, la barra de desplazamiento continúa desplazándose.