Introduction
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 TextBox
control.
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.
Background
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
The FilterTextBox
control inherits from the standard .NET TextBox
control. It extends the capabilities of this control by adding the following members...
Name |
Type |
Description |
TextChanging |
event |
Event first when Text property is changing, but before it is changed. |
OnTextChanging() |
void |
Invoked when Text property is changing, but before it is changed. |
TextChangingWarn() |
void |
Invoked when event is canceled. If not overridden, it sounds an audible beep. |
CaretPosition |
int |
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 true
. Setting Cancel
to this value, prevents the Text
from being inserted.
protected override void OnTextChanging (
TextChangingEventArgs e )
{
if (!e.Cancel && !e.IsDelete)
{
string text;
if (e.IsAssign)
text = e.AfterText;
else
text = e.InsertedText;
foreach(char charValue in text)
{
if ( !Char.IsDigit(charValue) )
{
e.Cancel = true;
break;
}
}
}
base.OnTextChanging(e);
}
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...
Name |
Type |
Description |
Cancel |
bool |
If set to true, the proposed changes to the Text property will be cancelled. |
IsAssign |
bool | If true, the event is associated with assignment to the Text property. |
IsDelete | bool | If true, the
event is associated with deletion of characters. |
IsInsert | bool | If true, the event is associated with insertion of characters. |
AfterText | string | Proposed value of Text property if event is not canceled. |
BeforeText | string | Original value of Text property prior to event. |
InsertText | string | If IsInsert is true, contains the text that is being inserted; otherwise, it contains the value null . |
Type | TextChangingType | The type of operation that caused the TextChanging event. |
BeforeRemoveStart | int | 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. |
BeforeRemoveLength | int | 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: Backspace
, Clear
, and Cut
. The insertion types are: KeyPress
and Paste
.
Implementation
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 TextBox
control.
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
overriding the 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 KeyPressEventArgs.Handled
to true
.
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
operations.
All sources of change, except non-control key strokes, are detailed in the table below...
Category | Name | Intercept | TextChangingType |
Context Menu | Delete | FilterTextBox.WndProc() | Clear |
Context Menu | Cut | FilterTextBox.WndProc() | Cut |
Context Menu | Paste | FilterTextBox.WndProc() | Paste |
Keyboard Input | Delete | FilterTextBox.OnKeyDown() | Clear |
Keyboard Input | Shift+Delete | FilterTextBox.WndProc() | Cut |
Keyboard Input | Control+X | FilterTextBox.WndProc() | Cut |
Keyboard Input | Shift+Insert | FilterTextBox.WndProc() | Paste |
Keyboard Input | Control+V | FilterTextBox.WndProc() | Paste |
Keyboard Input | Backspace | FilterTextBox.OnKeyPress() | Backspace |
Method | TextBox.AppendText() | n/a | n/a |
Method | TextBox.Clear() | FilterTextBox.Text | Assignment |
Method | TextBox.Cut() | FilterTextBox.WndProc() | Cut |
Method | TextBox.Paste() | FilterTextBox.WndProc() | Paste |
Method | TextBox.ResetText() | FilterTextBox.Text | Assignment |
Property | TextBox.Text | FilterTextBox.Text | Assignment |
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.
Conclusions
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
).
References
TextBox class (MSDN), http://msdn2.microsoft.com/en-us/library/system.windows.forms.textbox.aspx
Revision History
03-17-2007
Original article.