Thank you for this code. It obviously took a lot of work and has a lots of coding techniques I will be applying in the future. I did find some remaining bugs, however, which I have corrected and extended. The bugs included:
Any format with spaces in it failed the Decimal.TryParse test, despite being valid for the given culture. I redid the GetNumberStyles Select Case statements to account for two additional NumberStyles: AllowLeadingWhite and AllowTrailingWhite. These style options are required to use Decimal.TryParse for the following patterns:
NumberNegativePattern: "- n" requires AllowLeadingWhite and "n -" requires AllowTrailingWhite.
PostiveCurrencyPattern: "$ n" requires AllowLeadingWhite and "n $" requires AllowTrailingWhite.
NegativeCurrencyPattern: "$ n" and "-$ n" require AllowLeadingWhite and "n $", "-n $", "n $-" and "n- $" require AllowTrailingWhite.
After fixing the Currency Pattern I copied those routines and renamed them from Currency to Percent to set up all of the Percent Format conversions. The differences between handling Currency and Percentage text is that the Decimal.TryParse method cannot be used directly on text with a Percent symbol in it. So I adapted the code suggested in the fourth post of this thread. If I detect text with a Percent Symbol I strip out the percent symbol, using Decimal.TryParse using the appropriate cultural NumberStyles and then divide that number by 100 to populate the _nullableValue and _value properties. That code looks like:
So now I think everything works, except that I would like it handle all of localized properties of the NumberFormatInfor and CurrencyFormatInfo classes. So I would like to to display native digits instead of arabic digits where they differ, handle NaN, Positive and Negative Infinity, and
Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
'Remember the old numeric values.
Dim oldNullableValue = Me._nullableValue
Dim oldValue = Me._value
'Set the new numeric values. The fields are used rather than the properties so that the Text won't be changed again.
If Me.TextLength = 0 AndAlso _
(Me.Focused OrElse Me.BlankMode = BlankModes.Accept) Then
'The control is blank.
Me._nullableValue = Nothing
Me._value = Decimal.Zero
Dim number As Decimal
Dim formatProvider As NumberFormatInfo = Me.GetFormatProvider()
Dim percentSymbol As String = formatProvider.PercentSymbol
Dim isPercent As Boolean = Text.Contains(percentSymbol)
If isPercent Then
'Text must be able to be parsed.
Dim s As String = Text
If s.Contains(percentSymbol) Then
s = s.Replace(percentSymbol, "")
If Decimal.TryParse(s.Trim, _
Me.GetNumberStyles(True, False, True), _
'good number, convert percent to decimal
number = number / 100
'Get the number the text represents or zero if it doesn't represent a number.
Me.GetNumberStyles(True, True, True), _
Me._value = number
Me._nullableValue = number
'Raise the appropriate events in the appropriate order based on the property that was initially set.
If Me.nullableValueSet Then
'Setting the NullableValue property prompted the change.
If Me._value <> oldValue Then
Me.nullableValueSet = False
ElseIf Me.valueSet Then
'Setting the Value property prompted the change.
Me.valueSet = False
'Setting the Text property prompted the change.
If Not Me._nullableValue.Equals(oldNullableValue) Then
If Me._value <> oldValue Then