Introduction
This article describes the PreviewTextBox
control (derived from the standard WPF TextBox
control). This control adds a PreviewTextChanged
event
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.
Background
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 PreviewTextChanged
event
I wish MS had included in their implementation.
To use it in XAML, simply add a namespace to the root element, like so:
xmlns:ex="clr-namespace:Extended.Windows.Controls"
A full-context example of this can be found in the MainWindow.xaml file from the demo project, and looks like this:
<Window x:Class="PreviewText.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ex="clr-namespace:Extended.Windows.Controls"
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 PreviewTextChangedEvent
.
Setting e.Handled
to 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 PreviewTextChanged
event.
These include PreviewTextInput
, PreviewKeyDown
, and 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
to the PreviewTextChanged
event. If the handler sets e.Handled
to 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 OnPreviewTextInput
method.
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 OnPreviewKeyDown
method.
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:
AddHandler(CommandManager.PreviewExecutedEvent,
new ExecutedRoutedEventHandler(previewExecutedEvent), true);
This results in the previewExecutedEvent
method being invoked each time the user initiates one of these commands for the PreviewTextBox
control.
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 PreviewUndoEnabled
is provided to enable this optional logic. However, with proper validation elsewhere, neither Undo or Redo can introduce invalid values.
History
- 8/1/2011 - The original version was uploaded.