I am trying to use this tutorial so that I can have a transparent button. It works fine for the main background, but it doesn't draw over the other children. If I use BringToFront()
it then doesn't have the other child's drawing where it should be.
I have started to get around it by adding this to the code:
foreach (Control child in Parent.Controls) {
if(child != this) {
InvokePaintBackground(child, pea);
InvokePaint(child, pea);
}
}
And although I get some of what I want, it's in the wrong location (on the left instead of in the middle where it shoudl be) and the shapes which are drawn in the child's paint event also aren't showing up.
How can I modify so that I will have all of the other children as well giving the full illusion of transparency?
Note: I'm not worried about paining for anybody but other children, as I know that there aren't any, and there are pleny of other places to find out how to get all of the children recursively.
Thanks to C.Evenhuis answer, its now working. My implementation is simple (only one other child), so this is my code. For future readers, be sure to read that post though to get a fll scope.
using (PaintEventArgs pea = new PaintEventArgs(e.Graphics, rect)) {
pea.Graphics.SetClip(rect);
InvokePaintBackground(Parent, pea);
InvokePaint(Parent, pea);
foreach (Control child in Parent.Controls) {
if (child != this) {
pea.Graphics.ResetTransform();
pea.Graphics.TranslateTransform(child.Left - Left, child.Top - Top);
InvokePaintBackground(child, pea);
InvokePaint(child, pea);
}
}
}
When painting, all controls assume their top-left corner is at the (0, 0) coordinate. This is achieved by setting the viewport of the Graphics
object to the coordinates of the control before OnPaint
is called.
To paint the other controls, you'll have to do this manually:
if (child != this)
{
int offsetX = control.Left - Left;
int offsetY = control.Top - Top;
// Set the viewport to that of the control
pevent.Graphics.TranslateTransform(offsetX, offsetY);
// Translate the clip rectangle to the new coordinate base
Rectangle clip = pevent.ClipRectangle;
clip.Offset(-offsetX, -offsetY); // Ugly self-modifying struct
PaintEventArgs clippedArgs = new PaintEventArgs(pevent.Graphics, clip);
InvokePaintBackground(control, clippedArgs);
InvokePaint(control, clippedArgs);
pevent.Graphics.TranslateTransform(-offsetX, -offsetY)
}
Things get a bit more complicated if the underlying control is a Panel
containing child controls of its own - these are not automatically painted along with their parent. If you need to support that too I suggest sending a WM_PRINT
message to the parent control and to the silbing controls below the current control - for the sibling controls you can then set the PRF_CHILDREN
flag to let it paint its descendants too.
Also currently you're painting all sibling controls - including the ones above the current control. You might want to let the loop go backwards and break
when you reach the current control. This won't be a real issue though until you start stacking multiple transparent controls.
This isn't an answer but I had to do something similar once. This is what I did:
this.SetStyle(
ControlStyles.ResizeRedraw |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.UserPaint, true);
this.BackColor = Color.Transparent;
protected override void OnPaint(PaintEventArgs e)
{
// TODO: Draw the button here
base.OnPaint(e);
}
It does not draw the children behind but it worked better than InvokePaintBackground
and InvokePaint
for some reason. I had a lot of troubles attempting to draw the children especially when children were some owner drawn 3rd party controls (I'm talking really weird issues). I'll fav question to see if there's other ideas. Good luck.