Discussion:
CDialog + WM_PAINT
(too old to reply)
Zaphod®
2003-07-15 21:07:52 UTC
Permalink
If you just open the MSND libraty on WM_PAINT or CWnd::OnPaint you will find
yourself warned that you should not use OnPaint to execute application
defined paint code.

Views have OnDraw which is suitable to run paint and draw code (Erase
backgrownd message is also available), however you can reconsider using view
that inherits CFormView maybe.

regards.
I'm having a few problems with WM_PAINT within dialogs.
In one particular dialog, I am calling OnPaint() a total
of THREE times during initialisation yet still the stuff
I'm painting doesn't appear.
It may be a timing issue as I've had a bug report that one
of the dialogs in my app doesn't display a picture on a
paticular PC 50% of the time, and I've just encountered
the problem here on another dialog after adding some
seemingly innocent code (which could only mean its timing
related?).
Have I missed something important to do with painting on
dialogs ?
All's I can think off is that some kind of background
erase is going on without a following WM_PAINT ... trouble
is for some reason dialogs don't have a WM_ERASEBKGND
message so I can't see how to check for this :-(
Adding an "OnPaint()" directly on the end of OnInitDialog
() doesn't help. Short term,I've had to resort to calling
OnPaint() from within a timer for a short period after
dialog is created ... clearly wrong but what else is
there ?
Scott McPhillips
2003-07-15 23:39:00 UTC
Permalink
I'm having a few problems with WM_PAINT within dialogs.
In one particular dialog, I am calling OnPaint() a total
of THREE times during initialisation yet still the stuff
I'm painting doesn't appear.
It may be a timing issue as I've had a bug report that one
of the dialogs in my app doesn't display a picture on a
paticular PC 50% of the time, and I've just encountered
the problem here on another dialog after adding some
seemingly innocent code (which could only mean its timing
related?).
Have I missed something important to do with painting on
dialogs ?
All's I can think off is that some kind of background
erase is going on without a following WM_PAINT ... trouble
is for some reason dialogs don't have a WM_ERASEBKGND
message so I can't see how to check for this :-(
Adding an "OnPaint()" directly on the end of OnInitDialog
() doesn't help. Short term,I've had to resort to calling
OnPaint() from within a timer for a short period after
dialog is created ... clearly wrong but what else is
there ?
You've got things totally hosed up Jon. Remove *all* calls to OnPaint.
Never call it again, ever, in any Windows program. If you want to force
a repaint you can safely call Invalidate, which causes WM_PAINT to come
in later.

Remove *all* painting from anywhere except in your WM_PAINT (or
WM_ERASEBKGND) handler. If you don't do these things there is no hope
of it working right. You are misunderstanding the most basic aspects of
how WM_PAINT works. If you don't know how to do this spend some time
with the introductory SCRIBBLE tutorial.

--
Scott McPhillips [VC++ MVP]
Jon Evans
2003-07-16 07:39:31 UTC
Permalink
Hi Scott,

Thanks for the reply. However I don't think I explained it
very well.

When I first created the dialog, there were no direct
calls to OnPaint(). This initially worked fine on this PC.
I also intercepted OnSize() which re-calced some
positional stuff and called Invalidate() as the dialog
does get resized by the parent after initial creation.

Later I added some minor code to the OnInitDialog()
[setting the ranges of a spin control] and suddenly the
results of my WM_PAINT handler dissapeared. Placing an
Invalidate() at end of InitDialog() didn't help.

Adding a trace statement inside OnPaint() showed that the
func was called three times during initialisation and that
the co-ordinates at which I was attempting to draw were
perfectly valid.

Finally calling OnPaint() was just desperation.

I have had a bug report of a similar problem with a
different dialog in the same app ; I can't replicate it
here but it was a little more random (worked sometimes,
sometimes not) which screams timing related to me. Indeed
in the same report there is mention that occasionally only
half the image I'd drawn appeared !

TTFN,
Jon
-----Original Message-----
I'm having a few problems with WM_PAINT within dialogs.
In one particular dialog, I am calling OnPaint() a total
of THREE times during initialisation yet still the stuff
I'm painting doesn't appear.
It may be a timing issue as I've had a bug report that
one
of the dialogs in my app doesn't display a picture on a
paticular PC 50% of the time, and I've just encountered
the problem here on another dialog after adding some
seemingly innocent code (which could only mean its
timing
related?).
Have I missed something important to do with painting
on
dialogs ?
All's I can think off is that some kind of background
erase is going on without a following WM_PAINT ...
trouble
is for some reason dialogs don't have a WM_ERASEBKGND
message so I can't see how to check for this :-(
Adding an "OnPaint()" directly on the end of
OnInitDialog
() doesn't help. Short term,I've had to resort to
calling
OnPaint() from within a timer for a short period after
dialog is created ... clearly wrong but what else is
there ?
You've got things totally hosed up Jon. Remove *all*
calls to OnPaint.
Never call it again, ever, in any Windows program. If
you want to force
a repaint you can safely call Invalidate, which causes
WM_PAINT to come
in later.
Remove *all* painting from anywhere except in your
WM_PAINT (or
WM_ERASEBKGND) handler. If you don't do these things
there is no hope
of it working right. You are misunderstanding the most
basic aspects of
how WM_PAINT works. If you don't know how to do this
spend some time
with the introductory SCRIBBLE tutorial.
--
Scott McPhillips [VC++ MVP]
.
Alexander Grigoriev
2003-07-17 03:45:10 UTC
Permalink
Why not just make a custom control? Derive it from CWnd, insert it to the
dialog in OnInitDialog. You already have OnPaint handler for it.
Well, here goes.
After data in "m_pCfgTimeSlots" has been altered by the
dialog then a call is made to RedrawTimeSlots()which is a
macro :-
#define RedrawTimeSlots() (InvalidateRect
(&m_rectTimeBar,FALSE ))
m_rectTimeBar is calculated within OnInitDialog() and
OnSize() and has been verified as correct in value.
void CDlgCfgTimeZonesDay::OnPaint()
{
CPaintDC dc(this); // device context for painting
TRACE("OnPaint()\r\n") ;
CDC *pDC = GetDC() ;
DrawTimeBar( pDC ) ;
// Everything other than time bar is a bog standard
dialog control.
ReleaseDC( pDC ) ;
}
void CDlgCfgTimeZonesDay::DrawTimeBar(CDC *pDC)
{
// Draw the basic empty time slot display
CPen *pOldPen = (CPen *) pDC->SelectObject(
&m_penOuterBar ) ;
CBrush *pOldBrush = (CBrush *) pDC->SelectObject(
&m_brushInnerBar ) ;
pDC->Rectangle( &m_rectTimeBar ) ;
TRACE("Draw Bar (%d,%d)-(%d,%d)\r\n",
m_rectTimeBar.left, m_rectTimeBar.top,
m_rectTimeBar.right, m_rectTimeBar.bottom ) ;
/*************************/
/*** Add hour markings ***/
/*************************/
// Select a font to match that used by the dialog
CFont *pDialogFont = GetFont() ;
CFont *pOldFont = pDC->SelectObject(
pDialogFont ) ;
for( int Hour = 0 ; Hour <= 24 ; Hour++ )
{
// Set x = horiz pix position for marker
int x = ((m_rectTimeBar.right-1 -
m_rectTimeBar.left) * Hour )/24 + m_rectTimeBar.left ;
pDC->MoveTo( x, m_rectTimeBar.top - 3 ) ;
if( Hour % 3 ) // two small marks between
each 3 hour marker
{
pDC->LineTo( x, m_rectTimeBar.top -
6 ) ;
}
else // Larger marker with text every 3
hours
{
pDC->LineTo( x, m_rectTimeBar.top -
10 ) ;
CString csTime ;
csTime.Format( "%02d:00", Hour ) ;
CSize Size ;
Size = pDC->GetTextExtent(
csTime ) ;
POINT Point ;
Point.x = x ;
Point.y = m_rectTimeBar.top-11 ;
pDC->SetBkColor( pDC->GetPixel(
Point ) ) ; // Must work out the right way to get EXACT
background colour some day !!!
pDC->TextOut( x-(Size.cx/2) /*
marker is centered*/, m_rectTimeBar.top - 10 - Size.cy,
csTime ) ;
}
}
/*************************************/
/*** Now draw the individual slots ***/
/*************************************/
pDC->SelectObject( &m_penActiveSlot ) ;
pDC->SelectObject( &m_brushActiveSlot ) ;
for( int Slot = 0 ; Slot < m_pCfgTimeSlots-
GetNumSlots() ; Slot++ )
{
if( m_pCfgTimeSlots->SlotIsActive(
m_idCurrentDay, Slot ) )
{
// Get a rectangle that sits just inside main rect
CRect Rect = m_rectTimeBar ;
Rect.top += 2 ; // Small margin
Rect.bottom -= 2 ;
// Convert above rectangle so that it corresponds to
region occupied only by this particular slot
m_pCfgTimeSlots->GetSlotRect(
Slot, Rect ) ;
// Rect is now the size/pos of
just the slot
pDC->Rectangle( &Rect ) ;
}
}
// Restore device context's original colours
pDC->SelectObject( pOldPen ) ;
pDC->SelectObject( pOldBrush ) ;
// Restore device context's original font.
pDC->SelectObject( pOldFont ) ;
}
I have since changed the order of things being initialised
in the OnInitDialog function. The result is that
setting/not setting spin control ranges no longer has an
effect but varying later init stuff brings back the "now
you see it now you dont" problem. So its a timing issue.
-----Original Message-----
Post by Jon Evans
Later I added some minor code to the OnInitDialog()
[setting the ranges of a spin control] and suddenly the
results of my WM_PAINT handler dissapeared. Placing an
Invalidate() at end of InitDialog() didn't help.
An Invalidate() call in OnInitDialog is not expected to
change
anything. At that point the dialog is
already "scheduled" to get a
WM_PAINT after it becomes visible, and the extra request
changes
nothing.
I suggest commenting out code until stability returns,
then
reintroducing these code patches step by step and
diagnosing what's
really happening. Obviously, setting the range of a
spinner should have
no effect on the painting. Don't try to patch up the
painting, try to
eliminate the cause. There is also a possibility that
your OnPaint code
contains errors. You may wish to post it for examination.
--
Scott McPhillips [VC++ MVP]
.
Joseph M. Newcomer
2003-07-16 03:27:04 UTC
Permalink
How are you "calling" OnPaint? This is not a function you can call yourself, because it
can only work when a WM_PAINT message arrives. Otherwise the effects are meaningless.

So adding an OnPaint() call anywhere is somewhere between totally useless and completely
erroneous. If you need to force a drawing RIGHT NOW! then call UpdateWindow, which will
force a WM_PAINT if one is pending. You still need to do the Invalidate() or
InvalidateRect() to select the regions that will need to be redrawn.
joe
I'm having a few problems with WM_PAINT within dialogs.
In one particular dialog, I am calling OnPaint() a total
of THREE times during initialisation yet still the stuff
I'm painting doesn't appear.
It may be a timing issue as I've had a bug report that one
of the dialogs in my app doesn't display a picture on a
paticular PC 50% of the time, and I've just encountered
the problem here on another dialog after adding some
seemingly innocent code (which could only mean its timing
related?).
Have I missed something important to do with painting on
dialogs ?
All's I can think off is that some kind of background
erase is going on without a following WM_PAINT ... trouble
is for some reason dialogs don't have a WM_ERASEBKGND
message so I can't see how to check for this :-(
Adding an "OnPaint()" directly on the end of OnInitDialog
() doesn't help. Short term,I've had to resort to calling
OnPaint() from within a timer for a short period after
dialog is created ... clearly wrong but what else is
there ?
Joseph M. Newcomer [MVP]
email: ***@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Jon Evans
2003-07-16 07:50:56 UTC
Permalink
Hi Jeff,

I don't normally call OnPaint() ... honest ! Desperate
measures.

I'm drawing something that changes in response to user
interaction with the dialog, so intercepting WM_ERASEBKGND
to do any drawing might risk leading to flickering.

I've since noticed that occasionally I can see a flicker
of my object during dialog init so it must be something to
do with the background being erased with no subsequent
WM_PAINT ? If so then there is no alternative but to trap
WM_ERASEBKGND ... what is meant by "adjust the message
filter" ?

TTFN,
Jon
-----Original Message-----
Don't call OnPaint -- it's a message handler and the
WM_PAINT message is
sent to the window by the OS. Dialogs are able to handle
the
WM_ERASEBKGND message, but you may need to adjust the
message filter for
your dialog class to see it in the wizard. For my money,
painting on
dialogs should be done in response to WM_ERASEBKGND --
this is where the
WC_DIALOG class does all its painting, and all that the
dialog WM_PAINT
handler does is validate the update RECT. It also makes
it easy to
'capture' the dialog's background externally by doing a
SendMessage/WM_ERASEBKGND with a GetClientRect sized
bitmap
SelectObject'd into a memory HDC. Joe, on the other hand,
recommends
using a subclassed CStatic.
--
Jeff Partch [VC++ MVP]
I'm having a few problems with WM_PAINT within dialogs.
In one particular dialog, I am calling OnPaint() a total
of THREE times during initialisation yet still the stuff
I'm painting doesn't appear.
It may be a timing issue as I've had a bug report that
one
of the dialogs in my app doesn't display a picture on a
paticular PC 50% of the time, and I've just encountered
the problem here on another dialog after adding some
seemingly innocent code (which could only mean its
timing
related?).
Have I missed something important to do with painting
on
dialogs ?
All's I can think off is that some kind of background
erase is going on without a following WM_PAINT ...
trouble
is for some reason dialogs don't have a WM_ERASEBKGND
message so I can't see how to check for this :-(
Adding an "OnPaint()" directly on the end of
OnInitDialog
() doesn't help. Short term,I've had to resort to
calling
OnPaint() from within a timer for a short period after
dialog is created ... clearly wrong but what else is
there ?
.
Jeff Partch
2003-07-16 13:13:23 UTC
Permalink
Post by Jon Evans
Hi Jeff,
I don't normally call OnPaint() ... honest ! Desperate
measures.
I'm drawing something that changes in response to user
interaction with the dialog, so intercepting WM_ERASEBKGND
to do any drawing might risk leading to flickering.
Can't you optimize it somehow to only repaint the GetClipBox area? Maybe
draw to a GetCLientRect sized CreateCompatibleBitmap that you
SelectObject into a CreateCompatibleDC, and then BitBit using GetClipBox
offsets and size.
Post by Jon Evans
I've since noticed that occasionally I can see a flicker
of my object during dialog init so it must be something to
do with the background being erased with no subsequent
WM_PAINT ? If so then there is no alternative but to trap
WM_ERASEBKGND ... what is meant by "adjust the message
filter" ?
For VC6, right-click on the dialog's entry in the ClassView tree pane
and select "Add Windows Message Handler". In the resulting dialog, look
for a combobox in the bottom right corner with the label "Filter for
messages available to class". From this list select "Window".
--
Jeff Partch [VC++ MVP]
Alexander Grigoriev
2003-07-16 13:54:12 UTC
Permalink
What is your goal?
Do you want to draw a bitmap in the dialog, or want to use a custom
background, or what?
For a bitmap, you can use STATIC/SS_BITMAP, for painting custom background,
handle WM_ERASEBKGND (after calling default handler for it, if you want some
part with a default background).
Post by Jon Evans
Hi Jeff,
I don't normally call OnPaint() ... honest ! Desperate
measures.
I'm drawing something that changes in response to user
interaction with the dialog, so intercepting WM_ERASEBKGND
to do any drawing might risk leading to flickering.
I've since noticed that occasionally I can see a flicker
of my object during dialog init so it must be something to
do with the background being erased with no subsequent
WM_PAINT ? If so then there is no alternative but to trap
WM_ERASEBKGND ... what is meant by "adjust the message
filter" ?
TTFN,
Jon
-----Original Message-----
Don't call OnPaint -- it's a message handler and the
WM_PAINT message is
sent to the window by the OS. Dialogs are able to handle
the
WM_ERASEBKGND message, but you may need to adjust the
message filter for
your dialog class to see it in the wizard. For my money,
painting on
dialogs should be done in response to WM_ERASEBKGND --
this is where the
WC_DIALOG class does all its painting, and all that the
dialog WM_PAINT
handler does is validate the update RECT. It also makes
it easy to
'capture' the dialog's background externally by doing a
SendMessage/WM_ERASEBKGND with a GetClientRect sized
bitmap
SelectObject'd into a memory HDC. Joe, on the other hand,
recommends
using a subclassed CStatic.
--
Jeff Partch [VC++ MVP]
I'm having a few problems with WM_PAINT within dialogs.
In one particular dialog, I am calling OnPaint() a total
of THREE times during initialisation yet still the stuff
I'm painting doesn't appear.
It may be a timing issue as I've had a bug report that
one
of the dialogs in my app doesn't display a picture on a
paticular PC 50% of the time, and I've just encountered
the problem here on another dialog after adding some
seemingly innocent code (which could only mean its
timing
related?).
Have I missed something important to do with painting
on
dialogs ?
All's I can think off is that some kind of background
erase is going on without a following WM_PAINT ...
trouble
is for some reason dialogs don't have a WM_ERASEBKGND
message so I can't see how to check for this :-(
Adding an "OnPaint()" directly on the end of
OnInitDialog
() doesn't help. Short term,I've had to resort to
calling
OnPaint() from within a timer for a short period after
dialog is created ... clearly wrong but what else is
there ?
.
Continue reading on narkive:
Loading...