如何在按住滚动条的同时强制重绘 WIN32 滚动窗口

问题描述

enter image description here

我们注意到 Windows 应用程序上的 ScrollWindowEx 存在问题。因为有很多子窗口,如果你按住滚动条来回移动,会导致DesktopWindowManager出现巨大的停顿。

这里记录了这一点

Why is my MFC application hanging after interacting with both scroll-bars?

所以,我已经取消了对 ScrollWindowEx 的调用,现在我可以毫无问题地移动我的滚动条,但是当我放开滚动条时,窗口及其所有子项只会被绘制在新位置。

当按住滚动条时,我的整个程序似乎停顿并卡在消息循环中。如果我们可以强制滚动条附加到的窗口更新,这不是问题。但我该怎么做?

我已经试过了

UpdateWindow(pPane);
RedrawWindow(pPane,NULL,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENow);

它确实更新和重绘 - 你可以看到故障 - 但它会在上次留下的位置重绘它。不是滚动条的新位置会设置它的位置。

我打电话

SetScrollInfo(...)

参数正确,否则无法正常工作

如何在按住滚动条的同时使用正确的参数重绘窗口及其子窗口?

这是我的滚动消息处理程序

case WM_VSCROLL:
    {
        int iVScrollPos;
        SCROLLINFO vsi;
        ZeroMemory(&vsi,sizeof(SCROLLINFO));

        vsi.cbSize=sizeof(SCROLLINFO);
        vsi.fMask=SIF_ALL;
        GetScrollInfo(hWnd,SB_VERT,&vsi);
        iVScrollPos=vsi.nPos;

        switch(LOWORD(wParam))
        {
           case SB_LINEUP:
                if(vsi.nPos>vsi.nMin)
                   vsi.nPos=vsi.nPos-10;
                break;
           case SB_PAGEUP:
                vsi.nPos = vsi.nPos - vsi.nPage;
                break;
           case SB_LINEDOWN:
                if(vsi.nPos<vsi.nMax)
                   vsi.nPos=vsi.nPos+10;
                break;
           case SB_PAGEDOWN:
                vsi.nPos = vsi.nPos + vsi.nPage;
                break;
           case SB_THUMBTRACK:
                vsi.nPos=vsi.nTrackPos;
                break;
        }

        vsi.nMin=0;
        vsi.nMax=pOW->dialogysize;          
        vsi.nPage=sy;           

        SetScrollInfo(hWnd,&vsi,TRUE);
        GetScrollInfo(hWnd,&vsi);     
        
        if(vsi.nPos != iVScrollPos)
        {                 
RedrawWindow(hWnd,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENow);
}

当您移动滚动条时,窗口会重新绘制,但在您将鼠标按住滚动条时的位置相同。它仅在您松开鼠标按钮时更新位置。

谢谢

肖恩

编辑 - 我创建了一个重现行为的示例。请注意,我们有一个单独的窗口类来保存多个编辑窗口。我还没有在这个演示中处理 WM_SIZE 和 WM_MOVE 消息,但代码应该可以按原样处理窗口。

如果定义了 USESCROLL 并且您上下移动滚动条,它会导致 DesktopWindowManager 中的锁定

如果 USESCROLL 没有被注释,那么结构中的 trackPos 仍然会更新并且我调用 reDrawWindow,但是当我滚动滚动条时子编辑窗口不会移动。

因为我不想被锁定,请问如何让他们移动?

再次感谢

肖恩

#include <windows.h>
#include <stdio.h>

LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WParaM wParam,LParaM lParam);
LRESULT CALLBACK PaneProc(HWND hwnd,LParaM lParam);

int WINAPI wWinMain(HINSTANCE hInstance,HINSTANCE,PWSTR pCmdLine,int nCmdshow)
{
    // Register the window class.
    
    WNDCLASS wc = { };

    wc.lpfnWndProc   = WindowProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = "Class";

    RegisterClass(&wc);

    wc.lpfnWndProc   = PaneProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = "Pane";

    RegisterClass(&wc);

    // Create the window.

    HWND hwnd = CreateWindowEx(
        0,// Optional window styles.
        (LPCSTR)"Class",// Window class
        (LPCSTR)"Test",// Window text
        WS_OVERLAPPEDWINDOW| WS_BORDER | WS_CAPTION | WS_CLIPSIBLINGS | WS_THICKFRAME | WS_SYSMENU,// Window style
        200,200,400,hInstance,NULL);
    
    if (hwnd == NULL)
    {
        return 0;
    }
      HWND hwndPane = CreateWindowEx(
        0,// Optional window styles.
        (LPCSTR)"Pane",// Window text
        WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL,// Window style
        220,220,hwnd,NULL);
      
    if (hwndPane == NULL)
    {
        return 0;
    }

    for(int i=0;i<200;i++)
    {
        HWND eb = CreateWindowEx(WS_EX_CLIENTEDGE,TEXT("Edit"),ES_AUTOHSCROLL  | WS_CHILD| WS_VISIBLE,16,16+24*i,64,24,hwndPane,(HMENU)i+10000,NULL);
        char tmp[64];
        sprintf(tmp,"%d",i);
        SetwindowText(eb,tmp);
    }

    ShowWindow(hwnd,nCmdshow);
    ShowWindow(hwndPane,nCmdshow);

    // Run the message loop.
    while(1)
    {
        MSG msg = { };
        
        while (PeekMessage(&msg,PM_REMOVE))
        {
            TranslateMessage(&msg);
            dispatchMessage(&msg);
        }
    
        Sleep(10);
    }

    return 0;
}


LRESULT CALLBACK WindowProc(HWND hwnd,LParaM lParam)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd,&ps);

            FillRect(hdc,&ps.rcPaint,(HBrush) (COLOR_WINDOW+1));

            EndPaint(hwnd,&ps);
        }
        return 0;

    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}


LRESULT CALLBACK PaneProc(HWND hwnd,LParaM lParam)
{
    switch (uMsg)
    {

    case WM_CREATE:

        int iVScrollPos;
        SCROLLINFO vsi;
        ZeroMemory(&vsi,sizeof(SCROLLINFO));

        vsi.cbSize=sizeof(SCROLLINFO);
        vsi.fMask=SIF_ALL;      
        GetScrollInfo(hwnd,&vsi);
        iVScrollPos=vsi.nPos;

        vsi.nMin=0;
        vsi.nMax=16+24*200+40;      
        vsi.nPage=400;          

        SetScrollInfo(hwnd,TRUE);
        GetScrollInfo(hwnd,&vsi);     
        break;
            
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_VSCROLL:
    {
           int iVScrollPos;
           SCROLLINFO vsi;
           ZeroMemory(&vsi,sizeof(SCROLLINFO));

           vsi.cbSize=sizeof(SCROLLINFO);
           vsi.fMask=SIF_ALL;       
           GetScrollInfo(hwnd,&vsi);
           iVScrollPos=vsi.nPos;

            switch(LOWORD(wParam))
            {
               case SB_THUMBTRACK:
                    vsi.nPos=vsi.nTrackPos;
                    break;
            }

            vsi.nMin=0;
            vsi.nMax=16+24*200+40;      
            vsi.nPage=400;          

            SetScrollInfo(hwnd,TRUE);
            GetScrollInfo(hwnd,&vsi);     
            
            if(vsi.nPos != iVScrollPos)
            {
                float ScrollAmtY=-(vsi.nPos - iVScrollPos);

#define USESCROLL
#ifdef USESCROLL
                int ok=ScrollWindowEx(hwnd,ScrollAmtY,SW_INVALIDATE|SW_ERASE|SW_SCROLLCHILDREN);
#else
                UpdateWindow(hwnd);
                RedrawWindow(hwnd,RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE | RDW_INTERNALPAINT | RDW_UPDATENow);
#endif
            }
            return 0;
        }
        break;


    }
    return DefWindowProc(hwnd,lParam);
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)