My two cents notes, completing what is already discussed.
Generally it's not a brilliant idea to directly draw in the client area of a dialog, except the case you want to fill it with some fancy brush.
That case, indeed, the best place is in WM_ERASEBKGND message handler.
Example
Code:
BOOL CMyDialog::OnEraseBkgnd(CDC* pDC)
{
CBrush* pbrushOld = pDC->SelectObject(&m_brushBack);
CRect rect;
pDC->GetClipBox(&rect);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pbrushOld);
return TRUE; // no further default processing
}
I have given the above example just FYI.
If you have a graph/image to show in a dialog, it's better to make a custom, ActiveX or simpler, a static control.
For a static control, do the following:
- Derive your own class from CStatic, let's say it CStaticImage.
- In CStaticImage class handle WM_ERASEBKGND and WM_PAINT messages.
- In the WM_ERASEBKGND function handler do nothing; just return TRUE to prevent default processing.
Code:
BOOL CStaticImage::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
- Perform the drawing in WM_PAINT handler. If the drawing is complex, it's preferable to use a memory device context to draw then bit-blit its contents into the destination device context.
Code:
void CStaticImage::OnPaint()
{
CPaintDC dc(this); // device context for painting
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
const int nSavedDC = dcMem.SaveDC();
CRect rc;
GetClientRect(rc);
CBitmap bmp;
bmp.CreateCompatibleBitmap(&dc, rc.Width(), rc.Height());
dcMem.SelectObject(&bmp);
// just for demo purpose:
dcMem.Rectangle(0, 0, rc.Width(), rc.Height());
dcMem.Ellipse(2, 2, 100, 50);
dcMem.SetPixel(10, 10, RGB(0,0,255));
// do all other drawing here...
// when the drawing is complete, bit-blit into the target DC
dc.BitBlt(0, 0, rc.Width(), rc.Height(), &dcMem, 0, 0, SRCCOPY);
dcMem.RestoreDC(nSavedDC);
}
- Finally, put the picture control into the dialog window.
- In the resource editor, add a static (picture control) to dialog template; change its ID from IDC_STATIC to something else, e.g. IDC_STATIC_IMAGE.
- Using the Wizard, add to dialog class a control member variable for IDC_STATIC_IMAGE, let's say it m_staticImage; be sure that m_staticImage is of type CStaticImage and "StaticImage.h" header is included.
That's all.
Note that above code is just for demo purpose and you have to adapt it for your concrete needs.
Further, you can add other methods to CStaticImage in order to pass drawig parameters, force redraw, and so on.
Another one note:
InvalidateRect(0) invalidates (marks for painting) the whole client area of the window. If the drawing is very complex, may be preferable to pass only the area which effectively has to be painted at one moment. And, as already discussed here, it has no sense to call
InvalidateRect inside the WM_PAINT message handler (it does not lead into an infinite loop, as long as it does not send WM_PAINT message but, it has no sense).
And a final one:
Drawing something pixel by pixel may be not a good practical idea. Try to find and use "higher level" GDI functions.