Well, I learned a little something about
WM_PAINT just now.
In my li'l VU meter toy demo, I had code like this in my
WM_TIMER handler that drove the demo animation:
; Erase previous needle image:
MOV NeedleBMPhandle, NULL
INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
. . . .
; Draw next needle image:
MOV NeedleBMPhandle, <handle from structure>
INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
I threw some logging code into the
WM_PAINT handler of the meter-display proc, and was surprised to find that ... my
NeedleBMPhandle was never getting set to NULL! (I'm using this value as a flag to indicate that an image needs to be erased rather than drawn.)
I couldn't figure out what was going on here--after all, I was explicitly setting that variable to NULL before the
InvalidateRect() call, so why was it never coming up NULL in the
WM_PAINT handler?
Then I thought about it a bit more and remembered that
WM_PAINT is actually a low-priority message in Windows, and that apparently there was enough delay in handling that paint request (the
InvalidateRect()) that by the time it was sent the handle had been reset to non-NULL. And then I ran across this comment in a thread on the topic on Stack Overflow (https://stackoverflow.com/questions/7216161/determine-priority-of-a-window-message):
QuoteThe 'synthesized from the window state' clause is what makes WM_PAINT and WM_TIMER appear to have a low priority. And why moving the mouse rapidly doesn't flood the message queue with mouse messages. That is however not exclusive, you can for example call UpdateWindow() to force a WM_PAINT message to be sent, making it being dispatched with a 'high priority'.
Well, sure enough, when I changed my code to this:
; Erase previous needle image:
MOV NeedleBMPhandle, NULL
INVOKE InvalidateRect, MeterDispHandle, <addr. of rect.>, FALSE
INVOKE UpdateWindow, MeterDispHandle
it worked like a charm.
That's a helpful tip. Thank you :winking:
I am surprised that you need UpdateWindow after InvalidateRect. Have you tried to set bErase to TRUE?
I was surprised too. I'll have to go back and check that.
If you want the client area to be redrawn immediately after a call of InvalidateRect you need to call UpdateWindow. Or you take both calls in one step with RedrawWindow ...
; Erase previous needle image:
MOV NeedleBMPhandle, NULL
INVOKE RedrawWindow, MeterDispHandle, <addr. of rect.>, RDW_ERASE or RDW_INVALIDATE or RDW_UPDATENOW