Introduction
I know that in the meantime there will be several enhanced texbox controls available in the Internet. But developing my applications I need some functionality that is only available spreaded over several custom controls and not collected into one control. Therefor this control was developed. May be it will fit also your needs.
Background
This control has the functionality as followes:
- Mark, put a small colored triangle in one of the controls edge
- Mark Position, set the edge position
- Context Menu, validation controlled clipboard action cut, copy, paste, delete
- Input Type, No Validation, validate input: Integer or Decimal
If validated: - Decimal Places, number of decimal digits allowed
- Validate/set input value range Max/Min
- Allow/refuse negativ values
- Beep, if validation error send system beep tone
- Flash, if validation error flash backcolor 3 times
- Menue Item, Textstring of context menu item
Notice:
- The validation of the input is only done for values by key stroke or pasted values from the clipbord. It is not validated if the value comes out of the binding to a datasource column.
- If you set the Input type to Decimal and the decimal places are zero then the input is the same as for setting it to Integer.
- If you paste a value from the clipboard and the Input type is set to Integer or Decimal. Pasted text is filled in as the correct number value. In case of pasted decimal value all decimal digits are rounded to the correct digit places. In case of pasted decimal value and Integer validation is set the decimal digits are rounded to an integer value.
- The source code projects are checked for threads by a malware and antivirus software. Not threads are reported.
Using the code
The 1st ZIP File contains a project with two sub projects. The controls project and a windows forms test project. The projects are developed with VS2012 .NET 4.5 in VB.NET. The 2nd one includes only the controls DLL FIle.
To use the Project unzip the file and start it with Visual Studio (VS).
To use the control save the DLL in the controls project bin/Debug folder to the place you want and implement it in the VS Toolbox as usual. Drag the control from the Toolbox onto the windows form in designer mode.
The controls project contains three classes. One for the control itself (main class clsAdvTexbox.vb) and one for the used context menu (clsContextMenu.vb) and the third one is a helper class (clsToolBoxIcon.vb).
We need an own context menu to have the possibility to validate wether the pasted text from the clipboard is valid.
To have all properties of a standard textbox control available in the property grid the control is not build from a usercontrol. Instead of this it is build by inheritance.
Public Class advancedTexbox
Inherits TextBox
All custom properties
are well documented like this (think of not to forget imports System.ComponentModel):
<Browsable(True), DisplayName("Triangle Position"), _
Description("Choose Triangle Position"), _
Category("custom Properties")> _
Public Property TriPosition As _TriPos
....
Browsable, show/hide the property in the property grid.
DisplayName, Show this text in the property grid to identify the property.
Description, Show this text in the property grid footer.
Category, Group this property if the property grids grouping is activated.
The Visual Studio ToolBox Bug (clsTexBoxIcon)
Up from the fist version of Visual Studio the development system has problems to display toolbox icons of custom controls correct inside the toolbox. If you look into the VS Help you will be told to enhance the class statement like this:
<Toolboxitem("name.bmp"> _
class form1
But in most cases this will not work. But there is a work around by adding the class "clsToolBoxIcon.vb" to your Project.
<Serializable()> Friend Class TheToolboxItem
Inherits ToolboxItem
Public Sub New(ByVal oType As Type)
MyBase.New(oType)
End Sub
Public Overrides Sub Initialize(ByVal oType As Type)
If Not oType.Equals(GetType(advancedTexbox)) Then
String.Format(CultureInfo.CurrentCulture, _
"constructor {0} must be {1}.",
Me.GetType().FullName, GetType(advancedTexbox).FullName))
End If
MyBase.Initialize(oType)
End Sub
End Class
Use this class as it is. The only thing to do is to change the references to the class of the custom control. To do this fill in the GetType Statement the name of the class (see bold text above).
Next you have to change your custom control class statement as followes:
<ToolboxItem(True)> <ToolboxBitmap(GetType(TheToolboxItem), _
"numTextbox.bmp")> Public Class advancedTexbox
Inherits TextBox
Do not forget to create the icon in bmp format by 16x16 pixel and to place the file in the project.
clsContextMenu
This class creates a context menu with the menu items cut, copy, paste and delete. There is nothing special so it is not described here. The context menue is a nested control of the main custom TextBox control. Therefor we need some custom properties "Menueitem Cut" a.s.o. to lay out the menue items text property.
clsAdvTextBox
The code is well documented and in most cases intuitive readable also for beginners. The parts of the source code are segmented by regions for better readability. The heavy code parts are described below.
Draw a mark (triangle) ontop of the control in one of the controls edge.
Sometimes it might be useful to mark the TextBox to show the user that this is a special TextBox. For example that the TextBox input is controlled or it's input is mandatory. To draw the mark (colored triangle, see picture above) we have to capture a windows event (WndProc) because the standard TextBox does not have a paint event.
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = &HF And Not Triangle Is Pens.Transparent Then
Dim x2, y2 As Integer
Select Case TriPosition
Case _TriPos.Bottom_Left
x2 = 6 : y2 = Me.Height - 6
Case _TriPos.Bottom_Right
x2 = Me.Width - 6 : y2 = Me.Height - 6
Case _TriPos.Top_Left
x2 = 6 : y2 = 6
Case _TriPos.Top_Right
x2 = Me.Width - 6 : y2 = 6
End Select
Dim points(3) As Point
points(0) = New Point(x2 - 6, y2)
points(1) = New Point(x2, y2 - 4)
points(2) = New Point(x2, y2)
points(3) = New Point(x2 - 6, y2)
Dim g As Graphics = Graphics.FromHwnd(m.HWnd)
g.DrawPolygon(Triangle, points)
g.Dispose()
End If
End Sub
The color of the traingle can be choosen by the custom property TriangleColor, the position can be choosen by the custom property TriPosition. If the choosen color is transparent no triangle is drawn. To keep all elements of the control (text and triangle) tidy we have to call the me.refresh method to force the control to redraw if the controls text changes. We do that in the controls Me.TextChanged event.
Validate the input
To control the input we use the controls Me.KeyPress event:
Private Sub numericTexbox_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
If InputType = _IType.NoValidation Then Return
If e.KeyChar = CChar(ChrW(Keys.Back)) Or e.KeyChar = CChar(ChrW(Keys.Delete)) And e.KeyChar <> GroupeSeparator Then
Return
End If
If Range Then
If Char.IsDigit(e.KeyChar) Then
Dim strTemp As String = Me.Text + e.KeyChar
If Val(strTemp) >= RangeMin And Val(strTemp) <= RangeMax Then Return
End If
Else
If _InputType = 2 And _decimals > 0 And Char.IsDigit(e.KeyChar) Then
Dim temp() = Me.Text.Split(DecSeparator)
If temp.Length = 2 Then
If temp(1).Length <= _decimals - 1 Then Return
ElseIf temp.Length = 1 Then
Return
End If
ElseIf _InputType = 2 And e.KeyChar = DecSeparator AndAlso (Not Me.Text.Contains(DecSeparator)) Then
Return
ElseIf _InputType = 2 And _decimals = 0 Then
_InputType = 1
End If
If _InputType = 1 And Char.IsDigit(e.KeyChar) Then
DecimalPlaces = 0
Return
End If
If Me.Text.Length = 0 And e.KeyChar = "-" And _negativ And (Not Me.Text.Contains(DecSeparator)) Then Return
End If
e.Handled = True If Flash Then timerFlash.Enabled = e.Handled
If e.Handled And BP Then Beep()
End Sub
Validate clipboard actions
To control the clipboard actions we have to strip off the standard texbox context menu. This is done by setting the controls property me.shortcutsEnabled to false. We do this in the controls New() event. The definition of the custom context menu is outsourced in the separate class clsContextMenu for more transparency. If a Menuitem is clicked the custom event CM_MenueItem of the class clsContextMenu is fired to transfer the clicked item information into the controls main class clsAdvTextbox.
class clsContextMenu:
Public Class cMenue
Inherits ContextMenuStrip
Friend WithEvents mnCut As System.Windows.Forms.ToolStripMenuItem
Friend WithEvents mnCopy As System.Windows.Forms.ToolStripMenuItem
Friend WithEvents mnPaste As System.Windows.Forms.ToolStripMenuItem
Friend WithEvents ToolStripSeparator1 As System.Windows.Forms.ToolStripSeparator
Friend WithEvents mnDel As System.Windows.Forms.ToolStripMenuItem
Public Event MenueItemClicked(ByVal sender)
...
Private Sub mnCopy_Click(sender As Object, e As EventArgs) Handles mnCopy.Click
RaiseEvent MenueItemClicked(sender)
End Sub
Private Sub mnCut_Click(sender As Object, e As EventArgs) Handles mnCut.Click
RaiseEvent MenueItemClicked(sender)
End Sub
Private Sub mnDel_Click(sender As Object, e As EventArgs) Handles mnDel.Click
RaiseEvent MenueItemClicked(sender)
End Sub
Private Sub mnPaste_Click(sender As Object, e As EventArgs) Handles mnPaste.Click
RaiseEvent MenueItemClicked(sender)
End Sub
class clsAdvTextBox:
Private Sub CM_MenueItemClicked(sender As Object) Handles CM.MenueItemClicked
Select Case sender.name
Case "mnCut"
Clipboard.SetText(Me.SelectedText)
Me.SelectedText = ""
Case "mnCopy"
If Me.SelectedText = "" Then
Clipboard.SetText(Me.Text)
Else
Clipboard.SetText(Me.SelectedText)
End If
Case "mnPaste"
If InputType <> _IType.NoValidation Then
If IsNumeric(Clipboard.GetText) Then
If InputType = _IType.Integer Then
Me.Text = CInt(Clipboard.GetText)
ElseIf InputType = _IType.Decimal Then
If DecimalPlaces = 0 Then
Me.Text = CInt(Clipboard.GetText)
Else
Me.Text = Math.Round(CDbl(Clipboard.GetText), DecimalPlaces)
End If
End If
Else
If Flash Then timerFlash.Enabled = True
If BP Then Beep()
End If
Else
If Me.SelectedText = "" Then
Me.SelectedText = Clipboard.GetText()
Else
Me.Text = Clipboard.GetText
End If
End If
Case "mnDel"
Me.SelectedText = ""
End Select
End Sub
By the way, to avoid flickering of the triangle mark or the backcolor flashing it is recomended to set the parent forms property
DoubleBufferd to true!
History
May 7th 2014 -11:15
- Corrected some missing comments and text errors.
- recompiled and rebuild ZIP files.
- Added description for the Toolbox icon workaround
- Added Toolbox icon workaround class
- Corrections of localization, added properties for text of context menue items.