La barre de défilement continue à faire défiler après que vous relâchez le bouton de la souris


Symptômes


La barre de défilement continue de défiler même après le bouton gauche de la souris. Ce type de barre de défilement n’est pas approprié à ce problème, c’est-à-dire que le même problème se produit, que la barre de défilement fasse partie de la fenêtre ou qu’il s’agisse d’un contrôle de barre de défilement.

Cause


Ce problème se produit généralement lors de l’exécution d’une boucle d’extraction de messages suite à des actions effectuées pour le défilement lors de la réception d’un message de notification de barre de défilement. Lorsque vous faites défiler une boucle, une boucle de récupération des messages internes est démarrée dans Windows. La tâche de cette boucle de messages consiste à effectuer le suivi du défilement et à envoyer les messages de notification de barre de défilement appropriés, WM_HSCROLL et WM_VSCROLL. Le défilement est arrêté une fois WM_LBUTTONUP reçu. Si une autre boucle de messages est démarrée lors du défilement, le WM_LBUTTONUP est récupéré par cette boucle de messages et, dans la mesure où une application n’a pas accès à la boucle de récupération des messages internes de la barre de défilement, WM_LBUTTONUP ne peut pas être distribuée correctement. Par conséquent, le WM_LBUTTONUP n’est jamais reçu par le récupérateur de messages interne et le défilement n’est jamais terminé. L’application qui défile ne doit pas récupérer de messages de façon explicite pour causer ce problème. L’appel de l’une des fonctions suivantes ou le traitement de n’importe quel message ayant une boucle de récupération des messages, en même temps que le défilement, peuvent entraîner la perte de l’WM_LBUTTONUP. Les fonctions répertoriées ci-dessous appartiennent à cette catégorie :
DialogBox() DialogBoxIndirect() DialogBoxIndirectParam() DialogBoxParam() GetMessage() MessageBox() PeekMessage()

Résolution


Lorsque vous faites défiler le document, le message WM_LBUTTONUP ne doit pas être récupéré à partir de la file d’attente par n’importe quelle boucle d’extraction de messages autre que la barre de défilement interne. Une application risque de rencontrer ce problème en procédant comme suit :
  • Une application implémente une boucle de récupération des messages pour implémenter le traitement en arrière-plan, par exemple, le traitement en arrière-plan lors de l’exécution d’une opération de dessin longue.
  • Une application implémente une boucle de récupération des messages pour implémenter une communication avec une autre application ou DLL. Par exemple, pour faire défiler les données, l’application doit recevoir des données d’un autre emplacement.

Solutions de contournement possibles

Deux solutions de contournement possibles sont décrites ci-dessous. La première solution de contournement est utilisée par de nombreuses applications de sortie et par Windows. Néanmoins, dans de rares cas, il est possible que la première solution de contournement ne soit pas faisable. Dans le cas présent, la deuxième solution de contournement est utilisée. Toutefois, dans la mesure du possible, n’hésitez pas à implémenter l’extraction du message entièrement lors du défilement.
  • Utiliser le traitement en fonction du message du minuteur. Fractionnez le traitement compliqué en tâches plus petites et suivez l’emplacement de début et de fin de chaque tâche, puis effectuez chaque tâche sur la base d’un message de minuteur. Lorsque tous les composants du traitement sont terminés, arrêtez le minuteur. Vous trouverez ci-dessous un exemple de solution de contournement.
  • Implémentez une boucle de récupération des messages, en vous assurant qu’WM_LBUTTONUP n’est pas récupérée par celle-ci. Pour ce faire, vous pouvez utiliser des filtres. Vous trouverez ci-dessous des exemples de cette solution de contournement.

Exemple de solution de contournement 1

Une procédure de peinture est complexe pour une application. Appel de ScrollWindow (), pour faire défiler et générer des messages Paint. Le traitement en arrière-plan intervient lors de la peinture.
  1. Lorsque vous recevez le message WM_PAINT procédez comme suit :
    1. Appelez BeginPaint ().
    2. Copiez le rectangle invalidée dans une variable Rect globale (par exemple, grcPaint) à utiliser à l’étape 2. Le contrôle grcPaint global serait une Union de l’objet Rect (grcPaint) précédemment obtenu et du nouveau rectangle invalidée (PS. rcPaint). Le code pour cela ressemble à ce qui suit :
               RECT grcPaint;    // Should be initialized before getting the                           // first paint message.            :            :         UnionRect(&grcPaint, &ps.rcPaint,&grcPaint);
    3. Appelez ValidateRect () avec PS. rcPaint.
    4. Appeler EndPaint ().
    5. Définissez une minuterie.
    De cette façon, il n’y a plus de WM_PAINT messages générés, car il n’y a pas de régions non valides et un minuteur est configuré pour générer des messages WM_TIMER.
  2. Lors de la réception d’un message de WM_TIMER, vérifiez la variable Rect globale. s’il n’est pas vide, prenez une section et peignez-la. Ensuite, ajustez la variable Rect globale de sorte qu’elle n’inclue plus la zone peinte.
  3. Lorsque la variable globale Rect est vide, arrêtez le minuteur.

Exemple illustrant une solution de contournement 2

Une application doit obtenir des données via DDE ou un autre mécanisme d’une autre application, qui s’affiche alors dans la fenêtre. Pour faire défiler, l’application doit demander et obtenir les données d’une application serveur. Il existe trois filtres différents qui peuvent être utilisés pour configurer une PeekMessage () et obtenir les informations. Les filtres peuvent être définis à l’aide des paramètres uFilterFirst et uFilterLast de PeekMessage (). uFilterFirst spécifie le message de la main dans la plage à vérifier et uFilterLast spécifie le dernier message de la plage à vérifier.
  1. Recherchez et récupérez uniquement le ou les messages associés pour obtenir les données nécessaires.
  2. Recherchez WM_LBUTTONUP sans le supprimer de la file d’attente. s’il se trouve dans la file d’attente, rompez. Dans le cas contraire, récupérez et distribuez tous les messages.
  3. Récupérez tous les messages inférieurs à WM_LBUTTONUP et supérieurs à WM_LBUTTONUP, mais ne récupérez pas WM_LBUTTONUP.

Informations supplémentaires


Procédure pour reproduire le problème

Voici la séquence d’événements aboutissant à la perte du message WM_LBUTTONUP :
  1. Cliquez sur la barre de défilement à l’aide de la souris.
  2. Étape 1 génère un message de WM_NCLBUTTONDOWN.
  3. L’étape 2 entraîne le démarrage d’une boucle de messages interne Windows. Cette boucle de messages recherche les messages liés à la barre de défilement. L’objectif de cette boucle de messages est de générer des messages WM_HSCROLL ou WM_VSCROLL appropriés. La boucle de messages et le défilement s’arrête une fois WM_LBUTTONUP reçu.
  4. Lorsque vous recevez le message WM_HSCROLL ou WM_VSCROLL, l’application se trouve dans une boucle de récupération des messages directement ou appelle des fonctions qui génèrent la récupération de messages.
  5. WM_LBUTTONUP est supprimé de la file d’attente à l’aide de la boucle de messages décrite à l’étape 4. WM_LBUTTONUP est alors distribué.
  6. Comme le résultat de l’étape 5 WM_LBUTTONUP message est transmis ailleurs et la boucle de récupération des messages internes, mentionnée à l’étape 3 ne le reçoit jamais. La boucle de messages de l’étape 3 recherche le WM_LBUTTONUP d’arrêter le défilement. Étant donné qu’il n’est pas reçu, la barre de défilement continue de défiler.