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

发布于 2024-11-16 19:58:53 字数 1641 浏览 4 评论 0原文

我有一个 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 来发送消息,但没有一个能完全正常工作:

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?

I have a WIN32 owner-drawn static control that draws a progress bar using two source images (filled and unfilled). Works great on the initial draw:

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;

However, I can’t seem to get the thing to erase and repaint properly. I’ve tried SendMessage with WM_PAINT and RedrawWindow and neither one has worked quite right:

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;
}

Instead of redrawing the window with the new values, it just sits there with the initially drawn image and ignores further drawing commands. It draws the progress correctly for the initial value, whether it's 0%, 50%, etc, and I can verify that my WM_DRAWITEM message handler code is being called.

So, what is the correct way to tell this control to erase and redraw in WIN32?

Is is possible that I need to do something like BeginPaint/EndPaint, or delete the hDC in the DRAWITEMSTRUCT that I've been passed?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

水染的天色ゝ 2024-11-23 19:58:53

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

PS RedrawWindow 就是我在这种情况下使用的。 InvalidateRect 也可以工作,但前提是您的消息循环正在运行。这导致了另一个观察结果:如果您处于长时间运行的操作中,您可能无法返回消息循环,并且您的应用程序将显示为挂起,包括进度窗口的更新。

You aren't deselecting the bitmaps from the memory DC before you destroy the DC. Perhaps the bitmaps are in a state where Windows won't allow you to select them again, so the BitBlts are failing.

P.S. RedrawWindow is what I use in this situation. InvalidateRect works too, but only if your message loop is running. Which leads to another observation: if you're in the middle of a long running operation, you may not get back to the message loop and your app will appear to be hung, including updates to the progress window.

淡墨 2024-11-23 19:58:53

InvalidateRect() 是您需要调用的函数。

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

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

我建议您获取一本 Petzold 的《Windows 编程》一书并阅读所有相关内容。

InvalidateRect() is the function you need to call.

You never send or post WM_PAINT messages—the Window manager does that for you when they are needed (e.g. windows dragged over your window). If the repaint is due to changes that the Window manager does not know about then you force a repaint cycle by calling InvalidateRect(). Pass NULL for lpRect and the entire client area will be repainted. Pass TRUE for bErase to force the background to be erased when the repaint cycle begins.

When you call InvalidateRect() what happens is that a WM_PAINT message is placed in your message queue and the InvalidateRect() function call returns. When you next clear your message queue you the process the WM_PAINT message.

I suggest that you get hold of a copy of Petzold's Programming Windows book and read all about it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文