VB.NET Translucent Control using GDI+




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:
  1. SetStyle method: Used to set the ControlStyles bit to a specified value either to True or False
  2. GetStyle method: Used to retrieve the ControlStyles bit
  3. 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
  4. 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()
            ' ......... rest of the code 
            
        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.
         _
         _
         _
         _
         _
        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.
         _
         _
         _
         _
         _
         _
        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.
         _
         _
         _
         _
         _
        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:
        _
         _
         _
         _
         _
        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()

                ' .....rest of the code  
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
' ......rest of the code 
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.

Download

  To download, please visit my articles in CodProject

Comments

Popular posts from this blog

مقدمة الي تشفير الحروف الأبجدية العربية

Add Custom Event to a Class in VB.NET