Friday, August 19, 2011

C# Numbers Only Textbox


Number Input

Many times it is necessary for a C# Textbox control to only receive only numbers as input. A common technique used is to check the text to make sure it is a number and throw an exception if it is not.
But what if we could do one better? We can make a C# Textbox accept only numbers (integers).With the KeyPress C# event, this task is relatively simple and does not require a custom C# control. We will look over two common methods that are somewhat limited, and a better alternative at the end (if you are in a hurry, skip to the textbox that handles important non-integer keys).
7/5/11 Update: added third method, accounts for valid non-intergers keys
8/1/11 Update: Detect invalid paste events, allow navigation keys

Check KeyStrokes - TryParse

The first method of checking keystrokes is simple and available starting with the 2.0 version of the .Net Framework. The way to do it is by parsing the string as an integer with the TryParse C# function.
The TryParse function returns either a 0 or a number. 0 means that the string was not a pure number. The behavior is very convenient, so we can simple use:
private void txtType1_KeyPress(object sender, KeyPressEventArgs e)
{
     int isNumber = 0;
     e.Handled = !int.TryParse(e.KeyChar.ToString(), out isNumber);
}
The outcome of the parsing is not important. What matters here is if TryParse succeeded in parsing, which means the keystroke was a number. If e.Handled is set to true, that tells the textbox that you the programmer will take care of the keystroke, so setting it to true makes it ignore the keystroke...

Check KeyStrokes - Regex

However if we want to write more efficient C# code, it is better and faster to use Regular Expressions. Regular Expressions is a huge topic that I cannot hope to cover in a short article. However for the sake of simplicity, regular expressions are string combinations that are used to quickly match specific patterns in a body of text.
The one we need is "\\d+" which checks a single character to be a digit. That's it, C# regex at its finest:
private void txtType2_KeyPress(object sender, KeyPressEventArgs e)
{
     if (!System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), "\\d+"))
          e.Handled = true;
}

Important non-integer Keys

The two methods above have a significant limitation, they suppress keystrokes that are not numbers but are perfectly valid, for example Backspace, Delete, Ctrl + C, etc. Writing an exception case for each of these would be rather inefficient. A better way is to flip it around and instead of checking that the input is valid (only numbers), we can for invalid inputs and suppress these.
private void txtType3_KeyPress(object sender, KeyPressEventArgs e)
{
    if (char.IsLetter(e.KeyChar) ||
        char.IsSymbol(e.KeyChar) ||
        char.IsWhiteSpace(e.KeyChar) ||
        char.IsPunctuation(e.KeyChar))
        e.Handled = true;
}  
Now the textbox will accept number input as well as Backspace, delete, shift, the arrow keys, etc. However, as pointed out by a reader, it is now possible to paste text that has non-numeric characters.
To get around this issue, we first need to preview what the text is, and then either completely block the paste event for invalid inputs or paste only the numbers in the input. For our example, let's actually copy the parts of the pasted text that are valid (the numbers only).
private void txtType3_KeyDown(object sender, KeyEventArgs e)
{
    //Allow navigation keyboard arrows
    switch (e.KeyCode)
    {
        case Keys.Up:
        case Keys.Down:
        case Keys.Left:
        case Keys.Right:
        case Keys.PageUp:
        case Keys.PageDown:
            e.SuppressKeyPress = false;
            return;
        default:
            break;
    }

    //Block non-number characters
    char currentKey = (char)e.KeyCode;
    bool modifier = e.Control || e.Alt || e.Shift;
    bool nonNumber = char.IsLetter(currentKey) || 
                     char.IsSymbol(currentKey) || 
                     char.IsWhiteSpace(currentKey) || 
                     char.IsPunctuation(currentKey);

    if (!modifier && nonNumber)
        e.SuppressKeyPress = true;

    //Handle pasted Text
    if (e.Control && e.KeyCode == Keys.V)
    {
        //Preview paste data (removing non-number characters)
        string pasteText = Clipboard.GetText();
        string strippedText = "";
        for (int i = 0; i < pasteText.Length; i++)
        {
            if (char.IsDigit(pasteText[i]))
                strippedText += pasteText[i].ToString();
        }

        if (strippedText != pasteText)
        {
            //There were non-numbers in the pasted text
            e.SuppressKeyPress = true;

            //OPTIONAL: Manually insert text stripped of non-numbers
            TextBox me = (TextBox)sender;
            int start = me.SelectionStart;
            string newTxt = me.Text;
            newTxt = newTxt.Remove(me.SelectionStart, me.SelectionLength); //remove highlighted text
            newTxt = newTxt.Insert(me.SelectionStart, strippedText); //paste
            me.Text = newTxt;
            me.SelectionStart = start + strippedText.Length;
        }
        else
            e.SuppressKeyPress = false;
    }
}
The trickiest part is manually simulating the paste event of the textbox. Additionally, our previous version blocked the keyboard navigation keys (i.e. left arrow, right arrow, etc.). We fix this by explicitly allowing those keys.

No comments:

Post a Comment

Feel the Pulse of Tech' !!!

Feel the Pulse of Tech' !!!
Ur's may stop, but this will Beat 4 EVER