21

How can I only allow certain characters in a Visual C# textbox? Users should be able to input the following characters into a text box, and everything else should be blocked: 0-9, +, -, /, *, (, ).

I've used Google to look up this problem, but the only solutions I'm getting are allowing only alphabetic characters, only numerical or disallowing certain characters. What I want is not disallowing certain characters, I want to disallow everything by default except the characters that I put in the code.

Kendall Frey
  • 43,130
  • 20
  • 110
  • 148
Annoying Bot
  • 359
  • 1
  • 5
  • 13
  • 4
    ASP.NET? WinForms? WPF? All of these can use C#, and all are different. – David Sep 26 '12 at 17:21
  • It's a Windows Forms Application. – Annoying Bot Sep 26 '12 at 17:22
  • 1
    Have you tried creating a event handler for the PropertyChanged event that deletes the character if it is invalid? – Bob. Sep 26 '12 at 17:26
  • Do you want it to not accept invalid input? Like if the user types a `$` do you want it to just ignore that keystroke? What behavior is desired? – Ghost Sep 26 '12 at 17:27
  • Exactly, when the user presses $, it is simply ignored and left white on the textbox. When the user presses a legitimate character, it should be displayed. – Annoying Bot Sep 26 '12 at 17:30

6 Answers6

31

As mentioned in a comment (and another answer as I typed) you need to register an event handler to catch the keydown or keypress event on a text box. This is because TextChanged is only fired when the TextBox loses focus

The below regex lets you match those characters you want to allow

Regex regex = new Regex(@"[0-9+\-\/\*\(\)]");
MatchCollection matches = regex.Matches(textValue);

and this does the opposite and catches characters that aren't allowed

Regex regex = new Regex(@"[^0-9^+^\-^\/^\*^\(^\)]");
MatchCollection matches = regex.Matches(textValue);

I'm not assuming there'll be a single match as someone could paste text into the textbox. in which case catch textchanged

textBox1.TextChanged += new TextChangedEventHandler(textBox1_TextChanged);
private void textBox1_TextChanged(object sender, EventArgs e)
{
    Regex regex = new Regex(@"[^0-9^+^\-^\/^\*^\(^\)]");
    MatchCollection matches = regex.Matches(textBox1.Text);
    if (matches.Count > 0) {
       //tell the user
    }
}

and to validate single key presses

textBox1.KeyPress += new KeyPressEventHandler(textBox1_KeyPress);
private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
    // Check for a naughty character in the KeyDown event.
    if (System.Text.RegularExpressions.Regex.IsMatch(e.KeyChar.ToString(), @"[^0-9^+^\-^\/^\*^\(^\)]"))
    {
        // Stop the character from being entered into the control since it is illegal.
        e.Handled = true;
    }
}
Paul D'Ambra
  • 7,629
  • 3
  • 51
  • 96
  • 5
    you might want to add extra check for key with code 8, otherwise backspace will not work – imaximchuk Apr 18 '13 at 23:01
  • In case anyone else runs into this... I was not able to get this to work in the `KeyPress` handler. Even though I set `e.Handled` to `true`, it still insert the character into the control. When I switched to handling `KeyDown` instead, everything worked fine. The control I am using is a `RichTextBox`, so that might have something to do with it. I didn't experiment much after I got it to work. – danBhentschel Apr 21 '15 at 14:40
  • 3
    As a side note, TextChanged is not fired when text loses focus, but just after a change in text (e.g. I use it to color a text if it's valid). It's too late tough. – Teejay Aug 22 '17 at 13:38
  • Brilliant answer – Hasan Shouman Apr 27 '19 at 17:21
  • When using pattern which should catch catches characters that aren't allowed, I think, regexp should be changed from `[^0-9^+^\-^\/^\*^\(^\)]` to `[^0-9+\-\/\*\(\)]` in order to catch "^" char too. – SGh May 08 '21 at 09:26
11

You need to subscribe to the KeyDown event on the text box. Then something like this:

private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
    if (!char.IsControl(e.KeyChar) 
       && !char.IsDigit(e.KeyChar) 
       && e.KeyChar != '.' && e.KeyChar != '+' && e.KeyChar != '-'
       && e.KeyChar != '(' && e.KeyChar != ')' && e.KeyChar != '*' 
       && e.KeyChar != '/')
    {
        e.Handled = true;
        return;
    }
    e.Handled=false;
    return;
}

The important thing to know is that if you changed the Handled property to true, it will not process the keystroke. Setting it to false will.

Icemanind
  • 47,519
  • 50
  • 171
  • 296
  • 2
    The KeyDown KeyEventArgs argument does not have a KeyChar property. Perhaps you meant KeyPressEventArgs and the KeyPress event, which does have that property? – Michael A. McCloskey Sep 10 '20 at 15:47
1

You can probably use the KeyDown event, KeyPress event or KeyUp event. I would first try the KeyDown event I think.

You can set the Handled property of the event args to stop handling the event.

Maarten
  • 22,527
  • 3
  • 47
  • 68
1

Intercept the KeyPressed event is in my opinion a good solid solution. Pay attention to trigger code characters (e.KeyChar lower then 32) if you use a RegExp.

But in this way is still possible to inject characters out of range whenever the user paste text from the clipboard. Unfortunately I did not found correct clipboard events to fix this.

So a waterproof solution is to intercept TextBox.TextChanged. Here is sometimes the original out of range character visible, for a short time. I recommend to implement both.

using System.Text.RegularExpressions;

private void Form1_Shown(object sender, EventArgs e)
{
    filterTextBoxContent(textBox1);
}


string pattern = @"[^0-9^+^\-^/^*^(^)]";

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    if(e.KeyChar >= 32 && Regex.Match(e.KeyChar.ToString(), pattern).Success) { e.Handled = true; }
}

private void textBox1_TextChanged(object sender, EventArgs e)
{
    filterTextBoxContent(textBox1);
}

private bool filterTextBoxContent(TextBox textBox)
{
    string text = textBox.Text;

    MatchCollection matches = Regex.Matches(text, pattern);
    bool matched = false;

    int selectionStart = textBox.SelectionStart;
    int selectionLength = textBox.SelectionLength;

    int leftShift = 0;
    foreach (Match match in matches)
    {
        if (match.Success && match.Captures.Count > 0)
        {
            matched = true;
            Capture capture = match.Captures[0];

            int captureLength = capture.Length;
            int captureStart = capture.Index - leftShift;
            int captureEnd = captureStart + captureLength;

            int selectionEnd = selectionStart + selectionLength;

            text = text.Substring(0, captureStart) + text.Substring(captureEnd, text.Length - captureEnd);

            textBox.Text = text;

            int boundSelectionStart = selectionStart < captureStart ? -1 : (selectionStart < captureEnd ? 0 : 1);
            int boundSelectionEnd = selectionEnd < captureStart ? -1 : (selectionEnd < captureEnd ? 0 : 1);

            if (boundSelectionStart == -1)
            {
                if (boundSelectionEnd == 0)
                {
                    selectionLength -= selectionEnd - captureStart;
                }
                else if (boundSelectionEnd == 1)
                {
                    selectionLength -= captureLength;
                }
            }
            else if (boundSelectionStart == 0)
            {
                if (boundSelectionEnd == 0)
                {
                    selectionStart = captureStart;
                    selectionLength = 0;
                }
                else if (boundSelectionEnd == 1)
                {
                    selectionStart = captureStart;
                    selectionLength -= captureEnd - selectionStart;
                }
            }
            else if (boundSelectionStart == 1)
            {
                selectionStart -= captureLength;
            }

            leftShift++;
        }
    }

    textBox.SelectionStart = selectionStart;
    textBox.SelectionLength = selectionLength;

    return matched;
}
Martin Wantke
  • 4,287
  • 33
  • 21
0

For your validation event IMO the easiest method would be to use a character array to validate textbox characters against. True - iterating and validating isn't particularly efficient, but it is straightforward.

Alternately, use a regular expression of your whitelist characters against the input string. Your events are availalbe at MSDN here: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.lostfocus.aspx

iivel
  • 2,576
  • 22
  • 19
-1
    private void txtuser_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (!char.IsLetter(e.KeyChar) && !char.IsWhiteSpace(e.KeyChar) && !char.IsControl(e.KeyChar))
        {
            e.Handled = true;
        }
    }
Robert
  • 5,278
  • 43
  • 65
  • 115