Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / Visual Basic

advanced TextBox

Rate me:
Please Sign up or sign in to vote.
4.93/5 (5 votes)
7 May 2014CPOL5 min read 30.6K   3.5K   21   1
Inherited Textbox with extended properties and validation control.

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

Image 1

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.

VB.NET
Public Class advancedTexbox
    Inherits TextBox

All custom properties are well documented like this (think of not to forget imports System.ComponentModel):

VB.NET
 <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:

VB.NET
<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.

VB.NET
<Serializable()> Friend Class TheToolboxItem
    Inherits ToolboxItem 
    ' ## change the parameter of the GetType Statement to the name of the usercontrol class 
    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:

VB.NET
<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.

VB.NET
  ' ## draw colored triangle i a corner
  ' ## if pen color is transparent don't draw
  Protected Overrides Sub WndProc(ByRef m As Message)
      MyBase.WndProc(m)
      If m.Msg = &HF And Not Triangle Is Pens.Transparent Then ' WM_PAINT
          ' ## define triangle position
          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

          ' ## define triangle shape
          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)

          ' ## draw shape
          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:

VB.NET
' ##### Control the input value
   Private Sub numericTexbox_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress

       ' ## check if validation is required
       If InputType = _IType.NoValidation Then Return

       ' ## check if key pressed is backspace or delete, avoid group seperator
       If e.KeyChar = CChar(ChrW(Keys.Back)) Or e.KeyChar = CChar(ChrW(Keys.Delete)) And e.KeyChar <> GroupeSeparator Then
           Return
       End If

       ' ## check Range
       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
           ' ## check for decimal
           If _InputType = 2 And _decimals > 0 And Char.IsDigit(e.KeyChar) Then
               ' ## check decimal places
               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

           ' ## check integer
           If _InputType = 1 And Char.IsDigit(e.KeyChar) Then
               DecimalPlaces = 0
               Return
           End If

           ' ## check if negativ is allowed and negative is pressed as 1st char (leading negativ sign)
           If Me.Text.Length = 0 And e.KeyChar = "-" And _negativ And (Not Me.Text.Contains(DecSeparator)) Then Return
       End If

       ' ## all other cases are not valid
       e.Handled = True ' abord key press
       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:

VB.NET
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:

VB.NET
' ##### control clipboard actions"
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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder TechDoc G. Becker
Germany Germany
I'am developing Applications since the Comodore 64 Computer was available to the marked.
At present I'm programming in Visual Studio 2012 using C# and mostly Visual Basic.
Sorry for Language mistakes but as a German English is not my native Language. But I hope anyone is able to understand me.

Best Regards from Germany
Happy coding
Guenter

Comments and Discussions

 
QuestionThank you Pin
SpringGump26-Nov-15 16:59
SpringGump26-Nov-15 16:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.