Introduction
Here's a modified MaskedTextBox
that makes it easy to type various numbers, since the numbers "push in" from the right as you type them, the way it does on a pocket calculator.
Background
Here's why I've made this control:
When tabbing into a phone number field or clicking in, I've noticed it's easy to "miss", and you have to do more clicking or arrow-ing to get to the spot where the number looks right when entered.
For example, if you place a MaskedTextBox
on your form for telephone number entry, it would usually include the area code digit positions. But what if the user doesn't need to enter an area code?
(___) 555-1212
Problem is, when the user tabs in, the cursor by default would appear at the beginning of the area code portion. Or if the user clicks in, it's easy to click into the control at the wrong spot as well.
But there is an easier way. The way you enter text on your pocket calculator is better. The numbers always "push in" from the right side of the display.
So I decided to make a phone number entry box, which (during entry via the keyboard) only accepts digits and the backspace (or left-arrow does the same). If the number has 7 digits, it will look like the above. If you keep typing, you get all ten digits.
(Oh, you can set the value (the phone number value that is) using the A_Text
property. Anyone wants to add the capability to paste a number in? )
By the way, if you click somewhere in the box when a number is already there, it deletes the characters to the right of the click position, for convenience.
Using the Code
Unzip the download and add the two items to your project folder, then open your project and right-click on the project, then "Add..", and finally locate and open CoolNumberBox.vb.
The existing code provides ten different masks for various numbers, including 7-digit, 10-digit, and 11-digit entry for (North American) long distance numbers that need a "1" on the front of the number.
Public Enum NumberMasks
One_Digit_X
Two_Digit_XX
Three_Digit_XXX
Four_Digit_XXXX
Five_Digit_XXXXX
Seven_Digit_555_1212
Ten_Digit_800_555_1212
Eleven_Digit_1_800_555_1212
Area_Code_Plus_Exchange_XXX_XXX
Time_as_Hours_and_Minutes_X_XX
End Enum
Also, if you wanted to add support for international telephone number entry, you would need to change the masks. If you do, please make sure you adjust the mMaxLen
and mEmptyMask
variables to agree with your mask, in the Set
block of the A_PhoneNumberMask
property.
The field mMaxLen
represents the length of the "raw" number, so for example: (800) 555-1212 has a length of 10.
Accordingly, the mEmptyMask
field has mMaxLen
number of spaces.
Finally, the Mask
property of MaskedTextBox
is set (internally) according to standard masking principles, which you can get by looking up the MaskedTextBox
and its properties.
By the way, you'll notice that I have prepended "A_" to my custom property names. This is to cause intellisense to put them at the top of the list. Why? Because, months later, I always forget what my custom property names were, but with this method, they're always right there, and grouped together as a bonus.
The code is annotated, so download the code and give it a try.
Points of Interest
burlyeman24 has found a better way of dealing with the problem of backspacing, which is discussed a bit further down. Before I discuss the problem (which is now fixed!), I'll show the new solution. The key is to call the TheTextChanged
sub *before* setting e.Handled=True
.
The older, inferior way of handling the problem was attempting to overcome the problem in the OnKeyUp
handler:
Protected Overrides Sub OnKeyUp(ByVal e As System.Windows.Forms.KeyEventArgs)
If Me.ReadOnly Then Exit Sub
If e.KeyCode = Keys.Back Then TheTextChanged(mTheText)
MyBase.OnKeyUp(e)
End Sub
The corrected, better way, located in the OnKeyDown
hander, is first calling TheTextChanged
, *then* setting e.Handled=True
:
Case Keys.Left, Keys.Back
If Len(mTheText) Then mTheText = Mid(mTheText, 1, Len(mTheText) - 1)
TheTextChanged(mTheText
e.Handled = True
Exit Sub
I used the SelectionStart
property of the MaskedTextBox
control to force the cursor to sit at the end of the text of the MaskedTextBox
. This is basic to my philosophy here, of "pushing in" the characters from the right side, like in a cash register.
Private Sub TheTextChanged(ByVal ControlText As String)
Text = Mid(mEmptyMask, 1, mMaxLen - Len(ControlText)) + ControlText
SelectionStart = Mask.Length
End Sub
I want you to feel free to use and change this code in your own project. If you wish to acknowledge my work within your project, such as in a Thanks To: box, that would be nice. As well, you should respect my Copyright statement at the top of the code, by leaving it there to remind you where it came from.
History
- July 13, 2007 - First version.
- July 14, 2007 - Converted the project to a component instead of a UserControl.
- November 17, 2008 - Implemented a superior way of backspacing, thanks to burleyman24.
- Dec 2, 2009 - Editing changes for clarity. Updated zip to the latest version I have.