Introduction
The main purpose of this article is to show how to build your own Transparent Control in VB.NET by extending the control through inheritance.
Also, the class might be used as a base class to build your own custom transparent controls such as PictureBox
, Button
, Panel
, .....etc.
Background
Since the Windows Form Control is the only control that supports transparency through its Opacity
property, I decided to utilize the same strategy to build a control that supports transparency.
I saw so many articles talks about the transparency of the control, and I had studied most of the techniques which discuss the same issue and some of these techniques are listed below:
- To call
InvokePaintBackground
of the control’s parent and override the OnPaintBackground
event.
- To draw image which supports transparency to avoid the issues due to double buffering and flickering?
- To override
CreateParams
property, set ExStyle
value to support transparency and on OnPaintBackground
event, fill the control’s ClientRectangle
with a transparent color.
Control's Behavior
According to MSDN, the control class owns three methods which allow setting or checking the behavior of the control:
SetStyle
method: Used to set the ControlStyles
bit to a specified value either to True
or False
GetStyle
method: Used to retrieve the ControlStyles
bit
UpdateStyles
method: Used to force the assigned ControlStyles
bit to be reapplied to the control, as this method will call CreateParams
method to get the ControlStyles
bits to apply to ExStyle
and Style
ControlStyles
Enum: Used with SetStyle
method to specify the style and the behavior of the control
Since UpdateStyles
method allows us to change the styles in CreateParams
method, I believe we shall not override the CreateParams
method at all. But sometimes, we may need to do that based on code requirements.
For further details about SetStyle
, ControlStyles Enum
, GetStyle
and UpdateStyles
, you may check MSDN.
Using the Code
The concepts behind the transparent control are categorized as follows:
- Use the
SetStyle
and UpdateStyles
methods to change the styles in CreateParams
method, which can be done in Control Constructor:
#Region " Constructor "
Public Sub New()
SetStyle(ControlStyles.SupportsTransparentBackColor, True)
SetStyle(ControlStyles.Opaque, False)
SetStyle(ControlStyles.DoubleBuffer, True)
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
SetStyle(ControlStyles.UserPaint, True)
UpdateStyles()
End Sub
#End Region
BackColor
property: Overrides the control’s BackColor
property, sets its value to Color.Transparent
and uses attributes to hide the property.
<System.ComponentModel.Browsable(False)> _
<System.ComponentModel.EditorBrowsable_
(System.ComponentModel.EditorBrowsableState.Never)> _
<System.ComponentModel.DefaultValue(GetType(Color), "Transparent")> _
<System.ComponentModel.Description("Set background color.")> _
<System.ComponentModel.Category("Control Style")> _
Public Overrides Property BackColor() As System.Drawing.Color
Get
Return m_backcolor
End Get
Set(ByVal value As System.Drawing.Color)
m_backcolor = value
Refresh()
End Set
End Property
Opacity
property: This property will be used to set the opacity percentage of the control and we shall use UpdateStyles
method within this property to set the control behavior as explained before. Also, I was about to write a class to convert the opacity value to and from a string
, but in .NET, there is a built in OpacityConverter
class, just saved some time.
<System.ComponentModel.Browsable(True)> _
<System.ComponentModel.EditorBrowsable_
(System.ComponentModel.EditorBrowsableState.Always)> _
<System.ComponentModel.DefaultValue(1.0R)> _
<System.ComponentModel.TypeConverter(GetType(OpacityConverter))> _
<System.ComponentModel.Description_
("Set the opacity percentage of the control.")> _
<System.ComponentModel.Category("Control Style")> _
Public Overridable Property Opacity() As Double
Get
Return m_opacity
End Get
Set(ByVal value As Double)
If value = m_opacity Then
Return
End If
m_opacity = value
UpdateStyles()
Refresh()
End Set
End Property
Transparent
property: Indicates if the control should be transparent or not. When the value is set to false
, the transparency of the control will be skipped, and the control behaves as normal.
<System.ComponentModel.Browsable(True)> _
<System.ComponentModel.EditorBrowsable_
(System.ComponentModel.EditorBrowsableState.Always)> _
<System.ComponentModel.DefaultValue(GetType(Boolean), "False")> _
<System.ComponentModel.Description("Enable control trnasparency.")> _
<System.ComponentModel.Category("Control Style")> _
Public Overridable Property Transparent() As Boolean
Get
Return m_transparent
End Get
Set(ByVal value As Boolean)
If value = m_transparent Then
Return
End If
m_transparent = value
Refresh()
End Set
End Property
TransparentColor
property: This property will be used to set the fill color which will be used to fill and draw the Control’s ClientRectangle
:
<System.ComponentModel.Browsable(True)> _
<System.ComponentModel.EditorBrowsable_
(System.ComponentModel.EditorBrowsableState.Always)> _
<System.ComponentModel.DefaultValue(GetType(Color), "DodgerBlue")> _
<System.ComponentModel.Description("Set the fill color of the control.")> _
<System.ComponentModel.Category("Control Style")> _
Public Overridable Property TransparentColor() As Color
Get
Return m_transparentColor
End Get
Set(ByVal value As Color)
m_transparentColor = value
Refresh()
End Set
End Property
Draw the Control's Transparency
The trick behind transparency is to paint the control's ClientRectangle
with a SolidBrush
where its value will be set using BackColor
property:
Using sb As New SolidBrush(control.BackColor)
g.FillRectangle(sb, control.ClientRectangle)
sb.Dispose()
Then we shall fill the control’s clientRectangle
with another SolidBrush
where its value will be set by using TransparentColor
property and Opacity
property:
Using sbt As New SolidBrush(Color.FromArgb_
(control.Opacity * 255, control.TransparentColor))
g.FillRectangle(sbt, control.ClientRectangle)
sbt.Dispose()
End Using
Draw the Control’s Background
method:
Public Overridable Sub DrawBackground_
(ByVal g As Graphics, ByVal control As TransparentControl)
If Transparent Then
Using sb As New SolidBrush(control.BackColor)
g.FillRectangle(sb, control.ClientRectangle)
sb.Dispose()
Using sbt As New SolidBrush(Color.FromArgb_
(control.Opacity * 255, control.TransparentColor))
g.FillRectangle(sbt, control.ClientRectangle)
sbt.Dispose()
End Using
End Using
Else
Using sb As New SolidBrush(control.TransparentColor)
g.FillRectangle(sb, control.ClientRectangle)
sb.Dispose()
End Using
End If
End Sub
We can draw whatever we want based on our requirements and pass the code to the OnPaint
event, for example let us add borders to the control and for that, there are two additional functions which will be used to make the value of TransparentColor
property darker or lighter, those two functions are also connected with the opacity
property:
Private Function GetLightColor(ByVal colorIn As Color, _
ByVal percent As Single) As Color
If percent < 0 OrElse percent > 100 Then
Throw New ArgumentOutOfRangeException("percent must be between 0 and 100")
End If
Dim a As Int32 = colorIn.A * Me.Opacity
Dim r As Int32 = colorIn.R + CInt(((255 - colorIn.R) / 100) * percent)
Dim g As Int32 = colorIn.G + CInt(((255 - colorIn.G) / 100) * percent)
Dim b As Int32 = colorIn.B + CInt(((255 - colorIn.B) / 100) * percent)
Return Color.FromArgb(a, r, g, b)
End Function
Private Function GetDarkColor(ByVal colorIn As Color, _
ByVal percent As Single) As Color
If percent < 0 OrElse percent > 100 Then
Throw New ArgumentOutOfRangeException("percent must be between 0 and 100")
End If
Dim a As Int32 = colorIn.A * Me.Opacity
Dim r As Int32 = colorIn.R - CInt((colorIn.R / 100) * percent)
Dim g As Int32 = colorIn.G - CInt((colorIn.G / 100) * percent)
Dim b As Int32 = colorIn.B - CInt((colorIn.B / 100) * percent)
Return Color.FromArgb(a, r, g, b)
End Function
Draw the Control’s Border
method:
Public Overridable Sub DrawBorder(ByVal g As Graphics, _
ByVal control As TransparentControl)
Using p As New Pen(GetDarkColor(Me.TransparentColor, 40), 1)
Dim rect As New Rectangle(ClientRectangle.X, ClientRectangle.Y, _
ClientRectangle.Width - 1, ClientRectangle.Height - 1)
rect.Inflate(-1, -1)
g.DrawRectangle(p, rect)
p.Dispose()
End Using
Now let us pass both DrawBackground
and DrawBorder
methods to the Control’s OnPaint
event to finish the work:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
DrawBackground(e.Graphics, Me)
DrawBorder(e.Graphics, Me)
End Sub
I believe we can use the same technique to draw anything inside the control and the same shall supports opacity. Also we can use the same strategy to build some other translucent controls.
The control also supports smart tags technology. For further details about how to add smart tags to the control, you may check MSDN.
Finally, just build the control and use it in your form.
Note
The source code was written with Visual Studio 2008, .NET Framework 3.5.
History
- 10th October, 2009: First release
- 13th October, 2009: Version 1.1