Win32 API:如何避免基本窗口控件的闪烁?

4
我通过在WM_PAINT中根据当前滚动条的nPos值使父窗口无效并重新绘制所有内容来滚动父窗口。为了避免重绘背景,我处理了WM_ERASEBKGND。我还为我的TextOut调用和一些显示的位图做了简单的双缓冲处理。它运行良好,除了我的子控件之外。它们闪烁得很厉害,特别是当nPos == 0时,应用程序处理SB_LINEUP或nPos == nMax并处理SB_LINEDOWN,或者拖动滚动条时。我用MoveWindow()移动它们。我也尝试过DeferWindowPos()。我已经在谷歌上搜索了消除闪烁的解决方案,但它不起作用,或者我没有正确使用它。如何消除子控件的闪烁?
附:当我为主窗口使用WS_CLIPCHILDREN样式时,控件不会闪烁,但在滚动时,我的窗口的某些部分会出现问题,特别是静态控件,我通过处理WM_DRAWITEM来画它上面的一些东西。
编辑:(简化的代码) 我在WM_PAINT中这样双缓冲处理:
case WM_PAINT: {
    hDC = BeginPaint( hwnd, &PS );
    hDCMem = CreateCompatibleDC( hDC );
    hBMMem = CreateCompatibleBitmap( hDC, wnd_x, wnd_y );
    SelectObject( hDCMem, hBMMem );

    Font = CreateFont( 130, 50, 0, 0, FW_SEMIBOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN, "Arial" );
    SelectObject( hDCMem, Font );
    SetTextColor( hDCMem, RGB( 30, 144, 255 ) );
    SetBkColor( hDCMem, RGB( 192, 192, 192 ) );
    TextOut( hDCMem, 650, 69 - scr_pos, "ABC", 3 );

    MoveWindow( GetDlgItem( hwnd, ID_BUTT_START ), 720, 500 - scr_pos, 160, 150, TRUE );

    BitBlt( hDC, 0, 0, wnd_x, wnd_y, hDCMem, 0, 0, SRCCOPY );
    DeleteObject( hBMMem );
    DeleteDC( hDCMem );
    EndPaint( hwnd, &PS );
}

scr_pos 是从滚动条获取的当前 nPos 值。

case WM_ERASEBKGND:
    return 1;
break;

case WM_VSCROLL: {
    SCROLLINFO sinfo;
    ZeroMemory( &sinfo, sizeof( SCROLLINFO ) );
    sinfo.cbSize = sizeof( SCROLLINFO );
    sinfo.fMask = SIF_POS | SIF_PAGE | SIF_TRACKPOS;
    GetScrollInfo( hwnd, SB_VERT, &sinfo );
    scr_pos = sinfo.nPos;
    switch( LOWORD( wParam ) ) {
        case SB_TOP:
            scr_pos = 0;
        break;
        case SB_BOTTOM:
            scr_pos = 4000;
        break;
        case SB_LINEUP: {
            scr_pos -= 200;
            if ( scr_pos < 0 ) {
                scr_pos = 0;
            }
        }
        break;
        case SB_LINEDOWN: {
            scr_pos += 200;
            if ( scr_pos > 4000 ) {
                scr_pos = 4000;
            }
        }
        break;
        case SB_PAGEUP: {
            scr_pos -= si.nPage;
            if( scr_pos < 0 ) {
                scr_pos = 0;
            }
        }
        break;
        case SB_PAGEDOWN: {
            scr_pos += si.nPage;
            if( scr_pos > 4000 ) {
                scr_pos = 4000;
            }
        }
        break;
        case SB_THUMBPOSITION:
            scr_pos = HIWORD(wParam);
        break;
        case SB_THUMBTRACK:
            scr_pos = HIWORD(wParam);
        break;
    }    
    RedrawWindow( hwnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT );
//  InvalidateRect ( hwnd, NULL, true );
//  UpdateWindow( hwnd );
    ZeroMemory( & sinfo, sizeof( SCROLLINFO ) );
    sinfo.cbSize = sizeof( SCROLLINFO );
    sinfo.fMask = SIF_POS;
    snfo.nPos = scr_pos;
    SetScrollInfo( hwnd, SB_VERT, & sinfo, TRUE );
    }
}
break;

除了子控件,没有任何东西会闪烁...


如果您正在使用双缓冲,我无法想象它会如何闪烁。如果您发布实际代码而不是解释您的代码,那么诊断将变得更加容易。 - Cody Gray
你确定你没有在某个地方做不必要的无效操作吗? Cody Gray是正确的,请发布代码。 - Flot2011
代码已添加。如果我漏掉了一些重要的部分,请告诉我。 - okt
1个回答

6

WS_CLIPCHILDREN 确实是解决方案。请启用它,你需要找出“我的窗口的部分内容被弄乱”的原因,但你没有提供足够的详细信息。

你还可以研究一下 ScrollWindowEx。该文档明确描述了如何在滚动期间正确重新定位子窗口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接