Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#11648 closed defect (invalid)

wxTimer fails on wxMSW if EVT_PAINT handler (wxPaintEventHandler) does not create wxPaintDC

Reported by: m0nstr42 Owned by:
Priority: normal Milestone:
Component: base Version: 2.8.10
Keywords: wxTimer EVT_PAINT wxPaintEventHandler wxPaintEvent wxPaintDC wxMSW Cc:
Blocked By: Blocking:
Patch: no

Description

Using wxMSW, if an application uses a timer and has an EVT_PAINT event, the event handler function MUST create a wxPaintDC (e.g. include the line wxPaintDC dc(this);). If this is omitted, the timer ceases to trigger events.

Specific Situation: My application uses a wxTimer-derived class to trigger regular events. One of those events is to Refresh a wxGLCanvas-derived animation window. The wxGLCanvas has it's EVT_PAINT connected to a member function called OnPaint. If the line "wxPaintDC" is not in that function, the timer ceases to operate at all.

What I would expect: Without knowing the gory details of windows programming, I'd expect omission of that line to cause a failure to draw whatever it is I want to render. I'd expect the timer to continue to run, thus running all of the other timed events driven by it. In the limit, I'd expect that if my OnPaint function was empty, my program would continue to run normally except that the drawing would not happen.

What happens: The timer stops working completely. This happens in wxMSW, but not wxMAC.

I don't want to attach code because the project is huge. You can, however, test it fairly easily by taking any of the animation rendering tutorials that use wxTimer and commenting out the wxPaintDC line (but still, for example, printf something every time the timer goes off). I was able to confirm this by adding a simple timer to the penguin sample program.

I'm using a wxGLCanvas, but I found this forum post with someone experiencing similar problems but not using OpenGL: http://wxforum.shadonet.com/viewtopic.php?t=13295&highlight=

I don't know if this is exactly a bug, but the way in which things are implemented makes this problem VERY hard to debug. I suspected problems with my timer, ID clashes, something.. not that my OnPaint function was missing a line. After a couple days I figured out that commenting out the EVT_PAINT entry in the event table caused the timer to start working again. That was a big revelation but I was no closer to solving my problem!

I also know (in retrospect) that all of the examples include the wxPaintDC line, some with comments like "This must be here", but there's no explanation why. A reasonable person might expect that the drawing would simply stop working.

So - even if this isn't fixed in code, I think it could be documented better. The wxPaintEvent documentation has the line "Note that In a paint event handler, the application must always create a wxPaintDC object, even if you do not use it. Otherwise, under MS Windows, refreshing for this and other windows will go wrong." but, to go with my theme, this does not lead me to believe that the timer would stop working - not to mention if my timer stops working I don't go to the wxPaintEvent documentation immediately :-P It's not mentioned at all in the wxTimer documentation.

I got this working with the 2.9 svn release, but also tried 2.9.0 and originally encountered problems with 2.8.10.

Change History (3)

comment:1 Changed 4 years ago by vadz

  • Resolution set to invalid
  • Status changed from new to closed

It's not a bug, if you don't create wxPaintDC the area of the window which must be painted is never validated and so you keep getting more and more EVT_PAINT events. IMO the documentation is clear enough about it, it does say to not do what you did, I don't know why would you want to do something that is documented as resulting in problems but I'll try to make it more clear.

In any case, there is no bug here, just don't do this.

comment:2 Changed 4 years ago by m0nstr42

If you can ignore my admittedly boneheaded mistake (the wxPaintDC must have disappeared in a copy/paste... easy to lose one line in 27000), consider a hypothetical situation.

  • Developer starts working on a simple rendering app based on a wxTimer.
  • leaves EVT_PAINT handler empty or just puts "printf("paint\n");" to see if it is working.
  • notices that the timer isn't working
  • spends a day trying to figure out why the timer isn't working

Alternatively, in my case, you have someone with a fairly large project that is working on one OS but fails on another in a way that's incredibly hard to debug.

Do you see my point? I won't whine anymore, I'm just trying to save someone else the pain I went through. I like to think I'm relatively competent and it seriously took me about two solid days to figure this out.

comment:3 Changed 4 years ago by VZ

(In [63229]) Allow to not create wxPaintDC in EVT_PAINT handler in wxMSW.

Failure to create a wxPaintDC in EVT_PAINT handler resulted in many serious
and difficult to debug problems under wxMSW. We used to document that the user
shouldn't do it but this wasn't enough (see #11648). We could also assert if
we detected that a handler didn't create a wxPaintDC but it seems better to
just handle this case gracefully for consistency with the other platforms.

Add wxDidCreatePaintDC global variable which is reset before generating
wxPaintEvent and set to true when ::BeginPaint() is called from wxPaintDC
ctor and validate the update region of the window ourselves if it wasn't set
(meaning that wxPaintDC wasn't created).

Update the documentation to emphasize the link between EVT_PAINT handlers and
wxPaintDC but without saying that wxPaintDC object must always be created in
the handler as this is not true any more.

Note: See TracTickets for help on using tickets.