12
votes

When overriding the OnPaint method of a custom control I'm supplied with a PaintEventArgs, which contains a Graphics object and a ClipRectangle. The ClipRectangle is too small for my tastes and so I want to either extend it by say, 100 pixels, or ignore it completely.

No matter what I do, I can't seem to break out of the ClipRectangle. So far I've tried:

e.ClipRectangle.Inflate(100, 0);
e.Graphics.ResetClip();
e.Graphics.SetClip(new Rectangle(x, y, w, h));

The parent control is calling Invalidate(true) (to force an invalidation of all the childs' child controls).

I've also done some googling and looked on bob powell's site but can't find nuffin.

ResetClip resets the clipping region for the Graphics object to an infinitely large Rectangle, but the Graphics object cheerfully continues to use the provided ClipRectangle.

Help.

2
The caller must have control on what get's painted. You should call Invalidate() on one of the owning control so that the entire region get's invalidated -> scheduled for redrawsehe
I think you're right that the caller has a hand in specifying what region gets painted. Thing is, I am the caller, and I call Invalidate as normal with no restrictions.Matt Jacobsen
The Windows API passes a clip region to WM_PAINT if the repaint is happening because only part of the control needs to be redrawn, e.g. if you minimized a window that was only partially covering the control. I assume ClipRectangle is just giving you a friendlier view to the clip region, but I wouldn't expect there to be a clip region if you're calling Invalidate. Is the control partially covered by other controls? Can you show simple repro code?Joe White
+1 just for the title, by the way.Joe White

2 Answers

5
votes

Recap: It sounds like you have a child control with a custom paint method. You want to paint the control in an area that is larger than the control's bounds itself. For instance if you place your control at (10, 10, 100, 100) you want to draw the area at (0, 10, 110, 100).

You can't exactly do that, the control's HDC that is passed to paint method is for that control's native window handle. It is clipped by the OS and AFAIK there is no way to draw outside of it with the HDC given to the paint method.

What you can do: One option is to override the parent window's create method and remove the window style WS_CLIPCHILDREN (0x02000000), then draw the child in the parent's paint method.

Another option is to just expand the area of the child window so that it encompasses the area you want to draw. If you also override the OnPaintBackground method of your control you can prevent the background paint from clearing the parent's rendering. This is problematic though since the parent will clip the area of the child and not refresh it. Thus you still need to remove the parent's WS_CLIPCHILDREN for it to work.

I'm sure there are other possibilities all of which are basically the same result, you can't do that.

5
votes

Rectangle is a struct, not a class - it's passed as value so calling e.ClipRectangle.Inflate(100, 0); can't help even theoretically.

Next thing you have to know is that e.Graphics is related to hardware device context, so Windows won't let you expand painting area. Solution is to get device context of the parent window and then paint wherever you want to. This is how to do it:

[DllImport("User32.dll")]
static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("User32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

IntPtr dcPtr = GetDC(this.ParentForm.Handle);
Graphics g = Graphics.FromHdc(dcPtr);

// your code here

// don't forget to release it
g.Dispose();
ReleaseDC(this.ParentForm.Handle, dcPtr);