Sintomas
A barra de rolagem é rolada continuamente até que você solte o botão esquerdo do mouse. O tipo de barra de rolagem é irrelevante para esse problema, isto é, o mesmo problema ocorre independentemente de a barra de rolagem fazer parte da janela ou um controle de barra de rolagem.
Causa
Esse problema ocorre geralmente quando um loop de recuperação de mensagem é executado como resultado de ações executadas para rolagem após receber uma das mensagens de notificação de barra de rolagem. Durante a rolagem, um loop de recuperação de mensagem interna é iniciado no Windows. A tarefa deste loop de mensagem é acompanhar a rolagem e enviar as mensagens de notificação de barra de rolagem apropriadas, WM_HSCROLL e WM_VSCROLL. A rolagem é terminada quando WM_LBUTTONUP é recebido. Se outro loop de mensagem for iniciado durante a rolagem, o WM_LBUTTONUP é recuperado por esse loop de mensagem e, como um aplicativo não tem acesso ao loop de recuperação de mensagens interna da barra de rolagem, WM_LBUTTONUP não pode ser despachado corretamente. Portanto, o WM_LBUTTONUP nunca é recebido pelo recuperador de mensagens interna, e a rolagem nunca terminou. O aplicativo que é rolável não precisa recuperar mensagens explicitamente para causar esse problema. Chamar qualquer uma das funções a seguir ou processar qualquer mensagem que tenha um loop de recuperação de mensagens durante a rolagem pode fazer com que a WM_LBUTTONUP seja perdida. As funções listadas abaixo caem nesta categoria:
DialogBox() DialogBoxIndirect() DialogBoxIndirectParam() DialogBoxParam() GetMessage() MessageBox() PeekMessage()
Resolução
Durante a rolagem, a mensagem WM_LBUTTONUP não deve ser recuperada da fila por qualquer loop de recuperação de mensagens que não seja o interno da barra de rolagem. Um aplicativo pode vir este problema da seguinte maneira:
-
Um aplicativo implementa um loop de recuperação de mensagem para implementar o processamento em segundo plano, por exemplo, processamento em segundo plano durante a execução de uma pintura que consome tempo.
-
Um aplicativo implementa um loop de recuperação de mensagens para implementar a comunicação com outro aplicativo ou DLL. Por exemplo, para rolar, o aplicativo precisa receber dados de outro lugar.
Possíveis soluções alternativas
Duas soluções possíveis estão listadas abaixo. A primeira solução alternativa é usada por muitos aplicativos de saída e pelo Windows; no entanto, em circunstâncias raras, a primeira solução alternativa pode não ser uma viável. Nesse caso, a segunda alternativa pode ser usada. No entanto, se possível, tente evitar a implementação completa da recuperação de mensagens durante a rolagem.
-
Use o processamento baseado em temporizador/mensagem. Divida o processamento complicado em tarefas menores e controle o local de início e término de cada tarefa e execute cada tarefa com base em uma mensagem de temporizador. Quando todos os componentes do processamento estiverem concluídos, desative o temporizador. Veja abaixo um exemplo dessa solução alternativa.
-
Implemente um loop de recuperação de mensagens, mas certifique-se de que WM_LBUTTONUP não seja recuperada por ele. Isso pode ser feito com o uso de filtros. Veja abaixo alguns exemplos dessa solução alternativa.
Exemplo que demonstra a solução alternativa 1
Um aplicativo tem um procedimento complexo de pintura. Chamar ScrollWindow (), para rolar, gera mensagens de pintura. O processamento em segundo plano ocorre durante a pintura.
-
Quando receber a mensagem WM_PAINT faça o seguinte:
-
Chame BeginPaint ().
-
Copie o Rect invalidado para uma variável Rect global (por exemplo, grcPaint) a ser usado na etapa 2. O grcPaint global Rect seria uma União do Rect anteriormente obtido (grcPaint) e o novo Rect invalidado (PS. rcPaint). O código para isso será semelhante ao seguinte:
RECT grcPaint; // Should be initialized before getting the // first paint message. : : UnionRect(&grcPaint, &ps.rcPaint,&grcPaint);
-
Chame ValidateRect () com PS. rcPaint.
-
Chame EndPaint ().
-
Definir um temporizador.
Dessa forma, não são geradas mais mensagens WM_PAINT porque não há regiões inválidas, e um temporizador é configurado, o que gerará WM_TIMER mensagens.
-
-
Ao receber uma mensagem de WM_TIMER, verifique a variável de retângulo global; Se não estiver vazio, faça uma seção e pinte-a. Em seguida, ajuste a variável de retângulo global para que ela não inclua mais a região pintada.
-
Depois que a variável Rect global estiver vazia, desative o temporizador.
Exemplo que demonstra a solução alternativa 2
Um aplicativo precisa obter alguns dados por meio de DDE ou outro mecanismo de outro aplicativo, que será exibido na janela. Para poder rolar, o aplicativo precisa solicitar e obter os dados de um aplicativo de servidor. Há três filtros diferentes que podem ser usados para configurar um PeekMessage () e obter as informações. Os filtros podem ser configurados usando os parâmetros uFilterFirst e uFilterLast de PeekMessage (). uFilterFirst especifica a mensagem punho no intervalo a ser marcada e uFilterLast especifica a última mensagem no intervalo a ser marcada.
-
Verifique e recupere somente as mensagens relacionadas para obter os dados necessários.
-
Verifique se há WM_LBUTTONUP sem removê-lo para a fila; Se estiver na fila, interrompa. Caso contrário, recupere e envie todas as mensagens.
-
Recuperar todas as mensagens com menos de WM_LBUTTONUP e maiores do que WM_LBUTTONUP, mas não recuperar WM_LBUTTONUP.
Informações adicionais
Etapas para reproduzir o problema
Veja a seguir a sequência de eventos que levam à perda da mensagem WM_LBUTTONUP:
-
Clique na barra de rolagem usando o mouse.
-
A etapa 1 gera uma mensagem de WM_NCLBUTTONDOWN.
-
A etapa 2 faz com que um loop de mensagem interna do Windows seja iniciado. Este loop de mensagem procura mensagens relacionadas à barra de rolagem. A finalidade desse loop de mensagem é gerar mensagens WM_HSCROLL ou WM_VSCROLL apropriadas. O loop de mensagem e a rolagem são encerrados uma vez WM_LBUTTONUP são recebidos.
-
Ao receber a mensagem WM_HSCROLL ou WM_VSCROLL, o aplicativo se torna um loop de recuperação de mensagens diretamente ou chama funções que resultam na recuperação de mensagens.
-
WM_LBUTTONUP é removido da fila pelo loop de mensagem mencionado na etapa 4. Em seguida, WM_LBUTTONUP será despachado.
-
Como resultado da etapa 5 WM_LBUTTONUP mensagem é despachada em outro lugar e o loop de recuperação de mensagem interna, mencionado na etapa 3 nunca a recebe. O loop de mensagem na etapa 3 está procurando o WM_LBUTTONUP para interromper a rolagem. Como não é recebido, a barra de rolagem continua rolando.