' ***********************************************************************
' Author : Elektro
' Modified : 02-February-2015
' ***********************************************************************
' <copyright file="ElektroTextBox.vb" company="Elektro Studios">
' Copyright (c) Elektro Studios. All rights reserved.
' </copyright>
' ***********************************************************************
#Region " Imports "
Imports System.ComponentModel
#End Region
''' <summary>
''' An extended TextBox with character validation capabilities.
''' </summary>
Public NotInheritable Class ElektroTextBox : Inherits TextBox
#Region " Properties "
''' <summary>
''' Gets or sets a value indicating whether
''' the default <see cref="ContextMenuStrip"/> for this <see cref="ElektroTextBox"/> instance is disabled.
''' </summary>
''' <value>
''' <c>true</c> if <see cref="ContextMenuStrip"/> is disabled for this <see cref="ElektroTextBox"/> instance;
''' <c>false</c> otherwise.</value>
Public Property DisableEditMenu As Boolean = False
''' <summary>
''' An empty <see cref="ContextMenuStrip"/> that replaces the default <see cref="ContextMenuStrip"/>
''' when <see cref="DisableEditMenu"/> property is set to <c>true</c>.
''' of this <see cref="ElektroTextBox"/> instance.
''' </summary>
Private ReadOnly emptynessContextMenuStrip As ContextMenuStrip
''' <summary>
''' Gets or sets the character-set that contains the allowed characters to fill this <see cref="ElektroTextBox"/> instance.
''' </summary>
''' <value>The character-set that contains the allowed characters to fill this <see cref="ElektroTextBox"/> instance.</value>
<Description("The characters that are allowed to fill this ElektroTextBox")>
Public Property CurrentCharSet As CharSet
Get
Return Me.currentCharSet1
End Get
Set(ByVal value As CharSet)
Me.currentCharSet1 = value
Me.currentCharSetChars1 = Me.GetCharSetChars(value)
End Set
End Property
''' <summary>
''' The character-set that contains the allowed characters to fill this <see cref="ElektroTextBox"/> instance.
''' </summary>
Private currentCharSet1 As CharSet = CharSet.StandardAlphabetic Or CharSet.StandardSymbols Or CharSet.Numeric
''' <summary>
''' Gets the characters of the current character-set.
''' </summary>
''' <value>The characters of the current character-set.</value>
Public ReadOnly Property CurrentCharSetChars As IEnumerable(Of Char)
Get
Return Me.currentCharSetChars1
End Get
End Property
''' <summary>
''' The characters of the current character-set.
''' </summary>
Private currentCharSetChars1 As IEnumerable(Of Char) = Me.GetCharSetChars(Me.currentCharSet1)
''' <summary>
''' Determines whether a pasting operation is requested.
''' </summary>
Private isPasteRequested As Boolean = False
''' <summary>
''' Determines whether the 'Enter' key is requested.
''' </summary>
Private isEnterKeyRequested As Boolean = False
''' <summary>
''' Determines whether the 'Backspace' key is requested.
''' </summary>
Private isBackspacekeyRequested As Boolean = False
''' <summary>
''' Determines whether an unknown key is requested.
''' </summary>
Private isUnknownKeyRequested As Boolean = False
''' <summary>
''' Contains pre-defined <see cref="ElektroTextBox"/> character sets.
''' </summary>
Public NotInheritable Class CharSets
''' <summary>
''' Gets the standard alphabetic character set.
''' </summary>
''' <value>The standard alphabetic character set.</value>
Public Shared ReadOnly Property CharSetStandardAlpha As IEnumerable(Of Char)
Get
Return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
End Get
End Property
''' <summary>
''' Gets the standard symbols character set.
''' </summary>
''' <value>The standard symbols character set.</value>
Public Shared ReadOnly Property CharSetStandardSymbols As IEnumerable(Of Char)
Get
Return """|@·#$~%&¬/\()[]{}<>?!,;.:'¨^-_+=*"
End Get
End Property
''' <summary>
''' Gets the numerical character set.
''' </summary>
''' <value>The numerical character set.</value>
Public Shared ReadOnly Property CharSetNumeric As IEnumerable(Of Char)
Get
Return "1234567890"
End Get
End Property
''' <summary>
''' Gets the Spanish alphabetic character set.
''' </summary>
''' <value>The Spanish alphabetic character set.</value>
Public Shared ReadOnly Property CharSetSpanishAlpha As IEnumerable(Of Char)
Get
Return CharSetStandardAlpha.Concat("ñáéíóúàèìòùäëïöüÑÁÉÍÓÚÀÈÌÒÙÄËÏÖÜ")
End Get
End Property
''' <summary>
''' Gets the Spanish symbols character set.
''' </summary>
''' <value>The Spanish symbols character set.</value>
Public Shared ReadOnly Property CharSetSpanishSymbols As IEnumerable(Of Char)
Get
Return CharSetStandardSymbols.Concat("ºª¿¡`´€")
End Get
End Property
''' <summary>
''' Gets the Catalonian alphabetic character set.
''' </summary>
''' <value>The Catalonian alphabetic character set.</value>
Public Shared ReadOnly Property CharSetCatalonianAlpha As IEnumerable(Of Char)
Get
Return CharSetStandardAlpha.Concat("çáéíóúàèìòùäëïöüÇÁÉÍÓÚÀÈÌÒÙÄËÏÖÜ")
End Get
End Property
''' <summary>
''' Gets the Catalonian symbols character set.
''' </summary>
''' <value>The Catalonian symbols character set.</value>
Public Shared ReadOnly Property CharSetCatalonianSymbols As IEnumerable(Of Char)
Get
Return CharSetSpanishSymbols
End Get
End Property
''' <summary>
''' Gets the roman numerals characters set.
''' </summary>
''' <value>The roman numerals characters set.</value>
Public Shared ReadOnly Property CharSetRomanNumerals As IEnumerable(Of Char)
Get
Return "IVXLCDM"
End Get
End Property
End Class
#End Region
#Region " Enumerations "
''' <summary>
''' Specifies a <see cref="ElektroTextBox"/> character set.
''' These values can be combined.
''' </summary>
<FlagsAttribute>
Public Enum CharSet As Integer
''' <summary>
''' Any character set.
''' This will disable any character typing on the <see cref="ElektroTextBox"/> instance.
''' </summary>
None = 0
''' <summary>
''' Standard alphabetic characters.
''' </summary>
StandardAlphabetic = 1
''' <summary>
''' Standard symbol characters.
''' </summary>
StandardSymbols = 2
''' <summary>
''' Numeric characters.
''' </summary>
Numeric = 4
''' <summary>
''' Spanish alphabetic characters.
''' </summary>
SpanishAlphabetic = 8
''' <summary>
''' Spanish symbol characters.
''' </summary>
SpanishSymbols = 16
''' <summary>
''' Catalonian alphabetic characters.
''' </summary>
CatalonianAlphabetic = 32
''' <summary>
''' Catalonian symbol characters.
''' </summary>
CatalonianSymbols = 64
''' <summary>
''' Roman numerals characters.
''' </summary>
RomanNumerals = 128
End Enum
#End Region
#Region " Constructors "
''' <summary>
''' Initializes a new instance of the <see cref="ElektroTextBox"/> class.
''' </summary>
Public Sub New()
Me.emptynessContextMenuStrip = New ContextMenuStrip
End Sub
#End Region
#Region " Overriden Events "
''' <summary>
''' Raises the <see cref="E:Control.Enter"/> event.
''' </summary>
''' <param name="e">An <see cref="T:EventArgs"/> that contains the event data.</param>
Protected Overrides Sub OnEnter(ByVal e As EventArgs)
Me.ToggleEditMenu(Me.DisableEditMenu)
MyBase.OnEnter(e)
End Sub
''' <summary>
''' Raises the <see cref="E:Control.MouseEnter"/> event.
''' </summary>
''' <param name="e">An <see cref="T:EventArgs" /> that contains the event data.</param>
Protected Overrides Sub OnMouseEnter(ByVal e As EventArgs)
Me.ToggleEditMenu(Me.DisableEditMenu)
MyBase.OnMouseEnter(e)
End Sub
''' <summary>
''' Raises the <see cref="E:Control.KeyDown"/> event.
''' </summary>
''' <param name="e">A <see cref="T:KeyEventArgs"/> that contains the event data.</param>
Protected Overrides Sub OnKeyDown(ByVal e As KeyEventArgs)
If (e.KeyCode = Keys.Enter) Then ' Enter key.
' Let handle the 'Enter' key on 'KeyPress' event for proper evaluation.
Me.isEnterKeyRequested = True
ElseIf (e.KeyCode = Keys.Back) Then ' Backspace key.
' Let handle the 'Enter' key on 'KeyPress' event for proper evaluation..
Me.isBackspacekeyRequested = True
ElseIf (e.KeyCode = Keys.C) AndAlso (e.Modifiers = Keys.Control) Then ' CTRL+C hotkey.
' Allow to copy text.
e.Handled = False
e.SuppressKeyPress = True
MyBase.Copy()
ElseIf (e.KeyCode = Keys.X) AndAlso (e.Modifiers = Keys.Control) Then ' CTRL+X hotkey.
' Allow to cut text.
e.Handled = False
e.SuppressKeyPress = True
MyBase.Cut()
ElseIf (e.KeyCode = Keys.V) AndAlso (e.Modifiers = Keys.Control) Then ' CTRL+V hotkey.
' Let handle the text pasting on 'KeyPress' event for proper character(s) evaluation.
Me.isPasteRequested = True
Else ' Unhandled character.
' Let handle the unknown char on 'KeyPress' event for proper character evaluation.
Me.isUnknownKeyRequested = True
End If
Debug.
WriteLine(String.
Format("Modifiers:{0} KeyCode:{1} KeyData:{2} KeyValue:{3} ",
e.Modifiers.ToString,
e.KeyCode.ToString,
e.KeyData.ToString,
e.KeyValue.ToString))
#End If
MyBase.OnKeyDown(e)
End Sub
''' <summary>
''' Raises the <see cref="E:Control.KeyPress"/> event.
''' </summary>
''' <param name="e">A <see cref="T:KeyPressEventArgs"/> that contains the event data.</param>
Protected Overrides Sub OnKeyPress(ByVal e As KeyPressEventArgs)
If isPasteRequested Then
' Allow to paste text only if all characters are allowed characters.
e.Handled = Not Me.StringHasNonAllowedChars(Clipboard.GetText, Me.currentCharSetChars1)
Me.isPasteRequested = False
ElseIf isBackspacekeyRequested Then
' Allow character deletion.
e.Handled = False
Me.isBackspacekeyRequested = False
ElseIf isEnterKeyRequested Then
' Allow the effects of Enter key.
e.Handled = False
Me.isEnterKeyRequested = False
ElseIf isUnknownKeyRequested Then
' Allow unknown character only if it's an allowed character.
e.Handled = Not Me.StringHasNonAllowedChars({Convert.ToChar(e.KeyChar)}, Me.currentCharSetChars1)
Me.isUnknownKeyRequested = False
End If
MyBase.OnKeyPress(e)
End Sub
''' <summary>
''' Raises the <see cref="E:Control.DragEnter"/> event.
''' </summary>
''' <param name="drgevent">A <see cref="T:DragEventArgs"/> that contains the event data.</param>
Protected Overrides Sub OnDragEnter(ByVal drgevent As DragEventArgs)
If MyBase.AllowDrop Then
Select Case True
Case drgevent.Data.GetDataPresent(DataFormats.Text) ' ANSI text.
drgevent.Effect = DragDropEffects.Copy ' Drop text from dragged source.
Case Else
' Do Nothing.
End Select
End If
MyBase.OnDragEnter(drgevent)
End Sub
''' <summary>
''' Raises the <see cref="E:Control.DragDrop"/> event.
''' </summary>
''' <param name="drgevent">A <see cref="T:DragEventArgs"/> that contains the event data.</param>
Protected Overrides Sub OnDragDrop(ByVal drgevent As DragEventArgs)
If MyBase.AllowDrop Then
Select Case True
Case drgevent.Data.GetDataPresent(DataFormats.Text) ' ANSI text.
Dim dropString As String = DirectCast(drgevent.Data.GetData(DataFormats.Text), String)
' Allow text drop only if all characters are numeric.
If Me.StringHasNonAllowedChars(dropString, Me.currentCharSetChars1) Then
MyBase.Text = dropString
End If
Case Else
' Do Nothing.
End Select
End If
MyBase.OnDragDrop(drgevent)
End Sub
#End Region
#Region " Private Methods "
''' <summary>
''' Toggles the edit menu visibility.
''' </summary>
''' <param name="enable">
''' If set to <c>true</c>, restores the default <see cref="ContextMenuStrip"/> for this <see cref="ElektroTextBox"/> instance.
''' </param>
Private Sub ToggleEditMenu(ByVal enable As Boolean)
If (enable) AndAlso (MyBase.ContextMenuStrip Is Nothing) Then
' Disable default Copy/Cut/Paste contextmenu.
MyBase.ContextMenuStrip = Me.emptynessContextMenuStrip
ElseIf Not (enable) AndAlso (MyBase.ContextMenuStrip IsNot Nothing) Then
' Restore default edit contextmenu.
MyBase.ContextMenuStrip = Nothing
Else
' Do Nothing.
End If
End Sub
''' <summary>
''' Gets the characters of a <see cref="CharSet"/>.
''' </summary>
''' <param name="charSet">The <see cref="CharSet"/>.</param>
''' <returns>The characters of a <see cref="CharSet"/>.</returns>
Private Function GetCharSetChars(ByVal charSet As CharSet) As IEnumerable(Of Char)
Dim chars As IEnumerable(Of Char) = String.Empty
If charSet.HasFlag(charSet.None) Then
' Do Nothing.
End If
If charSet.HasFlag(charSet.StandardAlphabetic) Then
chars = chars.Concat(CharSets.CharSetStandardAlpha)
End If
If charSet.HasFlag(charSet.StandardSymbols) Then
chars = chars.Concat(CharSets.CharSetStandardSymbols)
End If
If charSet.HasFlag(charSet.Numeric) Then
chars = chars.Concat(CharSets.CharSetNumeric)
End If
If charSet.HasFlag(charSet.SpanishAlphabetic) Then
chars = chars.Concat(CharSets.CharSetSpanishAlpha)
End If
If charSet.HasFlag(charSet.SpanishSymbols) Then
chars = chars.Concat(CharSets.CharSetSpanishSymbols)
End If
If charSet.HasFlag(charSet.CatalonianAlphabetic) Then
chars = chars.Concat(CharSets.CharSetCatalonianAlpha)
End If
If charSet.HasFlag(charSet.CatalonianSymbols) Then
chars = chars.Concat(CharSets.CharSetCatalonianSymbols)
End If
If charSet.HasFlag(charSet.RomanNumerals) Then
chars = chars.Concat(CharSets.CharSetRomanNumerals)
End If
Return (From c As Char In chars Order By c.ToString Ascending Distinct)
End Function
''' <summary>
''' Determines whether the specified string has non allowed characters for this <see cref="ElektroTextBox"/> instance.
''' </summary>
''' <param name="characters">The characters that will be evaluated.</param>
''' <param name="allowedChars">The allowed characters.</param>
''' <returns><c>true</c> if all the characters of the specified string satisfy the condition;
''' <c>false</c> otherwise.</returns>
Private Function StringHasNonAllowedChars(ByVal characters As IEnumerable(Of Char),
ByVal allowedChars As IEnumerable(Of Char)) As Boolean
Return characters.All(Function(c As Char)
Return allowedChars.Contains(c, Nothing)
End Function)
End Function
#End Region
End Class