- [-+]? - optional sign section: matches either - , or + , or nothing
- \d* - integer section: matches either 0, or more decimal digits
- (\.?) - decimal point section: group that matches either 0, or 1 decimal point
- (\d+) - fractional section: group that matches either 1, or more decimal digits
- ([eE][-+]?\d+)? - exponent section: group that matches either nothing, or exponent base (e or E) followed by optional sign, followed by either 1, or more decimal digits.
The regular expression is provided as a variable of type Regex; parameter value RegexOptions.ECMAScript narrows possible set of digits representation to the English one only.
Sn. 2: Regex variable - file NumericalBox.cs, project NumericalBoxCtrl
Anchors ^ and $ say that the match starts at the beginning of the string and must occur at the end of the string.
OnTextChanged override - first attempt - project NumericalBoxCtrlJobHalfDone
Let us implement all above mentioned in OnTextChanged:
Sn. 3: OnTextChanged override and IsInputTextValid method - file NumericalBox.cs, project
NumericalBoxCtrlJobHalfDone
Private variable _lastVerifiedText stores the last typed string that passed regular expression check and is used for restoring the last good version. More convenient way of this restoration will be shown in the final project NumericalTextBox.
Now let us try the project NumericalTextBoxCtrlJobHalfDone - start the Demo
NumericalTextBoxCtrlJobHalfDone.exe and begin to type in number 123.45 in the text box.
Fig. 1: Control does not allow to type in 123. or 123e
The attempt fails! We are not able to type in the decimal point.
Cause of failure
The thing is that our _rgxChangedValid describes a completed well-formed number; for example, it has to contain at least one digit after the decimal point. However, during sequential typing the user is not able to type in the decimal point and the first digit after it simultaneously - decimal point always comes first and is rejected.
_rgxChangedValid expects the completed number and at the moment we have only a job half-done. "Don't show a fool a job half-done!"
OnTextChanged override - second attempt - project NumericalBoxCtrl
So we should use a different regular expression for approvig incomplete numbers in the OnTextChanged override. This is it:
Sn. 4: Regex variable for approve incomplete numbers - file NumericalBox.cs, project NumericalBoxCtrl
There are only two differences between NumericalBoxCtrlJobHalfDone _rgxChangeValid ang NumericalBoxCtrl _rgxChangedValid. The second regular expression has \d* on the second and on the third digit positions. The quantifier * means "matching the previous element zero or more times". That means "zero or more digits" in our case, and in turn that means that strings "123." and "123e" are allowed. Now this is the code of the new version of OnTextChanged:
Sn. 4: OnTextChanged - file NumericalBox.cs, project NumericalBoxCtrl
The private method IsTextValid (it will be explained below) checks the NumericalBox content with the help of _rgxChangedValid and tries to extract the longest valid substring from the invalid content.
OnLostFocus override
Now, the completed number should be checked also - when it is completed, of course. It is the LostFocus event, that is the criterion of completeness: a number should be considered completed when the NumericalBox loses the focus. Microsoft did not accidentally choose Mode=LostFocus for bindings from the TextBox!
Our first "regular expression for numbers validation" should be used here. We will call it _rgxLostFocusValid now:
And here OnLostFocus is:
Sn. 5: OnLostFocus - file NumericalBox.cs, project NumericalBoxCtrl
It looks almost like OnTextChanged except it uses _rgxLostFocusValid regular expression and does not place the cursor at the end of the string because of the cursor is cancelled by loss of focus.
By the way, about the loss of focus. It is performed by mouse click outside the box and I apologize that it is implemented in code behind. MVVM implementation of LostFocus event looks a bit ponderous for such a short project.
IsTextValid method and extraction of valid substring
IsTextValid method extracts the longest valid substring. It uses rgxSave=_rgxSaveChanged
regular expression in case of incomplete number and rgxSave=_rgxSaveLost
regular expression in case of completed number:
Sn. 6: excerpt from IsTextValid method - file NumericalBox.cs, project NumericalBoxCtrl
Both _rgxSaveChanged and _rgxLostChanged have no ^ and $ anchors for extracting a valid substring from the middle of text.
IsTextValid method also validates text by means of Regex.Match method, where rgxValid equals to _rgxChangedValid and _rgxLostValid in the "changed" validation and in the "lost focus" validation respectively:
Sn. 7: excerpt from IsTextValid method - file NumericalBox.cs, project NumericalBoxCtrl
Using the code
Try the Demo NumericalBoxCtrl.exe.
Now NumericalBox allows well-formed numbers:
Fig. 2: Well-formed number
and tries to extract valid part of malformed number after LostFocus event:
Fig. 3: Malformed number
Fig. 4: Malformed number is corrected
NumericalBox.cs can be used wherever it is necessary, instead of TextBox.