在窗口的某个区域绘制一个矩形,导致屏幕闪烁

时间:2014-04-17 10:30:36

标签: c windows winapi

在我的绘画功能中,我通过在屏幕上绘制黑色矩形来清晰屏幕,然后绘制一组水平和垂直线以显示一些图形。在我的应用程序中,x轴非常长。所以我使用水平滚动来允许用户在x轴上查看数据。下面是绘制背景和线条的程序的一部分,如上所述。

case WM_PAINT:
HRGN hrgnUpdate = CreateRectRgn(0, 0, 0, 0);
PAINTSTRUCT ps;
if (GetUpdateRgn(m_hWnd, hrgnUpdate, FALSE) != NULLREGION)
{
    if (BeginPaint(m_hWnd, &ps))
    {
        HBRUSH hBr;
        hBr = CreateSolidBrush(RGB(0,0,0));
        RECT rect;
        // Set rect to window size 
        // Draw black background. This will also work like erasing previous data.
        FillRect( m_hDC, &rect, hBr ) ;

        COLORREF testColor = RGB(0,255,0);
        HPEN hPen = CreatePen(PS_SOLID, 0, testColor);
        HPEN hOldPen = (HPEN) SelectObject(m_hDC, hPen);

        // Draw Numbers of Horizontal and Vertical lines using MoveToEx and LineTo.

    }

    EndPaint(m_hWnd, &ps);
}
break;

看来每次我通过拖动滚动条滚动窗口,屏幕都会闪烁。在我看来,当绘制背景时,它会在绘制线条之前立即显示在窗口上,然后绘制线条并显示在屏幕上。

无论如何,只有在绘制完所有线后才能确保所有绘图更新在屏幕上?或者有没有办法延迟背景绘制的效果,直到绘制完所有的线条?

1 个答案:

答案 0 :(得分:4)

是使用双缓冲:在窗口中添加WS_EX_COMPOSITED窗口样式(除其他外,请参阅MSDN)。它可以通过手工完成,也可以在使用CreateCompatibleBitmap()创建的位图内部绘制所有内容,然后使用BitBlt()将其内容复制到屏幕DC。

此外,您正在为每个绘画事件创建一个新画笔,这在资源和时间方面都是一个非常广泛的任务。只需创建一次,例如在WM_CREATE处理程序中,并在每个WM_PAINT中重复使用它(当然它不是强制性的,现在可能你没有看到任何差异;如果你不&# 39;然后不要忘记致电DeleteObject()释放他们):

case WM_CREATE:
    // Add your existing code for this event
    hBackgroundBrush = CreateSolidBrush(RGB(0, 0, 0));
    hForegroundPen = CreatePen(PS_SOLID, 0, RGB(0, 255, 0));
    break;

您的代码尚未完成,所以我不能说,但您还应该释放已分配的资源(并恢复绘图对象)。

正如大卫在评论中指出的那样,你不应该混合前景和背景画。它现在完成的方式是首先绘制背景(按窗口本身),然后用WM_PAINT消息(FillRect())删除。您有两种方法可以改进:在适当的位置处理背景绘制(然后,如注释中建议的,在WM_ERASEBKGRND事件处理程序内)或避免Windows绘制控件背景并在WM_PAINT内执行所有操作事件处理程序让我们看看两者:

case WM_ERASEBKGND:
{
    GetClientRect(m_hWnd, &rect);
    FillRect((HDC)wParam), &rect, hBackgroundBrush);

    return (LRESULT)1;
}
case WM_PAINT:
{
    HRGN hrgnUpdate = CreateRectRgn(0, 0, 0, 0);
    PAINTSTRUCT ps;

    if (GetUpdateRgn(m_hWnd, hrgnUpdate, FALSE) != NULLREGION) {
        if (BeginPaint(m_hWnd, &ps)) {
            HPEN hOldPen = (HPEN)SelectObject(m_hDC, hForegroundPen);
            // Do painting here
            SelectObject(m_hDC, hOldPen);

            EndPaint(m_hWnd, &ps);
        }
    }
    break;
}

第二种情况在某种程度上更简单,只是对WM_ERASEBKGND不执行任何操作(但通知Windows您处理了这种情况)并按原样保留WM_PAINT

case WM_ERASEBKGND:
    return (LRESULT)1;
case WM_PAINT:
{
    HRGN hrgnUpdate = CreateRectRgn(0, 0, 0, 0);
    PAINTSTRUCT ps;

    if (GetUpdateRgn(m_hWnd, hrgnUpdate, FALSE) != NULLREGION) {
        if (BeginPaint(m_hWnd, &ps)) {
            FillRect(m_hDC, &rect, hBackgroundBrush);

            HPEN hOldPen = (HPEN)SelectObject(m_hDC, hForegroundPen);
            // Do painting here
            SelectObject(m_hDC, hOldPen);

            EndPaint(m_hWnd, &ps);
        }
    }
    break;
}