This StringBox control implements a hybrid approach using regular expressions for key stroke validation and a touch of glue logic for big picture validation. These components also include a function to indicate when the entered text is 100% valid for the final pattern.
Introduction
This article illustrates a method of key stroke validation using regular expressions and some simple logic. A regular expression pattern is used to validate key entries regardless of cursor position. Presented here are 6 different classes for 6 different string patterns. The user can create new patterns as needed. A Boolean function indicates the StringBox
Text is complete and ready for use by the application.
Background
Some of the desktop applications I develop require entry of IP addresses. What I needed was a simple way for users to enter characters into the IP address box while only allowing them to enter the correct characters based on cursor position. Regular expressions are an excellent way to validate the final string, however they cannot validate partial strings as they are typed in. The classes and patterns in this code resolve this issue.
Using the Code
Demo Window
Below is a window to demo these classes. The six text boxes will only accept the appropriate keystrokes as specified by their respective labels. The check boxes will become checked when the StringBox
has a 100% valid and complete string
. Download the executable and give it a spin.
The main code behind this window is very simple.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void sbIPv4_TextChanged(object sender, EventArgs e)
{
cbIPv4.Checked = ((StringBox.StringBox)sender).IsComplete();
}
private void sbPort_TextChanged(object sender, EventArgs e)
{
cbPort.Checked = ((StringBox.StringBox)sender).IsComplete();
}
private void sbIPv4Port_TextChanged(object sender, EventArgs e)
{
cbIPv4Port.Checked = ((StringBox.StringBox)sender).IsComplete();
}
private void sbEmailAddress_TextChanged(object sender, EventArgs e)
{
cbEmailAddress.Checked = ((StringBox.StringBox)sender).IsComplete();
}
private void sbIPv6_TextChanged(object sender, EventArgs e)
{
cbIPv6.Checked = ((StringBox.StringBox)sender).IsComplete();
}
private void sbNANPPhoneNumber_TextChanged(object sender, EventArgs e)
{
cbNANPPhoneNumber.Checked = ((StringBox.StringBox)sender).IsComplete();
}
}
As you can see, the check boxes are set when their respective StringBox TextChanged
events fire.
StringBox Class
Validating keystrokes to match a specific pattern is a 2-step process. The first step is a check that only appropriate characters are accepted based on cursor position. As an example, an IP address consists of four bytes presented in decimal separated by a dot. Each part ranges from 0-255. Regular expressions patterns can be easily created to do this. An IP address regular expression pattern might look like this:
[\d]{1,3}[.][\d]{1,3}[.][\d]{1,3}[.][\d]{1,3}
This will only match when we have a full expression to check. So, checking the text as it is entered will not work. To resolve this problem, we change the pattern such that it matches all possible shorter versions of a complete IP address. We might use this pattern:
[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}
Notice with this pattern we will get a match regardless of how much of the string has been entered but only digits and dots are allowed in their proper positions.
Now let’s look at the keypress code that uses this pattern in the base class StringBox
:
private void StringBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.Handled) return;
var temp = SelectionLength > 0
? Text.Substring(0, SelectionStart) + e.KeyChar +
Text.Substring(SelectionStart + SelectionLength)
: Text.Insert(SelectionStart, e.KeyChar.ToString());
var handled = !(regex.IsMatch(temp) && (regex.Match(temp).Length == temp.Length));
if (!handled)
handled = !IsValid(temp);
e.Handled = handled;
}
This code creates a temp
string
which is the Text
string
plus the keystroke inserted into the correct position. Then it checks this temp
string
against the regular expression pattern for a match and that the match length is the same as the temp
length. If either condition is not met, then we reject key stroke by setting handled = true
.
Up to this point, an IP address of 999.999.999.999
would be acceptable. This is where the second step comes in. The keypress
event must also validate the temp
string using the virtual function IsValid
. If it fails this check, then the keystroke
is also rejected. Each derived class should override the virtual function.
StringIPv4 Class
Looking at StringIPv4 class code below:
public class StringIPv4 : StringBox
{
public StringIPv4()
{
Pattern = @"[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}[.]?[\d]{0,3}";
}
public override bool IsComplete()
{
var s = Text.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
return s.Length == 4;
}
protected override bool IsValid(string str)
{
var s = str.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var t in s)
{
var x = Convert.ToInt32(t);
if (x < 0 || x > 255) return false;
}
return true;
}
}
First, we see that this class is derived from StringBox
class and has a regular expression pattern definition in the constructor.
The IsValid
function overrides the virtual base class function. This function splits the temp
string into discrete parts delimited by the dot and checks to make sure each part is range limited to 0-255. If the value is not in this range, then it returns false
which would reject the keystroke.
The IsComplete
function will normally be called by the application to check if the Text
string is complete and ready to be used. In this case, all we need to check for is are there 4 bytes in the string
. IsValid
already checked that each part is range limited. An alternate method would be to parse the Text
as an IP address of type InterNetwork
. Then the full string
is a completely valid IPv4 address.
The six specific classes presented cover common application string
types. The goal is to use regular expressions for keystroke checking and simple high-level checks to validate the string
s. These classes will show up in your Visual Studio Toolbox window for drag and drop onto your windows.
Comments
To create a new StringBox
class:
- Derive the new
StringBox
from the base class - Create a proper pattern as part of step 1. The pattern must match all possible sub-strings of the final
string
. - Create an
IsComplete
function as a final test of the Text
string
. - Create an
IsValid
function as part of step 2 for keystroke checking.
Points of Interest
Creating this code showed me a new way to use unconventional regular expressions. Basically how to use a regular expression that seems to match a string
plus all sub-string
s of the pattern. Or how regular expressions can be used for more than just full string
validation.
Credits
This is only one way in many ways to do keystroke validation in a textbox
. I have no direct links to specific sources but a quick search here in CodeProject will turn up many.
The only link I can reference is a distant cousin, NumberBox, that I released a while back. It does keystroke validation for SNFS number formats.
History
- 22nd July, 2020: Initial version