Have you ever wanted to filter user input for a
TextBox control? Are the other solutions on Code Project too narrowly focused for your requirements? Do you just want to better understand filtering in general? If so, this article is for you.
The article describes a base class,
FilterTextBox, that can be used to implement flexible filtering for a
The class exposes a new event
TextChanging that fires when the
Text property is about to change, but before it has actually changed. The event arguments include an
AfterText property to preview the proposed changes. They also include a
Cancel property that can be modified to cancel the changes.
What do I mean by "filtering"? What if you wanted a
TextBox control that restricted user input to only numbers? How would you go about implementing this? One solution would be to use the
Validating event. Unfortunately, this event does not fire until after focus leaves the control. So the user will, at least temporarily, see the invalid input.
Wouldn't it be nice to prevent these characters from being seen in the first place? This is the problem solved by filtering.
The truth is, if your filtering needs are as simple as those just described, you should probably use the
MaskedTextBox control introduced in .NET 2.0.
Why provide another solution here? This is a fair question. In my case, I don't have .NET 2.0 yet, my filtering needs are more complex, and I just like to understand how things work.
I provide this class in case others have similar needs. Also, I think a
TextChanging event might be useful for other purposes.
Using the code
FilterTextBox control inherits from the standard .NET
TextBox control. It extends the capabilities of this control by adding the following members...
|Event first when Text property is changing, but before it is changed.
|Invoked when Text property is changing, but before it is changed.
|Invoked when event is canceled. If not overridden, it sounds an audible beep.
|Index of caret position within text.|
The most common use of the
FilterTextBox control is likely to be as a base class for other controls. In this case, the simplest way to use it is by overriding the
OnTextChanging method. The following example, from the included
NumericTextBox control, demonstrates this usage. In this example, newly inserted text is scanned for non-numeric characters. If any are found, the
Cancel property of the event arguments is set to
Cancel to this value, prevents the
Text from being inserted.
protected override void OnTextChanging (
TextChangingEventArgs e )
if (!e.Cancel && !e.IsDelete)
text = e.AfterText;
text = e.InsertedText;
foreach(char charValue in text)
if ( !Char.IsDigit(charValue) )
e.Cancel = true;
Alternatively, it is possible to simply subscribe to the
TextChanging event. An example of this approach is provided via the
alphabeticTextBox_TextChanging method in
FormMain. Since the code is nearly identical to the previous example, it is not shown here.
In each of these examples the
TextChangingEventArgs class is used to convey information about the
TextChanging event. It contains the following members...
|If set to true, the proposed changes to the
Text property will be cancelled.
|If true, the event is associated with assignment to the |
|If true, the
event is associated with deletion of characters.|
|If true, the event is associated with insertion of characters.|
|Proposed value of |
Text property if event is not canceled.
|Original value of |
Text property prior to event.
|If IsInsert is true, contains the text that is being inserted; otherwise, it contains the value |
|The type of operation that caused the |
|The index of the first character, in the original text, that is being removed or replaced. If IsInsert is true, it also specifies the index, in the original text, at which characters will be inserted.|
|The number of characters, in the original text, that are being removed or replaced.|
TextChangingType is simply an enumeration of all the operations that can cause a
TextChanging event. They fall into three categories: assignment (see
IsAssign), deletion (see
IsDelete), and insertion (see
IsInsert). The only assignment type is
Assign. The deletion types are:
Cut. The insertion types are:
The main challenge in implementing the
FilterTextBox was identifying all of sources of text change for the
TextBox control. Once each of these was identified, it was necessary to discover a mechanism for intercepting each change and canceling it.
To this end, I created a table for each source of change I could find. These fell into four categories: the context menu for the
TextBox, keyboard input, methods of the
TextBox control, and properties of the
Keyboard input is the most obvious source of change, since this is what the control is designed to filter. All non-control characters are intercepted by
OnKeyPress method. A
TextChanging event of type
TextChangingType.KeyPress is fired for each keystroke. If the event is canceled, by one of the subscribers, the key stroke is discarded by setting the
The backspace and delete key are similarly intercepted and canceled. For operations like Clear, Cut, and Paste; overriding the
WndProc method to intercept the corresponding Windows message seemed the most economical solution. While not pure .NET, it was the only practical means I could identify for transparently trapping the context menu
All sources of change, except non-control key strokes, are detailed in the table below...
Regrettably, I could not identify a means to intercept and cancel changes from the
TextBox.AppendText() method. It can not be overridden and does not assign to the
Text property to change its value. While this is unfortunate, it is unlikely to affect most applications.
Given the plethora of other postings on this subject, I hesitated to post this article. However, none of the solutions I found in a quick search aspired to be a general purpose base class for TextBox filtering. Instead each seemed to be narrowly targeted at solving a specific filtering need (for example, a numeric
TextBox class (MSDN), http://msdn2.microsoft.com/en-us/library/system.windows.forms.textbox.aspx