Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

StringBox Control with Keystroke Validation

0.00/5 (No votes)
22 Jul 2020 1  
This StringBox control implements keystroke validation using regular expressions and a touch of glue logic
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.

Image 1

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;

    // insert key into temp string
    var temp = SelectionLength > 0
        ? Text.Substring(0, SelectionStart) + e.KeyChar + 
          Text.Substring(SelectionStart + SelectionLength)
        : Text.Insert(SelectionStart, e.KeyChar.ToString());

    // if doesn't match pattern or length then reject the key by setting handled = true
    var handled = !(regex.IsMatch(temp) && (regex.Match(temp).Length == temp.Length));

    // do an additional check to make sure individual parts are correct
    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}";
    }

    /// <summary>
    /// Check if entire string is valid IPv4 string
    /// </summary>
    /// <returns>true if valid IPv4 string</returns>
    public override bool IsComplete()
    {
        // make sure there are 4 parts
        var s = Text.Split(new[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
        return s.Length == 4;
        // do not need to do range check since 'IsValid' already does this
        // alternate IPv4 check method
        //if (!IPAddress.TryParse(Text, out var address)) return false;
        //if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) 
        //    return true;
        //return false;
    }

    /// <summary>
    /// Checks if 'str' matches IPv4 complete pattern
    /// Called once each keystroke
    /// </summary>
    /// <param name="str">temp string to check</param>
    /// <returns>true if string is valid</returns>
    protected override bool IsValid(string str)
    {
        // make sure each part is range limited
        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 strings. 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:

  1. Derive the new StringBox from the base class
  2. Create a proper pattern as part of step 1. The pattern must match all possible sub-strings of the final string.
  3. Create an IsComplete function as a final test of the Text string.
  4. 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-strings 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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here