Pasek przewijania kontynuuje przewijanie po zwolnieniu przycisku myszy


Symptomy


Pasek przewijania jest ciągle przewijany nawet po zwolnieniu lewego przycisku myszy. Typ paska przewijania nie ma znaczenia dla tego problemu, co oznacza, że ten sam problem występuje bez względu na to, czy pasek przewijania jest częścią okna, czy jest kontrolką paska przewijania.

Przyczyna


Ten problem występuje zwykle wtedy, gdy pętla pobierania wiadomości jest wykonywana w wyniku wykonania akcji wykonywanych w celu przewijania po otrzymaniu jednego z komunikatów powiadomień paska przewijania. Podczas przewijania w systemie Windows rozpoczyna się wewnętrzna pętla pobierania wiadomości. Zadaniem tej pętli jest śledzenie przewijania i wysyłanie odpowiednich wiadomości z powiadomieniem paska przewijania, WM_HSCROLL i WM_VSCROLL. Przewijanie kończy się po otrzymaniu WM_LBUTTONUP. Jeśli podczas przewijania jest uruchamiana inna pętla wiadomości, WM_LBUTTONUP jest pobierana przez tę pętlę wiadomości, a aplikacja nie ma dostępu do wewnętrznej pętli pobierania wiadomości na pasku przewijania, WM_LBUTTONUP nie może być poprawnie wysyłana. Dlatego WM_LBUTTONUP nie jest już odbierany przez wewnętrzny komunikat Retriever, a przewijanie nie kończy się. Aplikacja, która jest przewijana, nie musi jawnie pobierać wiadomości, aby przyczynić się do tego problemu. Wywołanie dowolnej z następujących funkcji lub wszystkich wiadomości, które mają pętlę pobierania wiadomości, podczas przewijania może spowodować utratę WM_LBUTTONUP. Wymienione poniżej funkcje należą do tej kategorii:
DialogBox() DialogBoxIndirect() DialogBoxIndirectParam() DialogBoxParam() GetMessage() MessageBox() PeekMessage()

Rozwiązanie


Podczas przewijania wiadomość WM_LBUTTONUP nie powinna być pobierana z kolejki za pomocą pętli pobierania wiadomości innej niż wewnętrzna pasek przewijania. W przypadku aplikacji może być dostępny następujący problem:
  • Aplikacja implementuje pętlę pobierania wiadomości w celu zaimplementowania przetwarzania w tle, na przykład przetwarzania w tle podczas wykonywania czasochłonnych farb.
  • Aplikacja implementuje pętlę pobierania wiadomości w celu zaimplementowania komunikacji z inną aplikacją lub biblioteką DLL. Aby na przykład przewijać, aplikacja musi odebrać dane z innego miejsca.

Możliwe obejścia

Poniżej wymieniono dwa możliwe obejścia. Pierwsze obejście jest używane przez wiele aplikacji i systemu Windows. Jednak w rzadkich okolicznościach pierwsze obejście może być niemożliwe. W takim przypadku można zastosować drugie obejście. Jednak jeśli to możliwe, spróbuj całkowicie uniknąć wdrażania pobierania wiadomości podczas przewijania.
  • Korzystanie z przetwarzania opartego na wiadomościach czasomierza. Podziel skomplikowane przetwarzanie na mniejsze zadania i śledź miejsca, w których rozpoczynają się i kończy każde zadanie, a następnie wykonuj każde zadanie na podstawie komunikatu czasomierza. Po zakończeniu wszystkich składników przetwarzania należy Wykasuj czasomierz. Zobacz poniżej, aby zapoznać się z przykładem tego obejścia.
  • Zaimplementuj pętlę pobierania wiadomości, ale upewnij się, że WM_LBUTTONUP nie jest pobrana przez nią. Można to zrobić za pomocą filtrów. Poniżej znajdziesz kilka przykładów tego obejścia.

Przykład ilustrujący obejście 1

Aplikacja zawiera kompleksową procedurę malowania. ScrollWindow (), aby przewijać, generowane są wiadomości programu Paint. Przetwarzanie w tle ma miejsce podczas malowania.
  1. Gdy zostanie wyświetlony komunikat WM_PAINT, wykonaj następujące czynności:
    1. Call BeginPaint ().
    2. Skopiuj unieważniony prostokąt do zmiennej globalnej Rect (na przykład grcPaint), która ma być używana w kroku 2. Globalnym związkiem grcPaint będzie Unia już uzyskanego prostokąta (grcPaint) i Nowa unieważniony prostokąt (PS. rcPaint). Kod ten będzie podobny do następującego:
               RECT grcPaint;    // Should be initialized before getting the                           // first paint message.            :            :         UnionRect(&grcPaint, &ps.rcPaint,&grcPaint);
    3. Zadzwoń do ValidateRect () za pomocą PS. rcPaint.
    4. Call EndPaint ().
    5. Ustawianie czasomierza.
    W ten sposób nie zostaną wygenerowane żadne kolejne wiadomości WM_PAINT, ponieważ nie ma żadnych nieprawidłowych regionów i skonfigurowano czasomierz, który będzie generował wiadomości WM_TIMER.
  2. Po otrzymaniu wiadomości WM_TIMER Sprawdź, czy jest to zmienna Global prostokątna. Jeśli nie jest pusty, zrób sekcję i pomaluj ją. Następnie dostosuj zmienną prostokąta globalnego, tak aby nie zawierała już regionu malowanego.
  3. Gdy zmienna Global Rect jest pusta, należy skasować czasomierz.

Przykład ilustrujący obejście 2

Aplikacja musi uzyskać część danych za pośrednictwem DDE lub innego mechanizmu z innej aplikacji, który jest następnie wyświetlany w oknie. Aby można było przewijać, aplikacja musi się zażądać, a następnie uzyskać dane z aplikacji serwera. Istnieją trzy różne filtry, których można użyć w celu skonfigurowania PeekMessage () i uzyskania informacji. Filtry można konfigurować przy użyciu parametrów uFilterFirst i uFilterLast programu PeekMessage (). uFilterFirst określa komunikat Żółwik w zakresie, który ma zostać sprawdzony, a uFilterLast Określa ostatnią wiadomość w zakresie, który ma zostać sprawdzony.
  1. Sprawdź i Pobierz tylko wiadomości pokrewne, aby uzyskać potrzebne dane.
  2. Sprawdzanie WM_LBUTTONUP bez usuwania go z kolejki; Jeśli jest w kolejce, Przerwij. W przeciwnym razie Pobierz i Wyślij wszystkie wiadomości.
  3. Pobierz wszystkie wiadomości mniejsze niż WM_LBUTTONUP i większe niż WM_LBUTTONUP, ale nie pobieraj WM_LBUTTONUP.

Więcej informacji


Kroki prowadzące do odtworzenia problemu

Poniżej przedstawiono sekwencję zdarzeń prowadzących do utraty WM_LBUTTONUP wiadomości:
  1. Kliknij pasek przewijania za pomocą myszy.
  2. Krok 1 generuje wiadomość WM_NCLBUTTONDOWNą.
  3. Krok 2 powoduje uruchomienie wewnętrznej pętli programu Windows. Ta pętla wiadomości wyszukuje wiadomości związane z paskiem przewijania. Celem tej pętli jest wygenerowanie odpowiednich WM_HSCROLL lub WM_VSCROLLych wiadomości. Pętla i przewijanie wiadomości są przerywane po otrzymaniu WM_LBUTTONUP.
  4. Gdy otrzymasz wiadomość WM_HSCROLL lub WM_VSCROLL, aplikacja przeprowadzi do pętli pobierania wiadomości bezpośrednio lub wywołuje funkcje, które powodują pobieranie wiadomości.
  5. WM_LBUTTONUP jest usuwany z kolejki przez pętlę wiadomości wymienioną w kroku 4. Następnie WM_LBUTTONUP jest wysyłana.
  6. W wyniku kroku 5 WM_LBUTTONUP wiadomość jest wysyłana w innym miejscu, a pętla pobierania wiadomości wewnętrznych, wspomniana w kroku 3, nigdy nie otrzymuje. Pętla wiadomości w kroku 3 szuka WM_LBUTTONUP, aby zatrzymać przewijanie. Pasek przewijania będzie kontynuował przewijanie, ponieważ nie zostanie odebrany.