' ***********************************************************************
' Author : Elektro
' Last Modified On : 04-22-2014
' ***********************************************************************
' <copyright file="ControlHintHelper.vb" company="Elektro Studios">
' Copyright (c) Elektro Studios. All rights reserved.
' </copyright>
' ***********************************************************************
#Region " Imports "
Imports System.Reflection
#End Region
''' <summary>
''' Helper Class that manages text-hints for Edit controls.
''' </summary>
Friend NotInheritable Class ControlHintHelper
#Region " Object members "
''' <summary>
''' Collection of currentlly handled controls and it's text-hint properties.
''' </summary>
Public Shared ControlHints
As New Dictionary(Of Control, HintProperties
)
''' <summary>
''' Collection of currentlly handled controls and it's default property values.
''' </summary>
Private Shared ControlHintsDefaults
As New Dictionary(Of Control, HintProperties
)
#End Region
#Region " Properties "
''' <summary>
''' Determines a text-hint and it's personalization properties.
''' </summary>
Public Class HintProperties
''' <summary>
''' Gets or sets the text-hint type.
''' </summary>
''' <value>The Hint type.</value>
Public Property HintType As HintType = ControlHintHelper.HintType.Normal
''' <summary>
''' Gets or sets the text-hint.
''' </summary>
''' <value>The hint-text.</value>
Public Property Text As String = String.Empty
''' <summary>
''' Gets or sets the text-hint font.
''' </summary>
''' <value>The text font.</value>
Public Property Font As Font = Nothing
''' <summary>
''' Gets or sets the text-hint color.
''' </summary>
''' <value>The text color.</value>
Public Property ForeColor As Color = Color.Empty
End Class
#End Region
#Region " Enums "
''' <summary>
''' Specifies the posssible Hint types.
''' </summary>
Public Enum HintType As Integer
''' <summary>
''' The Hint is removed when the Control gets focus.
''' </summary>
Normal = 0
''' <summary>
''' The Hint is not removed when the Control gets focus.
''' </summary>
Persistent = 1
End Enum
#End Region
#Region " Constructors "
''' <summary>
''' Prevents a default instance of the <see cref="ControlHintHelper" /> class from being created.
''' </summary>
Private Sub New()
End Sub
#End Region
#Region " Public Methods "
''' <summary>
''' Sets a new text-hint for an specific control.
''' </summary>
''' <param name="Control">Indicates the control.</param>
''' <param name="HintProperties">TheIndicates the text-hint properties.</param>
''' <exception cref="Exception">Text hint can't be null or empty.</exception>
Public Shared Sub SetHint(ByVal [Control] As Control,
ByVal [HintProperties] As HintProperties)
' Ensure and fix empty hint properties.
With [HintProperties]
If String.IsNullOrEmpty(.Text) Then
Throw New Exception("Text hint can't be null or empty.")
Exit Sub
End If
If .Font Is Nothing Then
.Font = GetPropertyValue([Control], "Font")
End If
If .ForeColor = Color.Empty Then
.ForeColor = GetPropertyValue([Control], "ForeColor")
End If
End With
' Set the default control property values to restore them when needed.
Dim Deaults As New HintProperties
With Deaults
.Text = String.Empty
.Font = GetPropertyValue([Control], "Font")
.ForeColor = GetPropertyValue([Control], "ForeColor")
End With
' Ready to handle the Control.
Select Case ControlHints.ContainsKey([Control])
Case True ' Control-hint is already set and handled.
' Just replace the new hint properties in the collection.
ControlHints([Control]) = [HintProperties]
Case False ' Control-hint is not set.
' Add the hint properties into collections.
ControlHints.Add([Control], [HintProperties])
ControlHintsDefaults.Add([Control], Deaults)
With [Control]
' Remove previouslly handled events (if any).
RemoveHandler .HandleCreated, AddressOf Control_HandleCreated
RemoveHandler .Enter, AddressOf Control_Enter
RemoveHandler .Leave, AddressOf Control_Leave
RemoveHandler .Disposed, AddressOf Control_Disposed
RemoveHandler .MouseDown, AddressOf Control_MouseDown
RemoveHandler .KeyDown, AddressOf Control_KeyDown
' Start handling the control events.
AddHandler .HandleCreated, AddressOf Control_HandleCreated
AddHandler .Enter, AddressOf Control_Enter
AddHandler .Leave, AddressOf Control_Leave
AddHandler .Disposed, AddressOf Control_Disposed
AddHandler .MouseDown, AddressOf Control_MouseDown
AddHandler .KeyDown, AddressOf Control_KeyDown
End With
End Select
End Sub
''' <summary>
''' Sets a new text-hint for various controls.
''' </summary>
''' <param name="Controls">Indicates the controls.</param>
''' <param name="HintProperties">TheIndicates the text-hint properties.</param>
Public Shared Sub SetHint(ByVal Controls As Control(),
ByVal [HintProperties] As HintProperties)
For Each [Control] As Control In Controls
SetHint([Control], [HintProperties])
Next [Control]
End Sub
''' <summary>
''' Removes a text-hint from a control.
''' </summary>
''' <param name="Control">Indicates the control.</param>
Public Shared Sub RemoveHint(ByVal [Control] As Control)
Select Case ControlHints.ContainsKey([Control])
Case True ' Control-hint is already set.
' Remove events from event handlers.
With [Control]
RemoveHandler .HandleCreated, AddressOf Control_HandleCreated
RemoveHandler .Enter, AddressOf Control_Enter
RemoveHandler .Leave, AddressOf Control_Leave
RemoveHandler .Disposed, AddressOf Control_Disposed
RemoveHandler .MouseDown, AddressOf Control_MouseDown
RemoveHandler .KeyDown, AddressOf Control_KeyDown
End With
If Not [Control].IsDisposed Then ' If the Control is not disposed then...
If [Control].Text.Trim = ControlHints([Control]).Text.Trim Then
' Restore it's default property values and clear the control text.
RestoreProperties([Control], ControlHintsDefaults([Control]))
Else
' Restore it's default property values and skip the control text.
RestoreProperties([Control], ControlHintsDefaults([Control]), SkipProperties:={"Text"})
End If
End If
' Remove the control-hints from hints collections.
ControlHints.Remove([Control])
ControlHintsDefaults.Remove([Control])
End Select
End Sub
''' <summary>
''' Removes a text-hint from a control.
''' </summary>
''' <param name="Controls">Indicates the controls.</param>
Public Shared Sub RemoveHint(ByVal Controls As Control())
For Each [Control] As Control In Controls
RemoveHint([Control])
Next [Control]
End Sub
#End Region
#Region " Private Methods "
''' <summary>
''' Gets the property value of an specific control through reflection.
''' </summary>
''' <param name="Control">Indicates the Control.</param>
''' <param name="PropertyName">Indicates the name of the property to get it's value.</param>
''' <returns>If the property is not found on the Control, the return value is 'Nothing',
''' Otherwise, the return value is the Control's property value.</returns>
Private Shared Function GetPropertyValue(ByVal [Control] As Control,
ByVal PropertyName As String) As Object
Dim Prop As PropertyInfo = [Control].GetType.GetProperty(PropertyName)
Select Case Prop Is Nothing
Case True
Return Nothing
Case Else
Return Prop.GetValue([Control], Nothing)
End Select
End Function
''' <summary>
''' Sets the properties of an specific control.
''' </summary>
''' <param name="Control">Indicates the Control.</param>
''' <param name="HintProperties">Indicates the properties to set it's values.</param>
''' <param name="SkipProperties">Indicates the properties to skip.</param>
Private Shared Sub SetProperties(ByVal [Control] As Object,
ByVal HintProperties As HintProperties,
Optional ByVal SkipProperties As String() = Nothing)
' Get the control type.
Dim ControlType As Type = [Control].GetType
Dim ExcludeProperties As String() = If(SkipProperties Is Nothing, {""}, SkipProperties)
' Loop through the 'HintProperties' properties to determine whether exist all the needed properties in the Control.
For Each HintProperty As PropertyInfo In HintProperties.GetType.GetProperties
Dim ControlProperty As PropertyInfo = ControlType.GetProperty(HintProperty.Name)
If Not ControlProperty Is Nothing AndAlso Not ExcludeProperties.Contains(ControlProperty.Name) Then
ControlProperty.SetValue([Control], HintProperty.GetValue(HintProperties, Nothing), Nothing)
End If
Next HintProperty
End Sub
''' <summary>
''' Restores the properties of an specific control.
''' </summary>
''' <param name="Control">Indicates the Control.</param>
''' <param name="DefaultProperties">Indicates the properties to reset it's values.</param>
''' <param name="SkipProperties">Indicates the properties to skip.</param>
Private Shared Sub RestoreProperties(ByVal [Control] As Object,
ByVal DefaultProperties As HintProperties,
Optional ByVal SkipProperties As String() = Nothing)
Dim ExcludeProperties As String() = If(SkipProperties Is Nothing, {""}, SkipProperties)
' Get the control type.
Dim ControlType As Type = [Control].GetType
' Loop through the 'DefaultProperties' properties to determine whether exist all the needed properties in the Control.
For Each DefaultProperty As PropertyInfo In DefaultProperties.GetType.GetProperties.Reverse
Dim ControlProperty As PropertyInfo = ControlType.GetProperty(DefaultProperty.Name)
If Not ControlProperty Is Nothing AndAlso Not ExcludeProperties.Contains(ControlProperty.Name) Then
ControlProperty.SetValue([Control], DefaultProperty.GetValue(DefaultProperties, Nothing), Nothing)
End If
Next DefaultProperty
End Sub
#End Region
#Region "Event Handlers "
''' <summary>
''' Handles the HandleCreated event of the Control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
Private Shared Sub Control_HandleCreated(ByVal sender As Object, ByVal e As EventArgs)
Dim [Control] As Control = DirectCast(sender, Control)
Dim [HintProperties] As HintProperties = ControlHints([Control])
SetProperties([Control], [HintProperties])
End Sub
''' <summary>
''' Handles the Enter event of the Control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
Private Shared Sub Control_Enter(ByVal sender As Object, ByVal e As EventArgs)
Dim [Control] As Control = DirectCast(sender, Control)
Dim [HintProperties] As HintProperties = ControlHints([Control])
Select Case [HintProperties].HintType
Case HintType.Normal
If [Control].Text.Trim = [HintProperties].Text.Trim Then ' Restore it's default values.
RestoreProperties([Control], ControlHintsDefaults([Control]))
End If
End Select
End Sub
''' <summary>
''' Handles the Leave event of the control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
Private Shared Sub Control_Leave(ByVal sender As Object, ByVal e As EventArgs)
Dim [Control] As Control = DirectCast(sender, Control)
Dim [HintProperties] As HintProperties = ControlHints([Control])
Select Case [HintProperties].HintType
Case HintType.Normal
Select Case [Control].Text.Trim
Case Is = [HintProperties].Text.Trim ' Restore it's default values.
RestoreProperties([Control], ControlHintsDefaults(sender))
Case Is = String.Empty ' Set the hint values.
SetProperties([Control], [HintProperties])
End Select
Case HintType.Persistent
Select Case [Control].Text.Trim
Case Is = String.Empty ' Set the hint values.
SetProperties([Control], [HintProperties])
End Select
End Select
End Sub
''' <summary>
''' Handles the MouseDown event of the control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="MouseEventArgs" /> instance containing the event data.</param>
Private Shared Sub Control_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
Dim [Control] As Control = DirectCast(sender, Control)
Dim [HintProperties] As HintProperties = ControlHints([Control])
Select Case [HintProperties].HintType
Case HintType.Persistent
If [Control].Text.Trim = [HintProperties].Text.Trim Then
' Get the 'Select' Control's Method (if exist).
Dim Method = sender.GetType.GetMethod("Select",
BindingFlags.Public Or BindingFlags.Instance Or BindingFlags.InvokeMethod,
Nothing,
{GetType(Integer), GetType(Integer)},
Nothing)
If Not Method Is Nothing Then ' Select the zero length.
Method.Invoke(sender, {0I, 0I})
End If
End If
End Select
End Sub
''' <summary>
''' Handles the KeyDown event of the Control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="KeyEventArgs"/> instance containing the event data.</param>
Private Shared Sub Control_KeyDown(sender As Object, e As KeyEventArgs)
Dim [Control] As Control = DirectCast(sender, Control)
Dim [HintProperties] As HintProperties = ControlHints([Control])
Select Case [HintProperties].HintType
Case HintType.Persistent
Select Case [Control].Text.Trim
Case Is = [HintProperties].Text.Trim ' Restore the control values.
RestoreProperties([Control], ControlHintsDefaults([Control]))
Case Is = String.Empty
RestoreProperties([Control], ControlHintsDefaults([Control]), SkipProperties:={"Text"})
End Select
Case HintType.Normal
Select Case [Control].Text.Trim
Case Is = String.Empty ' Restore the control values.
RestoreProperties([Control], ControlHintsDefaults([Control]))
Case Else
' Do nothing.
End Select
End Select
End Sub
''' <summary>
''' Handles the Disposed event of the control.
''' </summary>
''' <param name="sender">The source of the event.</param>
''' <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
Private Shared Sub Control_Disposed(ByVal sender As Object, ByVal e As EventArgs)
RemoveHint(DirectCast(sender, Control))
End Sub
#End Region
End Class