Public Class GlassLabel
Inherits System.Windows.Forms.Label
End Class
I made two small changes to the standard behaviour of the Label control. First of all, the Label control is auto-sized by default. But for my control, the background is very important, as it admits gradients, so I decided to remove the auto-size capability of my control. This is done overriding the AutoSize property:
'The AutoSize property has been overridden to achieve the control
'not to be auto-sized.
Public Overrides Property AutoSize() As Boolean
Get
Return MyBase.AutoSize
End Get
Set(ByVal value As Boolean)
MyBase.AutoSize = False
End Set
End Property
The second change that I made was remove the possibility to place the text anywhere on the control (TextAlign), so in my control the text will be always horizontally and vertically centered.
'The TextAlign property has been overridden to achieve the control text
'be aligned always at Middle-Center.
Public Overrides Property TextAlign() As System.Drawing.ContentAlignment
Get
Return MyBase.TextAlign
End Get
Set(ByVal value As System.Drawing.ContentAlignment)
MyBase.TextAlign = ContentAlignment.MiddleCenter
End Set
End Property
This last is not really necessary, because I do all the painting over the control and so I decide exactly where the text appears. In fact, this change is useful only in the Properties Window, where you'll see always TopCenter in the TextAlign property and you will not be able to change it. But even without this piece of code the control will look exactly the same.
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
'All the painting work is done here.
...
End Sub
Dim gradBrush As LinearGradientBrush = New LinearGradientBrush(rectangle, color1, color2, direction)
Graphics.FillRectangle(gradBrush, rectangle)
Of course, you can use your gradient brush with rectangles, ellipses, polygons, regions, paths, etc.
Dim path As GraphicsPath = New GraphicsPath
path.AddString(text, fontFamily, fontStyle, emSize, clippingRect, stringFormat)
Graphics.DrawPath(pen, path)
Graphics.FillPath(brush, path)
In the complete listing of GlassLabel control you'll find some code in which I use GraphicsPath, and you'll see how to use them.
Graphics.ScaleTransform(1, -1)
After this point, all that I paint over this graphics objects (text, shapes, etc) will appear vertically reflected.
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging
Public Class GlassLabel
Inherits System.Windows.Forms.Label
'Variables to hold properties values
'Each initialization values represent the default value for the property
Private m_BackGradientColor1 As Color = Color.Black
Private m_BackGradientColor2 As Color = Color.White
Private m_BackGradientMode As LinearGradientMode = LinearGradientMode.Horizontal
Private m_BackGradient As Boolean = False
Private m_Alpha As Integer = 100
Private m_ForeGradientColor1 As Color = Color.Purple
Private m_ForeGradientColor2 As Color = Color.White
Private m_ForeGradientMode As LinearGradientMode = LinearGradientMode.Vertical
Private m_ForeGradient As Boolean = False
Private m_OffsetY As Integer = 0
Private m_OutlineColor As Color = Color.White
Private m_OutlineWidth As Integer = 0
'This property get/set text outline border color
Public Property OutlineColor() As Color
Get
Return m_OutlineColor
End Get
Set(ByVal value As Color)
m_OutlineColor = value
Me.Invalidate()
End Set
End Property
'This property get/set text outline border width
'If set to zero, then it's not outline
Public Property OutlineWidth() As Integer
Get
Return m_OutlineWidth
End Get
Set(ByVal value As Integer)
m_OutlineWidth = value
Me.Invalidate()
End Set
End Property
'This property get/set an integer value that represents
'the number of pixels that must close up the normal text and
'the reflected text. As MeasureString considers special characters
'and glyphos, it returns vertically an extra space. The OffsetY
'property tells the control to dispose N pixels of space between
'the drawn texts.
Public Property OffsetY() As Integer
Get
Return m_OffsetY
End Get
Set(ByVal value As Integer)
m_OffsetY = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating if draw the texts
'using a gradient fill (true) or a solid fill (false)
Public Property ForeGradient() As Boolean
Get
Return m_ForeGradient
End Get
Set(ByVal value As Boolean)
m_ForeGradient = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating the direction of
'the texts gradient fill
Public Property ForeGradientMode() As LinearGradientMode
Get
Return m_ForeGradientMode
End Get
Set(ByVal value As LinearGradientMode)
m_ForeGradientMode = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating the second color of
'the texts gradient fill
Public Property ForeGradientColor2() As Color
Get
Return m_ForeGradientColor2
End Get
Set(ByVal value As Color)
m_ForeGradientColor2 = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating the first color of
'the texts gradient fill
Public Property ForeGradientColor1() As Color
Get
Return m_ForeGradientColor1
End Get
Set(ByVal value As Color)
m_ForeGradientColor1 = value
Me.Invalidate()
End Set
End Property
'This property get/set a value that represents the level of
'transparency in the reflected text. Values must be between 0 and 255.
'The lower value, reflected text is more transparent.
'The higher value, more opaque.
Public Property Alpha() As Integer
Get
Return m_Alpha
End Get
Set(ByVal value As Integer)
m_Alpha = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating if draw the control's background
'using a gradient fill (true) or a solid fill (false)
Public Property BackGradient() As Boolean
Get
Return m_BackGradient
End Get
Set(ByVal value As Boolean)
m_BackGradient = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating the direction of
'the control's background gradient fill
Public Property BackGradientMode() As LinearGradientMode
Get
Return m_BackGradientMode
End Get
Set(ByVal value As LinearGradientMode)
m_BackGradientMode = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating the second color of
'the control's background gradient fill
Public Property BackGradientColor2() As Color
Get
Return m_BackGradientColor2
End Get
Set(ByVal value As Color)
m_BackGradientColor2 = value
Me.Invalidate()
End Set
End Property
'This property get/set a value indicating the first color of
'the control's background gradient fill
Public Property BackGradientColor1() As Color
Get
Return m_BackGradientColor1
End Get
Set(ByVal value As Color)
m_BackGradientColor1 = value
Me.Invalidate()
End Set
End Property
'The AutoSize property has been overridden to achieve the control
'not to be auto-sized.
Public Overrides Property AutoSize() As Boolean
Get
Return MyBase.AutoSize
End Get
Set(ByVal value As Boolean)
MyBase.AutoSize = False
End Set
End Property
'The TextAlign property has been overridden to achieve the control text
'be aligned always at Middle-Center.
Public Overrides Property TextAlign() As System.Drawing.ContentAlignment
Get
Return MyBase.TextAlign
End Get
Set(ByVal value As System.Drawing.ContentAlignment)
MyBase.TextAlign = ContentAlignment.MiddleCenter
End Set
End Property
'All the paintint work is done here.
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
With e.Graphics
'*** Apply high-quality properties to the graphics object ************************
.CompositingQuality = CompositingQuality.HighQuality
.InterpolationMode = InterpolationMode.HighQualityBicubic
.PixelOffsetMode = PixelOffsetMode.HighQuality
.SmoothingMode = SmoothingMode.HighQuality
.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias
'*** Draw the control's background ***********************************************
If Me.BackGradient Then
'The background must show a gradient, so we need to create a gradient brush
'and fill the control's background rectangle with this gradient
Using bkg As LinearGradientBrush = New LinearGradientBrush(e.ClipRectangle, Me.BackGradientColor1, Me.BackGradientColor2, Me.BackGradientMode)
.FillRectangle(bkg, e.ClipRectangle)
End Using
Else
'The background is a solid color. The Clear method of the Graphics object
'let us to choose a color to clear the background.
.Clear(Me.BackColor)
End If
'*********************************************************************************
'*** Create and draw the normal (not reflected) text *****************************
'Get the text width & height
Dim width As Single = .MeasureString(Me.Text, Me.Font).Width
Dim height As Single = .MeasureString(Me.Text, Me.Font).Height
'Create a rectangle that delimites the position & size of the text drawn
'The x position must be (control width - text width) / 2, so the text will be
'horizontally centered
Dim xpos As Single = (e.ClipRectangle.Width - width) / 2
'The y position must be also vertically centered, so we start from
'(control height - text height) / 2
'But below the normal text will be the reflected text, so must offset to top
'the half of text height
'Additionally, MeasureString give us extra space reserved for tall glyphos,
'so must consider the OffsetY value to delete this extra space, so must offset
'to bottom the half of OffsetY value
Dim ypos As Single = ((e.ClipRectangle.Height - height) / 2) - (height / 2) + (Me.OffsetY / 2)
'Finally, create the rectangle from x,y pos and width & height of the text
Dim originalRect As New RectangleF(xpos, ypos, width, height)
'Draw the original string. We'll use a GraphicsPath object instead
'using DrawString directly, because GraphicsPath will let us draw an
'outline border to the text
'Create the path
Dim path As GraphicsPath = New GraphicsPath
'Add the string to the path. Because GraphicsPath's AddString method
'uses emSize (the height of the em square box that bounds the character)
'instead of Point, we must convert out font's Point size to emSize using
'this formula: (Vertical Resolution / 72) * Font's Point Size
path.AddString(Me.Text, Me.Font.FontFamily, Me.Font.Style, (.DpiY / 72) * Me.Font.Size, originalRect, StringFormat.GenericDefault)
'If and outline must be drawn, draw it
If Me.OutlineWidth > 0 Then
Using p As Pen = New Pen(Me.OutlineColor, Me.OutlineWidth)
.DrawPath(p, path)
End Using
End If
'Create the brush to fill the text
Dim fill As Brush
If Me.ForeGradient Then
'Text must be filled with a gradient brush
fill = New LinearGradientBrush(originalRect, Me.ForeGradientColor1, Me.ForeGradientColor2, Me.ForeGradientMode)
Else
'Text must be filled with a solid brush
fill = New SolidBrush(Me.ForeColor)
End If
'Fill the text and destroy the brush
.FillPath(fill, path)
fill.Dispose()
'The GraphicsPath object won't be needed anymore
path.Dispose()
'From this point we must deal with reflected text. So it's a good idea to
'save the current state of our graphics object. What is really saved is the
'state of the objects (transformations applied, etc), not the drawings done
'until here.
Dim state As GraphicsState = .Save
'Reset the transformations done until here so we start from a "fresh clean"
'graphics object state.
.ResetTransform()
'ScaleTransform will set the graphics object into a state in which all the
'drawings done after the instruction will be affected by the scaling done.
'As we use 1 for horizontal value, the drawings will be not changed in the
'horizontal plane. But as we use -1 for the vertical value, all the drawings
'will be vertically inverted (the reflection effect that we want).
.ScaleTransform(1, -1)
'Now, as we did for the normal text, we'll create a rectangle that delimites
'the position and size of the reflected text
'The x-position must not be changed, as it is the same (horizontally centered)
'The y-pos must be vertically centered, so we start from
'(control height - text height) / 2
'From there, as we did with normal text, we must offset the half of the text height
'(in this case, offset to top)
'Also must offset the OffsetY value, to top too
'BUT we must remember that this will be drawn over a transformed (Scaled)
'graphics object, so must invert all signs (for example, offset to bottom instead
'to top)
ypos = (((((e.ClipRectangle.Height - height) / 2) + (height / 2)) * -1) - height) + (Me.OffsetY / 2)
'Create the rectangle
Dim reflectedRect As New RectangleF(xpos, ypos, width, height)
'Create the path to hold the text
Dim reflectedPath As GraphicsPath = New GraphicsPath
'Add the string to the path
reflectedPath.AddString(Me.Text, Me.Font.FontFamily, Me.Font.Style, (.DpiY / 72) * Me.Font.Size, reflectedRect, StringFormat.GenericDefault)
'Draw the outline, if it applies
If Me.OutlineWidth > 0 Then
'Note that we apply alpha transparency to the outline. If not, reflected
'text's outline will appear too much "solid"
Using p As Pen = New Pen(Color.FromArgb(Me.Alpha, Me.OutlineColor), Me.OutlineWidth)
.DrawPath(p, reflectedPath)
End Using
End If
'Create the brush to fill the reflected text
If Me.ForeGradient Then
'We must apply Alpha transparency on both gradient colors
fill = New LinearGradientBrush(reflectedRect, Color.FromArgb(Me.Alpha, Me.ForeGradientColor1), Color.FromArgb(Me.Alpha, Me.ForeGradientColor2), Me.ForeGradientMode)
Else
'Apply Alpha to solid color too
fill = New SolidBrush(Color.FromArgb(Me.Alpha, Me.ForeColor))
End If
'Draw the text (it will be automatically reflected because of the Scale
'transformation applied)
.FillPath(fill, reflectedPath)
'Destroy objects that are no more needed
fill.Dispose()
reflectedPath.Dispose()
'Restore the Graphics object state (eliminate transformations, so if we drew
'anymore from here will not be reflected)
.Restore(state)
'*********************************************************************************
End With
End Sub
End Class
I hope that you find this control to be useful. Feel free to use it in your applications and modify to your convenience.
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (3)
Commented:
Thanks for sharing Roland! =)
~IM
Author
Commented:Commented: