Introduction
Visual Studio.NET and the .NET Framework includes many very useful controls. Unfortunately a MaskedEdit control was not among them. Anyone who has used the VS 6.0 MaskedEdit control knows that Microsoft's last attempt was not without its problems. MS Access developers have always had a useable MaskedEdit control. I wonder sometimes why Microsoft hasn't adapted the Access control to their other tools. Anyway I decided to tackle this very useful but missing control.
After spending a few hours working to handle character selection in the TextBox based control, I began to understand why someone might avoid building this control. Then came setting the MaskedEdit control value which introduced a new set of challenges. The difficulty of building a bullet proof MaskedEdit became apparent very rapidly - so this is fair warning, don't forget to test, test, test.
Background
The MaskedEdit control is loosely based on the Access mask behavior and includes the following features:
- Standard InputMasks
- Social Security Number (SSN)
- Phone Numbers
- Zip Codes
- Custom InputMasks
- Runtime changable InputMask
- IsValid Property
- Text Property (includes literals)
- Value Property (excludes literals)
- Input Char Property (defaults to '_')
Using the MaskedEdit Control
The following characters have been defined as mask characters for the MaskedEdit control
- 0 - digit required
- 9 - digit optional
- L - lower case letter (a-z) required
- l - lower case letter optional
- U - upper case letter (A-Z) required
- u - upper case letter optional
- A - any case letter required
- a - any case letter optional
- D - letter or digit required
- d - letter or digit optional
- C - any char including punctuation
If you are not familiar with mask characters, you can find information in the MSDN library under "InputMask Property"
Regular expressions are used to define each mask char above. So for example '0' and '9' mask characters are coded as:
m_regexps.Add('0', @"[0-9]");
m_regexps.Add('9', @"[0-9 ]");
You can find all the other mask character RegExp's in the source code for the MaskedEdit control. The space ' ' character is used as the optional character. Validation code in other parts of the MaskedEdit control use the space character to determine if an entry is required. This is a key consideration if you decide to define your own input mask characters.
The input mask chars that I've defined are basically groups of digits, upper and lower case letters along with ANY mask character 'C'. I think these should be suitable for many circumstances but if you have a need for more finely tuned input restrictions, the MaskedEdit control can handle it. For example, suppose you have a need to input hexadecimal values. None of the current input mask chars handle hex digits (0 through 9 plus A through F). So lets add a new mask char for hex digits, which would look like
m_regexps.Add('H', @"[0-9A-F]");
This regexp would permit any of these characters "0123456789ABCDEF". So an InputMask for a 16 bit hex value might look like "\0xHHHH" would be displayed as 0x____ with 4 positions for hex digit entry. This means that you can adapt the MaskedEdit control to handle new input requirements. Don't forget that if you add an input mask char '-', that the standard InputMasks will be adversely affected.
If you're paying close attention you may have a question about the InputMask above. What is the '\' char? If you need to add a literal char that is already defined as a mask char then you will need to use the escape char '\'. This causes the MaskedEdit control to consider the next InputMask character a literal instead of an input mask character. For example, let suppose we want an InputMask to allow the user to input part numbers in the form
UL-1234
where UL- are literals and 1234 represent required digits. Both 'U' and 'L' are input mask characters for Upper and Lower case characters. Adding literal 'U' or 'L' means that we must add the escape character to designate them as literals instead of input mask characters, so our InputMask would be
\U\L-0000
Note that the '-' does not need the escape char prefix since it is not defined as a mask char.
MaskedEdit Properties
Next lets look at how to get and set the value contained in the MaskedEdit control.
Text Property - This property sets/gets the entire string contained in the control including literals. So an SSN input mask with data filled in such as
123-45-6789
will return "123-45-6789".
Value Property - This property sets/gets ONLY the input chars from the control excluding literals. So the example above would return "123456789". The value prop does NOT return optional chars that have not been entered by the user so for example:
(___) 123-4567
entered into a phone number mask would return "1234567" from the Value prop. Optional input mask chars cause some interesting problems that I have worked to handle. I would recommend using the MaskedEditTest application to test any InputMask you plan to use to make sure it works as you would expect.
The remainder of the MaskedEdit properties are pretty sraight forward.
- IsValid Property - Returns true if all required input chars have been entered correctly.
- InputChar Property - Defaulted to '_' underscore char but can be set to any other char except input mask chars or the escape char.
- ErrorInvalid Property - Defaulted to false. Setting this to true will cause the MaskedEdit control to throw errors if attempts are made to set Text or Value props to an invalid string. Setting this to true during debugging could prove very useful.
- StdInputMask Property - enum to select a standard input mask including
- None - makes the MaskedEdit control work like a standard text box
- SSN
- Phone
- Zip
- Custom - use this to define custom input mask
- InputMask Property - Use this to set custom InputMasks. The InputMask CAN be changed during runtime. The MaskedEdit control attempts to convert the current text into the new mask. This is LIKELY to cause problems. If you need to reset the mask during runtime, I would recommend setting the Value to an empty string prior to resetting the InputMask.
Usage Considerations
The MaskedEdit should work well for inputting "known" values such as SSN, phone and zip. The important point is that the user must KNOW what is to be entered since the mask doesn't give any clues as to the type of input expected (digits?, alpha chars?, ???). Tool tips or help files might be useful to explain complex input masks. Providing messages when invalid characters are entered by the user is a possible improvement. I am considering adding an InValid event, and an InValidMessage property to allow developers to add customized messages fired during invalid entry. Let me know your thoughts.
Setting the InputMask to only literal chars makes this a label control. I considered adding handling for this possibility but decided against it. If you try this, be prepared to catch errors :-\
Selection of characters in the MaskedEdit control is designed to keep the selection on input chars only and to skip over literals. This causes a somewhat peculiar selection behavior.
The base class TextBox has an extensive number of properties. I have added handling for some of these properties that I know affect the MaskedEdit control. There may be other props that I haven't considered that will cause problems or errors. Please post a note if you find one.
TODO
- Test, Test, Test I've spent more time than I would like to admit testing this control but as anything with this level of complexity, undiscovered bugs may exist. Please post a note if you find a bug.
- Add data binding features. I would anticipate binding the Text, Value, and InputMask properties. Let me know your thoughts on this.
- The MS Access has a feature to automatically upper/lower case input '<' = lower case, '>' = uppercase. This feature is not included in the MaskedEdit control. Let me know if you have a need for this feature, and I will work to add it on Rev. 2
History
- Released Rev 1.0 4/2/2003