Recently I was working on improving the user interface of our windows mobile application - UnitConverter. It had plain solid background. I wanted to add background image to it. I found this video tutorial on how to display background image on a form. However, that tutorial told us if we need to have transparent controls on top of the image, we have to write our own user controls. That’s how I started researching on how to create transparent controls. I found an article by Per Ola Sæther which laid the foundation of my solution.
The basic ideas to create a transparent control is to override the OnPaintBackgound method of the control so it calls the parent form’s OnPaintBackgound to draw the background and then draw content of the control on top of it.
In that article, the author showed how to create a transparent label control. But the code can be simplified. I also did some refactoring work so it extends to not just label control.
First I created the same interface introduced in that article.
public interface IPaintControl
{
// have the background painted
void InvokePaintBackground(PaintEventArgs e);
}
public class CcForm : Form, IPaintControl
{
public virtual void InvokePaintBackground(PaintEventArgs e)
{
OnPaintBackground(e);
}
}
public class CcTransparentControl : Control
{
private bool _transparentBackgound = true;
public bool TransparentBackground
{
get
{
return _transparentBackgound;
}
set
{
_transparentBackgound = value;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (_transparentBackgound)
{
IPaintControl parent = (IPaintControl)Parent;
if (parent != null)
{
parent.InvokePaintBackground(e);
}
}
else base.OnPaintBackground(e);
}
}
public class CcTransparentLabel : CcTransparentControl
{
private ContentAlignment textAlign = ContentAlignment.TopLeft;
public ContentAlignment TextAlign
{
get
{
return textAlign;
}
set
{
textAlign = value;
}
}
public CcTransparentLabel()
{
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics gfx = e.Graphics;
if (this.TextAlign == ContentAlignment.TopLeft)
{
gfx.DrawString(this.Text, this.Font,
new SolidBrush(this.ForeColor), ClientRectangle);
}
else if (this.TextAlign == ContentAlignment.TopCenter)
{
SizeF size = gfx.MeasureString(this.Text, this.Font);
int left = this.Width / 2 - (int)size.Width / 2;
var rect = new Rectangle(ClientRectangle.Left + left,
ClientRectangle.Top, (int)size.Width,
ClientRectangle.Height);
gfx.DrawString(this.Text, this.Font,
new SolidBrush(this.ForeColor), rect);
}
else if (this.TextAlign == ContentAlignment.TopRight)
{
SizeF size = gfx.MeasureString(this.Text, this.Font);
int left = this.Width - (int)size.Width + this.Left;
var rect = new Rectangle(ClientRectangle.Left + left,
ClientRectangle.Top, (int)size.Width,
ClientRectangle.Height);
gfx.DrawString(this.Text, this.Font,
new SolidBrush(this.ForeColor), rect);
}
}
}
public partial class FormWithSolidColorBackground : CcForm
public partial class FormWithImageBackground : CcForm
{
private Rectangle _backgroundRect;
private Bitmap _background;
private string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase.ToString());
public FormWithImageBackground()
{
InitializeComponent();
_background = new Bitmap(currentPath + @"\ImageBackground.jpg");
_backgroundRect = new Rectangle(0, 0, _background.Width, _background.Height);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawImage(_background, this.ClientRectangle, _backgroundRect, GraphicsUnit.Pixel);
}
}
public interface IPaintControl
{
// have the background painted
void InvokePaintBackground(PaintEventArgs e, Point location);
}
public class CcForm : Form, IPaintControl
{
public virtual void InvokePaintBackground(PaintEventArgs e, Point location)
{
OnPaintBackground(e);
}
}
public class CcTransparentControl : Control
{
....
protected override void OnPaintBackground(PaintEventArgs e)
{
if (_transparentBackgound)
{
IPaintControl parent = (IPaintControl)Parent;
if (parent != null)
{
parent.InvokePaintBackground(e, this.Location);
}
}
else base.OnPaintBackground(e);
}
}
public override void InvokePaintBackground(System.Windows.Forms.PaintEventArgs e, System.Drawing.Point location)
{
Graphics g = e.Graphics;
Rectangle destRect = new Rectangle(-1 * location.X, -1 * location.Y, ClientRectangle.Width, ClientRectangle.Height);
g.DrawImage(_background, destRect, _backgroundRect, GraphicsUnit.Pixel);
}