WIN32:如何告诉所有者绘制静态控件刷新自己?

时间:2011-06-24 17:46:22

标签: c++ winapi controls drawing ownerdrawn

我有一个WIN32所有者绘制的静态控件,它使用两个源图像(填充和未填充)绘制一个进度条。在最初的抽奖中效果很好:

case WM_DRAWITEM:
    {
        DRAWITEMSTRUCT* draw = (DRAWITEMSTRUCT*)lparam;
        // Manually draw the progress bar.
        if( draw->hwndItem == hwndProgress )
        {
            // Progress bar is 526 pixels wide.
            int left = progressPercent * 526 / 100;
            // Paint sections of window with filled and unfilled bitmaps
            // based on progress bar position.
            HDC hdcMem = ::CreateCompatibleDC(draw->hDC);
            ::SelectObject(hdcMem, hBmpProgressFull);
            ::BitBlt(draw->hDC, 0, 0, left, 36, hdcMem, 0, 0, SRCCOPY);
            ::DeleteDC(hdcMem);
            HDC hdcMem2 = ::CreateCompatibleDC(draw->hDC);
            ::SelectObject(hdcMem2, hBmpProgressEmpty);
            ::BitBlt(draw->hDC, left, 0, 526-left, 36, hdcMem2, left, 0, SRCCOPY);
            ::DeleteDC(hdcMem2);
            return TRUE;
        }
    }
    return 0;

然而,我似乎无法正确地擦除和重绘。我已经尝试过使用WM_PAINT和RedrawWindow的SendMessage,而且没有一个人工作得很好:

bool SetLoginProgressBar(float value)
{
    if( hwndProgress != NULL )
    {
        progressPercent = (int)(value * 100.0);
        //::RedrawWindow(hwndProgress, NULL, NULL, RDW_INVALIDATE|RDW_INTERNALPAINT);
        ::SendMessage(hwndProgress, WM_PAINT, NULL, NULL);
    }
    return true;
}

不是使用新值重新绘制窗口,而是使用最初绘制的图像坐在那里并忽略其他绘图命令。它正确地为初始值绘制进度,无论是0%,50%等,我可以验证我的WM_DRAWITEM消息处理程序代码是否被调用。

那么,在WIN32中告诉此控件擦除和重绘的正确方法是什么?

我是否有可能需要做一些像BeginPaint / EndPaint这样的事情,或者删除我已经传递过的DRAWITEMSTRUCT中的hDC?

2 个答案:

答案 0 :(得分:6)

在销毁DC之前,您没有从内存DC中取消选择位图。也许位图处于Windows不允许再次选择它们的状态,因此BitBlts失败。

P.S。 RedrawWindow就是我在这种情况下使用的。 InvalidateRect也可以,但仅当您的消息循环正在运行时。这导致了另一个观察:如果您正处于长时间运行的操作中,您可能无法返回到消息循环,并且您的应用程序似乎已挂起,包括对进度窗口的更新。

答案 1 :(得分:3)

InvalidateRect()是您需要致电的功能。

您永远不会发送或发布WM_PAINT条消息 - 窗口管理器会在需要时为您执行此操作(例如,窗口拖动窗口)。如果重绘是由于窗口管理器不知道的更改,则通过调用InvalidateRect()强制重新绘制周期。通过NULL lpRect,整个客户区将被重新绘制。为TRUE传递bErase以强制在重绘周期开始时删除背景。

当您致电InvalidateRect()时,会发生一条WM_PAINT消息放入您的消息队列并返回InvalidateRect()函数调用。当您下次清除消息队列时,您将处理WM_PAINT消息。

我建议您获取Petzold编程Windows手册的副本并阅读所有相关内容。