views:

214

answers:

3

I am writing a toolbar-style control and use the ThemeServices unit to paint the background required by the toolbar. Like so:

ThemeServices.DrawElement(Canvas.Handle, 
  ThemeServices.GetElementDetails(trRebarRoot), ARect);

I then drop child controls onto the toolbar and voila - instant toolbar. Except that every now and again I notice that the nested (child) controls don't repaint as well. So my control paints its background and the child controls disappear until you move the mouse over them.

Some of the edit controls show their client area (no border) and some of them are just gone altogether.

This happens mostly when I place a window from another application over the toolbar and move it away.

I keep thinking this has to be very easy to cure, that perhaps I'm somehow preventing the paint message from reaching the child controls, or missing an override but I have been unable to figure it out.

+1  A: 

That's normal for a canvas to have to repaint when covered by another windows control.

You should draw your tool bar in the OnPaint event of the container control.

Marcus Adams
Sorry, you've lost me. Forget about the toolbar and let's talk about a regular TPanel for now. If I open something over the TPanel, I would expect it to redraw its visible portions, even if it has a couple of edit boxes on top. The toolbar is no different.
Cobus Kruger
Did you put a break point on your WM_PAINT handler to notice that it's called again when a windows control goes over top of it and then is removed?
Marcus Adams
A: 

OK, I found the problem myself. I was drawing the background in WM_NCPAINT because it is a gradiated background that cannot really be drawn bit by bit. I didn't realize that WM_NCPAINT is often called without the client controls being repainted. So I still paint the background in WM_NCPAINT but also in WM_PAINT. The latter is already clipped to exclude the border and to get the former to clip the client area, I called ExcludeClipRect.

This works like a treat.

Cobus Kruger
+1  A: 

For this to work properly so you do not end up over-painting the child controls from the WM_NCPaint, you can use the Window's GDI Region functions to create a clipping region which excludes the areas you do not want to paint.

You can do this by creating a Region using the CreateRectRgn (or similar) function with the size of your background.

Select this region in to the DC you are using via the SelectClipRgn function.

Next, loop through the top level child windows on your toolbar / panel and calling ExcludeClipRect with the appropriate coords for each child.

Finally when you paint, only the area's that have not been excluded will be painted.

Also be aware you might need to clear up your region (best check the GDI help).

Aikislave
Pretty much what I found (see my own answer), but yours give a much better explanation. Thanks!
Cobus Kruger