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.

Comments (15)
  1. Joel says:

    I wish Visual Studio 2008 would use something like this for the toolbox sidebar which takes forever, as it adds each custom control one-by-one to the list while also repeatedly jumping between the topmost, bottommost and currently selected items in the list (for no apparent reason).

  2. 640k says:

    If you think vs2008 is slow, try vs2010.

  3. Joshua says:

    Just don't use the designer and you'll be fine.

    I almost never use it these days. I can do a smaller/faster/better job in code.

  4. Alex Grigoriev says:

    I wish Windows Live (DirectActiveX.NET just kidding) Mail programmers worked in the same company as Raymond Chen. Oops, they do. The damn thing doesn't know to use WM_SETREDRAW when deleting the mails from the list.

  5. @Alex Grigoriev says:

    I wish the commenters would actually complain on the correct blog windowsteamblog.com/…/windowslive. Oops, that would mean they'd have to do something other than complain to someone who has nothing to do with said product. This means I have to read the damn comments that have nothing to do with the post.

  6. Joel says:

    @Alex I'm not really planning to do anything about it.  It's a minor annoyance, honestly, and only an issue the first time I start up VS for the day.  Still…I wish they had used something like WM_SETREDRAW.  Just sayin'.

  7. Brian Frost says:

    I've seen the angst over the 'wrong' use of LockWindowUpdate in favour of WM_SETREDRAW but my approach to reducing screen flicker in my Delphi Apps is rather empirical. In my tools box I have four possibilities – DoubleBuffered (writes the bitmap to a backing canvas before painting), BeginUpdate-EndUpdate (ListView, TreeView etc) LockWindowUpdate() and WM_SETREDRAW. I fiddle with each until I find a solution that looks nice. Invariably LockWindowUpdate looks best although I know its naughty. Why is it best?

    [It's not surprising that naughty gives the best results for the person being naughty. If your network card doesn't implement exponential backoff but instead always retransmits immediately, then you will win all collisions. If you steal from the store, you don't have to pay. If you grab all the crayons in art class, then you will have an easier time drawing your pictures. Of course, you also screw over everybody else. -Raymond]
  8. kinokijuf says:

    @Alex Grigoriev;@Alex Grigoriev: I bet this is leftover from OE. I see exact same behaviour in winmail.

  9. bad api says:

    [It's not surprising that naughty gives the best results for the person being naughty. If your network card doesn't implement exponential backoff but instead always retransmits immediately, then you will win all collisions. If you steal from the store, you don't have to pay. If you grab all the crayons in art class, then you will have an easier time drawing your pictures. Of course, you also screw over everybody else. -Raymond]

    Exponential backoff will most likely make collisions worse. Only random backoff will minimize collisions.

    To draw flicker free in windows gdi you have to STEAL resources?

    [Who said you were stealing resources? I was just giving real-world examples of how bad people get an advantage over people who follow the rules. -Raymond]
  10. Brian Frost says:

    ..and my real point is why should LockWindowUpdate() give a 'better' visual effect than WM_SETREDRAW?

    [I thought I explained it in the article. WM_SETREDRAW lets the control decide how to redraw. Maybe the control does a bad job. But instead of LockWindowUpdate you can just ShowWindow(SW_HIDE), which is a local solution. -Raymond]
  11. nikos says:

    talking about the listview control, its implementation of setredraw is very buggy. If I turn redraw off, then add/rearrange a lot of items, chances are that when redraw is re-enabled the control will not paint fully, not scroll correctly or both. The end result leaves much to be admired (especially in LVS_LIST) so usually I request a full redraw with Invalidate() after each WM_SETREDRAW(true)

  12. GregM says:

    Nikos, how is it buggy when it behaves exactly as documented?

    This message can be useful if an application must add several items to a list box. The application can call this message with wParam set to FALSE, add the items, and then call the message again with wParam set to TRUE. Finally, the application can call RedrawWindow(hWnd, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN) to cause the list box to be repainted.

  13. Gabe says:

    If you're trying to avoid flickering, I don't see how ShowWindow(SW_HIDE) would help. It sounds like you're suggesting that one should hide the control, perform updates, and show it again. That will definitely cause flickering!

    [But it will speed up the update. If you want to get rid of the flickering, you disable redraw on the parent, I guess. Or put up a cover window. -Raymond]
  14. bad api says:

    Nikos, how is it buggy when it behaves exactly as documented?

    The listview content is easily provoked to garbage when enabling gridlines and adding items.

  15. bob asking says:

    Can you please clarify about WM_SETREDRAW message – does it allow nesting? I mean if you do SendMsg(WM_SET­REDRAW, FALSE) twice, then SendMsg(WM_SET­REDRAW, TRUE) twice, will it allow repainting only after second SendMsg(WM_SET­REDRAW, TRUE)?

    Also, from your post it is not very clear to me how should I implement WM_SET­REDRAW. Are there any examples of how it could be done? For example, should I call default implementation? And what other messages should be handled in specific way while setredraw flag is set?

    [WM_SETREDRAW does not nest. I'll consider "How to implement WM_SETREDRAW" a topic suggestion since I'm not going to try to create a scratch program in a comment. -Raymond]

Comments are closed.

Skip to main content