January 24th, 2011

There's a default implementation for WM_SETREDRAW, but you might be able to do better

If your window doesn’t have a handler for the WM_SET­REDRAW message, then Def­Window­Proc will give you a default implementation which suppresses WM_PAINT messages for your window when redraw is disabled, and re-enables WM_PAINT (and triggers a full repaint) when redraw is re-enabled. (This is internally accomplished by making the window pseudo-invisible, but that’s an implementation detail you shouldn’t be concerned with.) Although the default implementation works fine for simple controls, more complex controls can do better, and in fact they should do better, because that’s sort of the point of WM_SET­REDRAW. The intended use for disabling redraw on a window is in preparation for making large numbers of changes to the window, where you don’t want to waste time updating the screen after each tiny little change. For example, if you’re going to add a hundred items to a list box, you probably want to disable redraw while adding the items so you don’t have to suffer through 100 screen refreshes when only one is enough. You’ve probably seen the programs that forget to suppress redraw when filling a large list box: The application freezes up except for a list box whose scroll bar starts out with a big thumb that slowly shrinks as the number of items increases. I say that this is sort of the point of WM_SET­REDRAW for a complex control, because if you have a simple control (like a button), there isn’t much in the way of “bulk updates” you can perform on it, so there isn’t much reason for anybody to want to disable redraw on it anyway. The types of windows for which people want to disable redraw are the types of windows that would benefit most from a custom handler. For example, the list view control has a custom handler for WM_SET­REDRAW which sets an internal redraw has been disabled flag. Other parts of the list view control check this flag and bypass complex screen calculations if is set. For example, when you add an item to a list view while redraw is disabled, the list view control doesn’t bother recalculating the new scroll bar position; it just sets an internal flag that says, “When redraw is re-enabled, don’t forget to recalculate the scroll bars.” If the list view is in auto-arrange, it doesn’t bother rearranging the items after each insertion or deletion; it just sets an internal flag to remember to do it when redraw is re-enabled. If you have a regional list view, it doesn’t bother recalculating the region; it just sets a flag. And when you finally re-enable drawing, it sees all the little Post-It note reminders that it left lying around and says, “Okay, let’s deal with all this stuff that I had been putting off.” That way, if you add 100 items, it doesn’t perform 99 useless scroll bar calculations, 99 useless auto-arrange repositionings, and create, compute, and then destroy 99 regions. Since some of these calculations are O(n), deferring them when redraw is disabled improves the performance of inserting n items from O() to O(n). Moral of the story: If you have a control that manages a large number of sub-items, you should have a custom WM_SET­REDRAW handler to make bulk updates more efficient.

Bonus chatter: Note that using Lock­Window­Update as a fake version of WM_SET­REDRAW does not trigger these internal optimizations. Abusing Lock­Window­Update gets you the benefit of not repainting, but you still have to suffer through the various O() calculations.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

0 comments

Discussion are closed.