This article describes the
PreviewTextBox control (derived from the standard WPF
TextBox control). This control adds a
to allow for simple and flexible validation of user input.
By subscribing to this event, or overriding the
OnPreviewTextChanged method, almost any imaginable filtering can be easily implemented. For example, the control
could form the basis for a WPF equivalent to the WinForm
MaskedTextBox control. However, this exceeded my requirements.
The demo project also provides a sample control,
IntTextBox, derived from it. The sample control limits the user to entering valid integral values.
The demo project simply displays this sample control to show it in action.
Previously, I wrote an article about a WinForm control that served a similar purpose. I found a need for this control in WPF, but wanted one native to the WPF environment.
This article describes the new implementation. Hopefully, others may find it useful.
Many similar articles exist. However, none covered all of the different ways a user might introduce bad data. Most focused solely on user-typed input and paste.
Using the Code
To the end user,
PreviewTextBox is nearly identical to the standard WPF
TextBox control. It simply adds the
I wish MS had included in their implementation.
To use it in XAML, simply add a namespace to the root element, like so:
A full-context example of this can be found in the MainWindow.xaml file from the demo project, and looks like this:
Title="Preview Text" Height="100" Width="225">
Once you've done this, you can reference any control in the
Extended.Windows.Controls CLR namespace by preceding them with the
ex prefix in the XAML. For example:
<ex:IntTextBox Grid.Row="0" Grid.Column="1" Margin="4,0" />
Two approaches to validation are possible. In the first, the end user simply subscribes to the
true in the event handler will cancel the change.
In the second, used in the
IntTextBox sample control, the
OnPreviewTextChanged method is overridden. By creating a derived control,
the same validation can more easily be re-used in XAML.
For those interested in how
PreviewTextBox is implemented, the code is fully commented. I will briefly describe the highlights of that implementation
in the remainder of this article.
There are three major integration points where native WPF
TextBox events are intercepted to raise the
PreviewExecutedEvent (from the command manager).
In all cases, the code predicts the resulting text (as if the change had already occurred). This resulting text is provided as a property of the arguments
PreviewTextChanged event. If the handler sets
true in the
PreviewTextChanged handler, this same value is communicated
to the original event handler.
PreviewTextInput is raised for most of what a user types. It is intercepted by overriding the
PreviewKeyDown is raised when a user presses a key. It is intercepted separately from
PreviewTextInput because, for some reason,
the space character does not raise that event. It is intercepted by overriding the
PreviewExecutedCommand is raised for many different editing commands (delete, backspace, etc.) and application commands (Cut, Paste, etc.).
It is intercepted by adding a handler in the overridden
OnInitialized method. The handler is added as follows:
new ExecutedRoutedEventHandler(previewExecutedEvent), true);
This results in the
previewExecutedEvent method being invoked each time the user initiates one of these commands for the
Points of Interest
The code does not currently intercept programmatic assignments to the
Text property. I was unable to reliably intercept such changes.
Also, they were not necessary for filtering user input.
The code does not, by default, intercept the application commands Undo and Redo. The mechanism for intercepting these commands is rather messy.
Because the undo stack is not available, it requires an undo and redo (or the reverse) simply to predict the text that would result. A property
is provided to enable this optional logic. However, with proper validation elsewhere, neither Undo or Redo can introduce invalid values.
- 8/1/2011 - The original version was uploaded.