I've been reading a number of topics recently where people were using text boxes for input of numeric values.
Everyone kept using CInt or CDbl to obtain values which is generally a bad form since a user can provide the wrong input (i.e. text instead of digits).

To help the beginners (or just lazy people) to properly validate user input I've extended a textbox control to provide such people with a ready-to-use solution.

This class can process input from any cultures (in my coutry, for example, we use comma as a delimeter between the integer and fractional parts of a number, in some countries they use dot and god knows who uses what elsewhere).

This class utilizes Globalization NumberFormatInfo to get the Format Provider, suitable for the current system.

Also, this control has properties to assign background colors separately for successfully validated and not validated values.

Also, there are properties that allow validation of Integer values only (if you don't need decimal values). Note: change the type from Decimal to Double if you like.

Also, there is a property that can prevent a user to change focus (to cancel the validation) if he or she made the wrong input.

And finally, there is a property that treats an empty string as zero (default value is true).



Basically I would like you to try it out and give me some feedback if possible.

vb.net Code:
  1. ''' <summary>
  2. ''' Extends the texbox with the Decimal and / or Integer validation
  3. ''' And provides users with Value and ValueInt properties that return
  4. ''' validated decimal or integer values.
  5. ''' </summary>
  6. ''' <remarks>
  7. ''' * To turn off decimal validation turn ValidateIntOnly
  8. '''   property to True (default is false).
  9. ''' * To prevent user from changing focus with not validated input set
  10. '''   the CancelValidationOnFail property to True (default is false).
  11. ''' * To Get the decimal value use the Value property.
  12. ''' * To Get the integer value use the ValueInt property.
  13. ''' </remarks>
  14. Class NumericTextbox
  15.  
  16.     Inherits TextBox
  17.  
  18.     'Private members
  19.     Private _Validated As Boolean = False
  20.     Private _IntOnly As Boolean = True
  21.     Private _ShouldCancelValidattion As Boolean = False
  22.     Private _TreatEmptyAsZero As Boolean = True
  23.     Private _Value As Decimal
  24.     Private _IntValue As Integer
  25.  
  26.  
  27.     Private _NotValidatedBackColor As Color = Color.Pink
  28.     Private _ValidatedBackColor As Color = Color.PaleGreen
  29.  
  30.     ''' <summary>
  31.     ''' Occurs between Validating and Validated events if the validation failed.
  32.     ''' </summary>
  33.     ''' <param name="sender"></param>
  34.     ''' <param name="e"></param>
  35.     ''' <remarks></remarks>
  36.     Public Event NotValidated(ByVal sender As Object, ByVal e As System.EventArgs)
  37.  
  38.     Public Sub New()
  39.         MyBase.New()
  40.         MyBase.CausesValidation = True
  41.     End Sub
  42.  
  43.     Public Overrides Property Multiline() As Boolean
  44.         Get
  45.             Return MyBase.Multiline
  46.         End Get
  47.         Set(ByVal value As Boolean)
  48.             If value Then
  49.                 Throw New NotSupportedException("Multiline mode is not supported")
  50.             End If
  51.         End Set
  52.     End Property
  53.  
  54.  
  55.  
  56.     ''' <summary>
  57.     ''' Gets or sets the setting indicating whether the control should treat an empty string as zero. Default is True.
  58.     ''' </summary>
  59.     ''' <value></value>
  60.     ''' <returns></returns>
  61.     ''' <remarks></remarks>
  62.     Public Property TreatEmptyAsZero() As Boolean
  63.         Get
  64.             Return _TreatEmptyAsZero
  65.         End Get
  66.         Set(ByVal v As Boolean)
  67.             _TreatEmptyAsZero = v
  68.             Call Me.OnTextChanged(Nothing)
  69.         End Set
  70.     End Property
  71.  
  72.     ''' <summary>
  73.     ''' Gets or sets the background of the control when the value is not validated.
  74.     ''' </summary>
  75.     ''' <remarks>Overrides the BackGound property. the default value is Color.Pink</remarks>
  76.     Public Property NotValidatedBackColor() As Color
  77.         Get
  78.             Return _NotValidatedBackColor
  79.         End Get
  80.         Set(ByVal v As Color)
  81.             _NotValidatedBackColor = v
  82.             MyBase.BackColor = v
  83.             MyBase.Invalidate()
  84.         End Set
  85.     End Property
  86.  
  87.     ''' <summary>
  88.     ''' Gets or sets the backgound of the control when the value has been validated.
  89.     ''' </summary>
  90.     ''' <remarks>The default value is Color.PaleGreen</remarks>
  91.     Public Property ValidatedBackColor() As Color
  92.         Get
  93.             Return _ValidatedBackColor
  94.         End Get
  95.         Set(ByVal v As Color)
  96.             _ValidatedBackColor = v
  97.             MyBase.BackColor = v
  98.             MyBase.Invalidate()
  99.         End Set
  100.     End Property
  101.  
  102.  
  103.     ''' <summary>
  104.     ''' Turns off Decimal validation and validates integers only.
  105.     '''     ''' </summary>
  106.     ''' <value></value>
  107.     ''' <returns></returns>
  108.     ''' <remarks>Value property will return a rounded decimal.
  109.     ''' To get an integer value use <see cref="ValueInt">ValueInt property</see>.</remarks>
  110.     Public Property ValidateIntOnly() As Boolean
  111.         Get
  112.             Return _IntOnly
  113.         End Get
  114.         Set(ByVal v As Boolean)
  115.             _IntOnly = v
  116.         End Set
  117.     End Property
  118.  
  119.     ''' <summary>
  120.     ''' Gets a decimal value of the validated user input.
  121.     ''' </summary>
  122.     ''' <value></value>
  123.     ''' <returns></returns>
  124.     ''' <remarks>Read only</remarks>
  125.     Public ReadOnly Property Value() As Decimal
  126.         Get
  127.             Return _Value
  128.         End Get
  129.     End Property
  130.  
  131.     ''' <summary>
  132.     ''' Gets an integer values of the valdated user input.
  133.     ''' </summary>
  134.     ''' <value></value>
  135.     ''' <returns></returns>
  136.     ''' <remarks>To get the decimal value use <see cref="Value">Value property</see></remarks>
  137.     Public ReadOnly Property ValueInt() As Integer
  138.         Get
  139.             If Not _IntOnly Then
  140.                 Throw New InvalidOperationException("ValidateIntOnly property is set to false. Set it to true to turn on the integer evaluation.")
  141.                 Exit Property
  142.             End If
  143.             Return _IntValue
  144.         End Get
  145.     End Property
  146.  
  147.     ''' <summary>
  148.     ''' Gets or sets a setting indicating whether the control should cancel validation of a control.
  149.     '''     ''' </summary>
  150.     ''' <value></value>
  151.     ''' <returns></returns>
  152.     ''' <remarks>When set the control won't allow to change focus if the input value is in the wrong formst.</remarks>
  153.     Public Property CancelValidationOnFail() As Boolean
  154.         Get
  155.             Return _ShouldCancelValidattion
  156.         End Get
  157.         Set(ByVal v As Boolean)
  158.             _ShouldCancelValidattion = v
  159.         End Set
  160.     End Property
  161.  
  162.  
  163.     ''' <summary>
  164.     ''' Raises Validating event or cancels Validation if CancelValidationOnFail property is set to true
  165.     ''' </summary>
  166.     ''' <param name="e"></param>
  167.     ''' <remarks></remarks>
  168.     Protected Overrides Sub OnValidating(ByVal e As System.ComponentModel.CancelEventArgs)
  169.         If ValidateValue() Then
  170.             MyBase.OnValidating(e)
  171.         Else
  172.             e.Cancel = _ShouldCancelValidattion
  173.             MyBase.OnValidating(e)
  174.             RaiseEvent NotValidated(Me, New System.EventArgs)
  175.         End If
  176.     End Sub
  177.  
  178.     Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
  179.         If ValidateValue() Then
  180.             Me.BackColor = _ValidatedBackColor
  181.         Else
  182.             Me.BackColor = _NotValidatedBackColor
  183.         End If
  184.         MyBase.OnTextChanged(e)
  185.         Me.Invalidate()
  186.     End Sub
  187.  
  188.     Protected Overridable Function ValidateValue() As Boolean
  189.         ' Allows any format to be converted to decimal
  190.         Dim ValidationText As String = MyBase.Text
  191.  
  192.         ' Check whether we should trean an empty string as zero
  193.         If MyBase.Text.Length = 0 AndAlso Me._TreatEmptyAsZero Then ValidationText = "0"
  194.  
  195.         Dim fp As IFormatProvider = System.Globalization.NumberFormatInfo.InvariantInfo
  196.         If Not _IntOnly Then
  197.             Return Decimal.TryParse(ValidationText, System.Globalization.NumberStyles.Any, fp, _Value)
  198.         Else
  199.             Return Integer.TryParse(ValidationText, System.Globalization.NumberStyles.Integer, fp, _IntValue)
  200.             _Value = CDec(_IntValue)
  201.         End If
  202.     End Function
  203.  
  204. End Class