Allow to not create wxPaintDC in EVT_PAINT handler in wxMSW.
authorVadim Zeitlin <vadim@wxwidgets.org>
Sat, 23 Jan 2010 13:22:25 +0000 (13:22 +0000)
committerVadim Zeitlin <vadim@wxwidgets.org>
Sat, 23 Jan 2010 13:22:25 +0000 (13:22 +0000)
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.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63229 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
interface/wx/event.h
src/msw/dcclient.cpp
src/msw/window.cpp

index a774275fee178cad6aca713f17f4f8336e964063..fa8a77daa856312652e45d73a4cab267cc340e19 100644 (file)
@@ -517,6 +517,7 @@ MSW:
 - Worked around child window and caret positioning bug (in Windows) when using
   wxBORDER_THEME in a container window.
 - Suppressed spurious character event for decimal key in numeric keypad.
+- Allow to not create wxPaintDC in EVT_PAINT handler.
 
 i18n:
 
index dc010cc28d5383fb55421456ac5ab52560a610dc..07f56d82c86baedd953c61db5d6036eca796eb28 100644 (file)
@@ -1547,16 +1547,8 @@ public:
 
     A paint event is sent when a window's contents needs to be repainted.
 
-    Please notice that in general it is impossible to change the drawing of a
-    standard control (such as wxButton) and so you shouldn't attempt to handle
-    paint events for them as even if it might work on some platforms, this is
-    inherently not portable and won't work everywhere.
-
-    @remarks
-    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.
-    For example:
+    The handler of this event must create a wxPaintDC object and use it for
+    painting the window contents. For example:
     @code
     void MyWindow::OnPaint(wxPaintEvent& event)
     {
@@ -1565,6 +1557,12 @@ public:
         DrawMyDocument(dc);
     }
     @endcode
+
+    Notice that you must @e not create other kinds of wxDC (e.g. wxClientDC or
+    wxWindowDC) in EVT_PAINT handlers and also don't create wxPaintDC outside
+    of this event handlers.
+
+
     You can optimize painting by retrieving the rectangles that have been damaged
     and only repainting these. The rectangles are in terms of the client area,
     and are unscrolled, so you will need to do some calculations using the current
@@ -1601,6 +1599,12 @@ public:
     }
     @endcode
 
+    @remarks
+    Please notice that in general it is impossible to change the drawing of a
+    standard control (such as wxButton) and so you shouldn't attempt to handle
+    paint events for them as even if it might work on some platforms, this is
+    inherently not portable and won't work everywhere.
+
 
     @beginEventTable{wxPaintEvent}
     @event{EVT_PAINT(func)}
index b64aee8c82fa743b01c064836eaccab6144be661..bad81c1e72762e0ba45cd3d69a797aa56573060d 100644 (file)
@@ -236,6 +236,11 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) :
     }
     else // not in cache, create a new one
     {
+        // see comments in src/msw/window.cpp where this is defined
+        extern bool wxDidCreatePaintDC;
+
+        wxDidCreatePaintDC = true;
+
         m_hDC = (WXHDC)::BeginPaint(GetHwndOf(m_window), &g_paintStruct);
         if (m_hDC)
             ms_cache.Add(new wxPaintDCInfo(m_window, this));
index 0ebb93aa599e036d279ed77d1dbe9b6cce83667e..c3c711cb987a7ca841242126dca21af453ccbc97 100644 (file)
@@ -4796,6 +4796,14 @@ wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName,
 // painting
 // ---------------------------------------------------------------------------
 
+// this variable is used to check that a paint event handler which processed
+// the event did create a wxPaintDC inside its code and called BeginPaint() to
+// validate the invalidated window area as otherwise we'd keep getting an
+// endless stream of WM_PAINT messages for this window resulting in a lot of
+// difficult to debug problems (e.g. impossibility to repaint other windows,
+// lack of timer and idle events and so on)
+extern bool wxDidCreatePaintDC = false;
+
 bool wxWindowMSW::HandlePaint()
 {
     HRGN hRegion = ::CreateRectRgn(0, 0, 0, 0); // Dummy call to get a handle
@@ -4810,11 +4818,20 @@ bool wxWindowMSW::HandlePaint()
 
     m_updateRegion = wxRegion((WXHRGN) hRegion);
 
+    wxDidCreatePaintDC = false;
+
     wxPaintEvent event(m_windowId);
     event.SetEventObject(this);
 
     bool processed = HandleWindowEvent(event);
 
+    if ( processed && !wxDidCreatePaintDC )
+    {
+        // do call MSWDefWindowProc() to validate the update region to avoid
+        // the problems mentioned above
+        processed = false;
+    }
+
     // note that we must generate NC event after the normal one as otherwise
     // BeginPaint() will happily overwrite our decorations with the background
     // colour