C++ WIN32:如何让所有者绘制的静态控件刷新自身?

C++ WIN32:如何让所有者绘制的静态控件刷新自身?,c++,winapi,controls,drawing,ownerdrawn,C++,Winapi,Controls,Drawing,Ownerdrawn,我有一个WIN32所有者绘制的静态控件,它使用两个源映像(已填充和未填充)绘制进度条。在初始绘制时效果非常好: case WM_DRAWITEM: { DRAWITEMSTRUCT* draw = (DRAWITEMSTRUCT*)lparam; // Manually draw the progress bar. if( draw->hwndItem == hwndProgress ) { /

我有一个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和重画窗口发送消息,但两种方法都没有完全正确:

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?

是您需要调用的函数

您从不发送或发布
WM_PAINT
消息,窗口管理器会在需要时为您发送或发布这些消息(例如,窗口拖到您的窗口上)。如果重新绘制是由于窗口管理器不知道的更改引起的,则可以通过调用
invalidate()
强制重新绘制循环。通过
lpRect
NULL
,将重新绘制整个客户端区域。将
TRUE
传递给
bErase
以在重新绘制循环开始时强制擦除背景

当您调用
invalidate()
时,发生的情况是将
WM_PAINT
消息放置在消息队列中,并且
invalidate()
函数调用返回。下次清除消息队列时,您将处理
WM_PAINT
消息


我建议您拿一本Petzold的编程Windows手册,并阅读所有相关内容。

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


在这种情况下,我使用的是p.S.重画窗口。invalidate也可以工作,但只有在消息循环正在运行的情况下才能工作。这将导致另一个观察:如果你处于一个长时间运行的操作中,你可能无法返回到消息循环,你的应用程序将被挂起,包括对进度窗口的更新。

你是否尝试用ReCt调用控制区域来调用SimuleAtdioDeCube()?如果消息循环没有运行,那么你就被挂起了,故事结束。@David Heffernan,我对“似乎被挂起”的意思是,这是一种暂时的情况,在某个时候会清除。我自己从来没有使用过重画窗口。我不能完全想象为什么无效安装还不够,但我确信有充分的理由。@David,重画窗口的意思是“现在就做,我会等”,无效安装的意思是“一有机会就做”。没错,它们通常是可互换的。对于我自己绘制的静态控件,我将宏定义为
#define RedrawControl(ctrl)RedrawWindow((ctrl),NULL,NULL,RDW_INVALIDATE)