Rather a long gap between my last post and this...v. poor effort, must try harder.
Actually, I have been working on a project which involves a good deal of WPF, so have been busily getting to grips with its finer points, so not had much to post in the way of interesting information, and quite a lot of reading other people's blogs for what interesting information I could myself misappropriate...sorry, err...I mean absorb.
Well, it turned out we needed a MaskedTextBox control, which is conspicuously missing from the current WPF control set. We didn't need anything as flexible as the Windows Forms one- just one that would allow us to constrain the entering of such format-dependent strings as time, bank account numbers, sort codes etc.
This I achieved by inheriting from the WPF TextBox, and overriding the OnPreviewKeyDown and OnPreviewTextInput methods. The former is invoked OnKeyDown, as you would expect, and the latter when a keypress results in a character which will be appended or inserted at the current caret position.
I exposed a dependency property of string type representing the current input mask, then hooked up the property changed event, repopulating a collection of objects (one for each character in the input mask) which encapsulate the validation rules for the corresponding character position in the textbox.
The input mask syntax itself is very simple; it allows input to be constrained in 4 different ways, each indicated by a different placeholder character in the mask string (much like the Windows Forms equivalent, albeit much simplified). The 4 placeholder characters are:
- i - allows integers only.
- d - allows decimals only (i.e. integers, plus a single decimal point)
- a - allows alphabetic characters only (i.e. a-z, A-Z)
- w - allows alphanumeric characters only (i.e. a-z, A-Z, 0-9)
Everything in the input mask which is not one of the above characters is treated as a literal by the control, and therefore permanently visible in the input textbox, and not erasable.
So, taking one of the input field types mentioned above: the input mask for a date would look like this:
ii:ii
Which indicates two sets of 2-integer numbers, separated by a colon.
Note that this does not prevent the user from entering an invalid date-- they could still enter 99:99, and the textbox would allow it, but for my purposes, I am not interested in ensuring users enter a valid value- this will be picked up by the validation, which occurs later. The purpose of this control was simply to ensure that the input is correctly formatted.
Of course, there are all kinds of other options you could add in terms of input mask flexibility, and the windows forms control is a very good example of a much richer input mask feature set, but this control fulfills my current requirements, and is readily extensible. That said, I would imagine that an official WPF masked textbox will most likely be released with the next version of WPF, which will render this entirely redundant!
In the meantime, you can download the control's source code using the link above.
Edit (9/1/2009) : I have uploaded version 1.2 of the source code which addresses a few bugs in the initial version, in particular allowing the control to work in data binding scenarios.
Edit (12/2/2009) : Version 1.3 of the source code uploaded which fixes a minor text input bug in version 1.2, caused by some odd behaviour in the Text_CoerceValue method.