Creating transparent Windows Forms controls.
The transparency feature of the Windows Forms control leaves much to be desired and is a blatant fudge. The control is not really transparent, it just pretends to be by looking at the background of it's parent control and copying the appropriate portion of the image or background onto it's own surface during the OnPaintBackground method.
This means that a "transparent" control placed on top of another on the same parent will in fact obscure the other child controls. Figure 1 shows this effect in action.
Figure1: A supposedly transparent Panel
The panel control to the right of the form obscures the PictureBox control and shows only the background of the parent.
In order to make a truly transparent control we need to do a couple of things. Firstly, it's necessary to change the behaviour of the window by giving it a WS_EX_TRANSPARENT style. This is accomplished by overriding the CreateParams property so that the correct window style is included when the control is instantiated. The listing below shows this property override.
protected override CreateParams CreateParams
{
get
{
CreateParams cp=base.CreateParams;
cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
The second thing we need to do is to invalidate the parent of the control, not the control itself, whenever we need to update the graphics. This ensures that whatever is behind the control gets painted before we need to do our own graphics output. To do this, a routine such as that shown in the following listing is needed.
protected void InvalidateEx()
{
if(Parent==null)
return;
Rectangle rc=new Rectangle(this.Location,this.Size);
Parent.Invalidate(rc,true);
}
Finally, we need to ensure that the background draw routine does not mess up the recently repainted parent-form content by stubbing out the OnPaintBackground method.
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do not allow the background to be painted
}
Now the control is ready for the rest of its modification. It is important to note at this point that transparent controls are not suitable for double buffering using the standard SetStyle method. The memory bitmap which is provided to your code has an opaque background and does not allow the carefully retained parent pixels to show through.
To complete this article, a simple control that does nothing but paint moving ellipses on its surface is shown in the following listing.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace transcontroldemo
{
/// <summary>
/// Summary description for TransPanel.
/// </summary>
public class TransPanel : Panel
{
Timer Wriggler=new Timer();
public TransPanel()
{
//
// TODO: Add constructor logic here
//
Wriggler.Tick+=new EventHandler(TickHandler);
this.Wriggler.Interval=500;
this.Wriggler.Enabled=true;
}
protected void TickHandler(object sender, EventArgs e)
{
this.InvalidateEx();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp=base.CreateParams;
cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
protected void InvalidateEx()
{
if(Parent==null)
return;
Rectangle rc=new Rectangle(this.Location,this.Size);
Parent.Invalidate(rc,true);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do not allow the background to be painted
}
Random r=new Random();
protected override void OnPaint(PaintEventArgs e)
{
int h=this.Height/2;
int w=this.Width/2;
Pen p=new Pen(Color.Black,2);
int x,y;
for(x=0,y=0; x<w; x+=w/10, y+=h/10)
{
e.Graphics.DrawEllipse(p,x+r.Next(10)-5,y+r.Next(10)-5,this.Width-(2*x),this.Height-(2*y));
}
p.Dispose();
}
}
}
Figure 2 shows the transcontroldemo application in action.
Figure 2: A truly transparent control.