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:
Code:
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
Else
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, "")
End If
If Decimal.TryParse(s.Trim, _
Me.GetNumberStyles(True, False, True), _
FormatProvider, _
number) Then
'good number, convert percent to decimal
number = number / 100
End If
Else
'Get the number the text represents or zero if it doesn't represent a number.
Decimal.TryParse(Me.Text, _
Me.GetNumberStyles(True, True, True), _
Me.FormatProvider, _
number)
End If
Me._value = number
Me._nullableValue = number
End If
'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.
Me.OnNullableValueChanged(EventArgs.Empty)
If Me._value <> oldValue Then
Me.OnValueChanged(EventArgs.Empty)
End If
MyBase.OnTextChanged(e)
Me.nullableValueSet = False
ElseIf Me.valueSet Then
'Setting the Value property prompted the change.
Me.OnValueChanged(EventArgs.Empty)
Me.OnNullableValueChanged(EventArgs.Empty)
MyBase.OnTextChanged(e)
Me.valueSet = False
Else
'Setting the Text property prompted the change.
MyBase.OnTextChanged(e)
If Not Me._nullableValue.Equals(oldNullableValue) Then
Me.OnNullableValueChanged(EventArgs.Empty)
End If
If Me._value <> oldValue Then
Me.OnValueChanged(EventArgs.Empty)
End If
End If
End Sub
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