Table of Contents

16.6.3 Examples for using a TextBox - Validation of input

Securing the use of valid data in a program is part of the concept for avoiding runtime errors. In → chapter 16.6.2 Valid Data you will find information on the topic and approaches how to validate data from TextBoxes and their different specializations (MaskBox, ValueBox, HistoryBox, InputBox and ButtonBox). For the input of complex numbers, matrices, polynomials or vectors you will find examples in the chapters 29.1 Complex Numbers, 29.3.3 Matrix, 29.3.4 Polynomial and 29.3.5 Vector.

There is no generally valid procedure for checking whether an input string from a TextBox can be converted to a valid date - if the specializations cannot be used. However, the following examples should show you how to use different strategies to provide a program with valid data from an input string for processing.

16.6.3.1 Example 1

A number A is recorded in a TextBox and an average measured value (without unit) as a real number R in another TextBox. From the two inputs, a quotient with four decimal places is calculated in the program QM = (A²-Pi) / R and output in a third TextBox (read only). The contents of the output TextBox are deleted each time the entries are changed.

QM

Figure 16.6.3.1.1: Calculation of QM

Before you get started quickly, you should consider the following hints in peace and quiet, although the order does not correspond to any order of precedence:

The source code is presented in extracts:

[1] ' Die Eigenschaft txbResult.ReadOnly wird auf TRUE gesetzt
[2] Public Sub btnCalculate_Click()
[3]   Dim sInputN, sInputR, sPatternN, sPatternR As String
[4]   Dim iCount As Integer
[5]   Dim fValue, fCoefficient As Float
[6]     
[7]   If Not txbInteger.Text Then ' Prüfung: TextBox leer?
[8]      Message.Error("Eingabefehler 1!") ' Kommentar in einer MessageBox
[9]      txbInteger.SetFocus ' Fokus auf die TextBox txbInteger setzen
[10]      Return ' Prozedur verlassen
[11]   Endif  
[12]   If Not txbReal.Text Then ' Prüfung: TextBox leer?
[13]      Message.Error("Eingabefehler 2!")
[14]      txbInteger.SetFocus
[15]      Return
[16]   Endif
[17]   
[18]  '----------------------------------------------------------------------------
[19]   
[20]   sInputN = Trim(txbInteger.Text) ' Leerzeichen am Anfang und am Ende entfernen
[21]   Try iCount = CInteger(sInputN) ' Versuch: Konvertierung String → Ganze Zahl
[22]   If Error Then ' Fehlerbehandlung
[23]      Message("Fehler 1")
[24]      txbInteger.SetFocus
[25]      Return
[26]   Endif
[27]   
[28] ' Anzahl Null herausfiltern
[29]   If iCount = 0 Then 
[30]      Message.Info("Die Anzahl muss größer als Null sein!")
[31]      txbReal.SetFocus
[32]      Return
[33]   Endif  
[34] 
[35] '----------------------------------------------------------------------------
[36]     
[37]   sInputR = Trim(txbReal.Text) ' Leerzeichen am Anfang und am Ende entfernen 
[38] ' Dezimalseparator der aktuellen Locale durch den Punkt ersetzen  
[39]   sInputR = Replace$(sInputR, Left$(Format$(0, ".0")), ".")
[40]   
[41]   Try fValue = CFloat(sInputR) ' Versuch: Konvertierung String → Reelle Zahl
[42]   If Error Then 
[43]      Message("Fehler 2")
[44]      txbReal.SetFocus
[45]      Return
[46]   Endif
[47] 
[48]   If fValue = 0 Then ' Null herausfiltern    
[49]      Message.Info("Die (reelle) Zahl 0 ist nicht zulässig!")
[50]      txbReal.SetFocus
[51]      Return
[52]   Endif
[53] 
[54] '----------------------------------------------------------------------------
[55]  
[56]   fCoefficient = Round((iCount * iCount - Pi()) / fValue, -4) ' Berechnung Quotient
[57]   txbResult.Text = Str(fCoefficient) ' Anzeige Quotient (Koeffizient QM)
[58] 
[59] End ' btnCalculate_Click()
[60] 
[61] '----------------------------------------------------------------------------
[62] 
[63] Public Sub txbInteger_KeyPress() ' Umsetzung Eingabe-Alphabet
[64]   CheckInput("0123456789")
[65] End ' txbDigits_KeyPress()
[66] 
[67] Public Sub txbReal_KeyPress() ' Umsetzung Eingabe-Alphabet
[68]   CheckInput("+-,0123456789")
[69] End ' txbDigits_KeyPress()
[70] 
[71] Public Sub CheckInput(sAllowed As String) ' Idee Charles Guerin  
[72]   Select Case Key.Code
[73]     Case Key.Left, Key.Right, Key.BackSpace, Key.Delete, Key.End, Key.Home, Key.Enter, Key.Return
[74]       Return
[75]     Default
[76]       If Key.Text And If InStr(sAllowed, Key.Text) Then
[77]          Return
[78]       Endif
[79]   End Select
[80]   Stop Event 
[81] End ' CheckInput(sAllowed As String)
[82] 
[83] ' Public Sub txbInteger_Change()
[84] '    txbResult.Clear
[85] ' End
[86] ' Public Sub txbReal_Change()   
[87] '   txbResult.Clear
[88] ' End
[89] 
[90] Public Sub TBIR_Change()
[91]   txbResult.Clear
[92] End ' TBIR_Change()

Comment:

[1]   sInputN = Trim(txbInteger.Text)
[2]   sPatternN = "^[1-9][0-9]*$" ' Alternative mit Null: "^[0]{1}$|^[1-9][0-9]*$"
[3]   If sInputN Not Match sPatternN Then 
[4]      Message.Error("Eingabefehler 3!")
[5]      txbInteger.SetFocus
[6]      Return
[7]   Endif  
[8]   iCount = CInteger(sInputN)

16.6.3.2 Example 2

To ensure that only certain characters - here it is the numbers 0 to 9 - are accepted in a TextBox, the following sample source code is offered in the documentation:

' My text box only accepts digits 
 
Public Sub MyTextBox_KeyPress() 
  If Instr("0123456789", Key.Text) = 0 Then 
     Stop event 
  Endif 
End

Hints:

Works - but unfortunately, you can't correct the sequence of digits in any way, which can be considered a deficiency. Therefore, the following section introduces an improved solution in which you can move the cursor in the TextBox and delete individual digits with the BackSpace key:

Public Sub txbDigits_KeyPress() 
' Nur die Ziffern 0..9 als zulässige Ziffern zulassen
  If (Key.Text Not Like "[0-9]") And (Key.Code <> Key.BackSpace) And (Key.Code <> Key.Left) \\
     And (Key.Code <> Key.Right) Then
     Stop Event
  Endif ' Key.Text Not Like "[0-9]" ?  
End ' txbDigits_KeyPress() 

You will undoubtedly still discover a disadvantage - the input alphabet is not variable if you think, for example, of the case that all digits 0-9 are allowed, plus the two sign (+-) and the characters dot, comma and asterisk, as well as the letter x. The input alphabet is not variable. The following solution safely eliminates the disadvantage:

Public Sub CheckInput(sAllowed As String) ' Idee Charles Guerin  
  Select Case Key.Code
    Case Key.Left, Key.Right, Key.BackSpace, Key.Delete, Key.End, Key.Home, Key.Enter, Key.Return
      Return
    Default
      If Key.Text And If InStr(sAllowed, Key.Text) Then
         Return
      Endif
  End Select
  Stop Event 
 
End ' CheckInput(sAllowed As String)
 
Public Sub txbDigits_KeyPress()
  CheckInput("+-,.*0123456789x")
End ' txbDigits_KeyPress()

The check whether a character entered belongs to the input alphabet or not works without errors. However, remember to check the validity of the input string afterwards if, for example, the input string is to be interpreted as a term in a linear equation. The string -5,88*x+7,1+*23 contains only characters from the input alphabet and is nevertheless not suitable for processing, since it contains a syntax error in the sense of the term specification.

16.6.3.3 Example 3

This example does not focus on checking but on prevention, because the user is shown a hint text in the TextBox to minimize incorrect entries. A special TextBox developed by Tobias Boege is used - the Explain-TextBox. The Explain-TextBox is a TextBox, which allows to display an explanatory text (explanation) in the font color grey, if the TextBox is empty and does not have the focus. In this way, you might be able to dispense with explanatory labels in front of the text boxes:

Figures 16.6.3.3.3.1: Explain-TextBox

The class ExplainTextbox is provided in a project in the download area.

16.6.3.4 Example 4

It should be checked whether the input of a year number in the interval from 1600 to 2100 in a text box is or will be a leap year.

An astronomical year is the duration of the revolution of the Earth around the central body Sun - our star. In our time units this is 365 days, 5 hours, 48 minutes and 47 seconds. A normal calendar year - but what is already normal - has a length of 365 days. After 4 years, the 5 hours, 48 minutes and 47 seconds, which corresponds to about a quarter of a day, are combined into one day, the 29th of February, and thus a leap year has 366 days. There is a leap year every 4 years, but not every 100 years, but then every 400 years to compensate for the discrepancies of the inserted quarter days to 6 hours. I am thrilled!

As a preventive measure, the number of characters is limited to a maximum of 4 characters:

[1] Public Sub Form_Open()
[2]   txbLeapYear.MaxLength = 4
[3]   ...
[4] End ' Form_Open()

The function IsLeapYear (..) converts the above rule for leap years and returns the function value True if the year used as argument was or is a leap year:

[1] Public Function IsLeapYear(iYear As Integer) As Boolean
[2] 
[3]   If (iYear Mod 4) = 0 And (iYear Mod 100) <> 0 Or ((iYear Mod 400) = 0) Then
[4]      Return True
[5]   Else
[6]      Return False
[7]   Endif
[8] 
[9] End ' Function IsLeapYear(..)

In the following source code section - which is followed by a comment - you will see various (preliminary) checks:

[1] Public Sub txbLeapYear_Activate()
[2]   Dim iCount As Integer
[3]   Dim sInput As String
[4]   
[5]   sInput = Trim(txbLeapYear.Text) ' Entfernung von Leerzeichen am Anfang und am Ende des Strings
[6]   
[7]   For iCount = 1 To Len(sInput)
[8]       If Mid(sInput, iCount, 1) Not Like "[1234567890]" Then ' Zeichen-Prüfung auch mit [0-9]
[9]          Message.Error("Eingabefehler - Zeichen!")
[10]          Return
[11]       Endif
[12]    Next
[13]    
[14]   If Len(sInput) <> 4 Then 
[15]      Message.Error("Jahreszahl nicht vierstellig!") ' optional
[16]      Return
[17]   Endif     
[18]   
[19]   If CInt(sInput) < 1600 Or CInt(sInput) > 2100 Then ' Bereichsprüfung [1600..2100]
[20]      Message.Error("Eingabefehler - Bereich!")
[21]      Return
[22]   Endif
[23]   
[24]   If IsLeapYear(CInt(sInput)) Then ' Prüfung Schaltjahr und Kommentare
[25]      If CInt(sInput) < Year(Now) Then
[26]         Message.Info("Das Jahr " & sInput & " war ein Schaltjahr.")
[27]      Else
[28]         Message.Info("Das Jahr " & sInput & " wird ein Schaltjahr sein.")
[29]      Endif
[30]   Else
[31]      If CInt(sInput) < Year(Now) Then
[32]         Message.Info("Das Jahr " & sInput & " war kein Schaltjahr.")
[33]      Else
[34]         Message.Info("Das Jahr " & sInput & " wird kein Schaltjahr sein.")
[35]      Endif
[36]   Endif
[37] 
[38] End ' txbLeapYear_Activate()

Comment:

The alternative presented here uses a regular expression with which all checks are performed in one step:

Public Sub txbLeapYear_Activate()
  Dim sSubject, sPattern As String
 
  sSubject = Trim(txbLeapYear.Text)   
  sPattern = "^([1][6789][0-9]{2})|[2][0]([0-9]{2})|2100$" ' Check pattern as regular expression
 
  If sSubject Not Match sPattern Then 
     Message.Error("Eingabefehler!")
     Return
  Endif
 
  Message.Info(Subst$("Das Jahr &1 ist &2ein Schaltjahr!",sSubject,IIf(IsLeapYear(CInt(sSubject)),"","k")))
 
End ' txbLeapYear_Activate()

16.6.3.5 Example 5

With the following source code you can completely synchronize the two TextBoxes txbA and txbB during input:

Public Sub txbA_Change() 
  txbB.Text = txbA.Text 
End ' txbA_Change()

16.6.3.6 Example 6

Public Sub txbInput_Activate()
  Dim sInput As String = txbInput.Text
 
' Replace the decimal separator of the current (German) locale with the dot
  Print sInput
    sInput = Replace$(sInput, Left$(Format$(0, ".0")), ".")
  Print sInput
 
End

16.6.3.7 Example 7

There are n TextBoxes on a form, where the content of k selected TextBoxes (k ≤ n) is to be deleted with one call. The solution is to define a unique selling point for the TextBoxes whose content is to be deleted, and the tag property is a good way to do this.

Public Sub Form_Open()
  FMain.Center
 
  txbBox1.Tag = "L"
  txbBox2.Tag = "L"
  txbBox4.Tag = "L"
  txbBox6.Tag = "L"
  ...  
End ' Form_Open()
 
Private Sub TBMultiClear()
  Dim C As Control
  Dim tBox As TextBox  
  For Each C In Me.Controls
     If C Is TextBox Then
        If C.Tag = "L" Then 
         ' Print C.Name ' Kontrolle
           tBox = C
           tBox.Clear
        Endif
     Endif
  Next
End ' TBMultiClear()
 
Public Sub btnTBClear_Click()
  TBMultiClear() 
End ' btnTBClear_Click()

16.6.3.8 Example 8

The following task must be processed: When activating the Activate-Event of a textbox A (printing on the return key) a certain action is to be triggered and then the cursor is to be placed in the textbox B, which receives the focus. This is done manually with the tab key if the TextBox B in the hierarchy immediately follows the TextBox A. You define the hierarchy of components in the IDE in the hierarchy window. The following source code also implements the above mentioned behavior:

Public Sub txb_A_KeyPress()
  If Key.Code = Key.Enter Or Key.Code = Key.Return Then
   ' ACTION ...
     txb_B.SetFocus()
   ' Desktop.SendKeys("\t") ' Alternative if the Desktop component is used
  Endif
End ' txb_A_KeyPress()

The use of the alternative has the advantage that the text is marked in the TextBox B, just like when using the tabulator key.

Download