Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a transparent control which works when on top of other controls?

I have a control (derived from System.Windows.Forms.Control) which needs to be transparent in some areas. I have implemented this by using SetStyle():

public TransparentControl()
{
    SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    this.BackColor = Color.Transparent.
}

Now, this works if there are no controls between the form and the transparent control. However, if there happens to be another control below the transparent control (which is the use case here), it does not work. The intermediate control is not draw, but the form below does show through. I can get the effect that I need by overriding CreateParams and setting the WS_EX_TRANSPARENT flasg like so:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
        return cp;
    }
}

The problem here is that it really slows down the painting of the control. The control is already double buffered, so nothing to do there. The performance hit is so bad that it is unacceptable. Has anyone else encountered this problem? All of the resources that I can find suggest using method #1, but again, that does not work in my case.

EDIT: I should note that I do have a workaround. The child (transparent) controls could simply draw themselves onto the parent's Graphics object, but it is really ungly and I don't like the solution at all (though it may be all I have).

EDIT2: So, following the advice that I got on how transparency works in .NET, I have implemented the IContainer interface in my user control which contains the transparent controls. I have created a class which implements ISite, I add my child controls to the Components collection of the UserControl, the Container property lines up correctly in the debugger, but I still do not get a transparency effect. Does anyone have any ideas?

like image 411
Ed S. Avatar asked Feb 26 '09 21:02

Ed S.


3 Answers

This is just a simple thing I cooked up.. The only issue I've found is that it doesn't update when the intersecting controls are updated..

It works by drawing a control that's behind/intersects with the current control to a bitmap, then drawing that bitmap to the current control..

protected override void OnPaint(PaintEventArgs e)
{
    if (Parent != null)
    {
        Bitmap behind = new Bitmap(Parent.Width, Parent.Height);
        foreach (Control c in Parent.Controls)
            if (c.Bounds.IntersectsWith(this.Bounds) & c != this)
                c.DrawToBitmap(behind, c.Bounds);
        e.Graphics.DrawImage(behind, -Left, -Top);
        behind.Dispose();
    }
}
like image 107
Lee Avatar answered Oct 11 '22 17:10

Lee


Transparent controls in DotNet are implemented by having the transparent control's container draw itself in the transparent control's window and then having the transparent control draw itself. This process doesn't take into account the possibility of overlapping controls. So you will need to use some sort of work-around to make it work.

In some cases I've had success with complex nesting, but that's mostly only good for quick-and-dirty layering of bitmaps, and it doesn't solve any issues with partially overlapping controls.

like image 21
snarf Avatar answered Oct 11 '22 19:10

snarf


I found out that the modifications below make things a bit faster:

if((this.BackColor == Color.Transparent) && (Parent != null)) {
    Bitmap behind = new Bitmap(Parent.Width, Parent.Height);
    foreach(Control c in Parent.Controls) {
        if(c != this && c.Bounds.IntersectsWith(this.Bounds)) {
            c.DrawToBitmap(behind, c.Bounds);
        }
    }
    e.Graphics.DrawImage(behind, -Left, -Top);
    behind.Dispose();
}

I also think that using this.Width / this.Height instead of Parent.Width / Parent.Height would be even faster, but I didn't have time to tinker with it.

like image 31
Rubem Pechansky Avatar answered Oct 11 '22 17:10

Rubem Pechansky