How do I make a textbox that only accepts numbers?

Viewed 1360320

I have a windows forms app with a textbox control that I want to only accept integer values. In the past I've done this kind of validation by overloading the KeyPress event and just removing characters which didn't fit the specification. I've looked at the MaskedTextBox control but I'd like a more general solution that could work with perhaps a regular expression, or depend on the values of other controls.

Ideally this would behave such that pressing a non numeric character would either produce no result or immediately provide the user with feedback about the invalid character.

40 Answers

Two options:

  1. Use a NumericUpDown instead. NumericUpDown does the filtering for you, which is nice. Of course it also gives your users the ability to hit the up and down arrows on the keyboard to increment and decrement the current value.

  2. Handle the appropriate keyboard events to prevent anything but numeric input. I've had success with this two event handlers on a standard TextBox:

    private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) &&
            (e.KeyChar != '.'))
        {
                e.Handled = true;
        }
    
        // only allow one decimal point
        if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
        {
            e.Handled = true;
        }
    }
    

You can remove the check for '.' (and the subsequent check for more than one '.') if your TextBox shouldn't allow decimal places. You could also add a check for '-' if your TextBox should allow negative values.

If you want to limit the user for number of digit, use: textBox1.MaxLength = 2; // this will allow the user to enter only 2 digits

And just because it's always more fun to do stuff in one line...

 private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
    }

NOTE: This DOES NOT prevent a user from Copy / Paste into this textbox. It's not a fail safe way to sanitize your data.

I am assuming from context and the tags you used that you are writing a .NET C# app. In this case, you can subscribe to the text changed event, and validate each key stroke.

private void textBox1_TextChanged(object sender, EventArgs e)
{
    if (System.Text.RegularExpressions.Regex.IsMatch(textBox1.Text, "[^0-9]"))
    {
        MessageBox.Show("Please enter only numbers.");
        textBox1.Text = textBox1.Text.Remove(textBox1.Text.Length - 1);
    }
}

Try a MaskedTextBox. It takes a simple mask format so you can limit the input to numbers or dates or whatever.

You can use the TextChanged event

private void textBox_BiggerThan_TextChanged(object sender, EventArgs e)
{
    long a;
    if (! long.TryParse(textBox_BiggerThan.Text, out a))
    {
        // If not int clear textbox text or Undo() last operation
        textBox_LessThan.Clear();
    }
}

Just use a NumericUpDown control and set those ugly up down buttons visibility to false.

numericUpDown1.Controls[0].Visible = false;

NumericUpDown is actually a collection of controls containing a 'spin box' (up down buttons), a text box and some code to validate and wange-jangle it all together.

Marking:

YourNumericUpDown.Controls[0].visible = false 

will hide the buttons while keeping the underlying code active.

While not being an obvious solution it is simple and effective. .Controls[1] would hide the text box portion if you wanted to do that instead.

I have made something for this on CodePlex.

It works by intercepting the TextChanged event. If the result is a good number it will be stored. If it is something wrong, the last good value will be restored. The source is a bit too large to publish here, but here is a link to the class that handles the core of this logic.

This is a nice and short way to do it with .NET 5/Core

private void textBox1_KeyDown(object sender, KeyEventArgs e) {
  if (e.KeyData != Keys.Back)
    e.SuppressKeyPress = !int.TryParse(Convert.ToString((char) e.KeyData), out int _);
}

Edit: Added support for backspace key

you could use TextChanged/ Keypress event, use a regex to filter on numbers and take some action.

Take a look at Input handling in WinForm

I have posted my solution which uses the ProcessCmdKey and OnKeyPress events on the textbox. The comments show you how to use a Regex to verify the keypress and block/allow appropriately.

Using the approach described in Fabio Iotti's answer I have created a more generic solution:

public abstract class ValidatedTextBox : TextBox {
    private string m_lastText = string.Empty;
    protected abstract bool IsValid(string text);
    protected sealed override void OnTextChanged(EventArgs e) {
        if (!IsValid(Text)) {
            var pos = SelectionStart - Text.Length + m_lastText.Length;
            Text = m_lastText;
            SelectionStart = Math.Max(0, pos);
        }
        m_lastText = Text;
        base.OnTextChanged(e);
    }
}

"ValidatedTextBox", which contains all nontrivial validation behavior. All that's left to do is inherit from this class and override "IsValid" method with whatever validation logic is required. For example, using this class, it is possible to create "RegexedTextBox" which will accept only strings which match specific regular expression:

public abstract class RegexedTextBox : ValidatedTextBox {
    private readonly Regex m_regex;
    protected RegexedTextBox(string regExpString) {
        m_regex = new Regex(regExpString);
    }
    protected override bool IsValid(string text) {
        return m_regex.IsMatch(Text);
    }
}

After that, inheriting from the "RegexedTextBox" class, we can easily create "PositiveNumberTextBox" and "PositiveFloatingPointNumberTextBox" controls:

public sealed class PositiveNumberTextBox : RegexedTextBox {
    public PositiveNumberTextBox() : base(@"^\d*$") { }
}

public sealed class PositiveFloatingPointNumberTextBox : RegexedTextBox {
    public PositiveFloatingPointNumberTextBox()
        : base(@"^(\d+\" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + @")?\d*$") { }
}

Here are more than 30 answers and a lot of answers are helpful. But I want to share a generalized form for the System.Windows.Forms.TextBox and System.Windows.Controls.TextBox.

There is not available KeyPress event in System.Windows.Controls.TextBox. This answer is for those people who want to implement with the same logic for System.Windows.Forms.TextBox and System.Windows.Controls.TextBox.

This is NumberTextBox code. Use commented line instead of the previous line for System.Windows.Controls.TextBox.

public class NumberTextBox : System.Windows.Forms.TextBox
//public class NumberTextBox : System.Windows.Controls.TextBox
{
    private double _maxValue;
    private double _minValue;
    private bool _flag;
    private string _previousValue;

    public NumberTextBox()
    {
        this.TextAlign = HorizontalAlignment.Right;
        //TextAlignment = TextAlignment.Right;
        KeyDown += TextBox_KeyDown;
        TextChanged += TextBox_TextChanged;
        _minValue = double.MinValue;
        _maxValue = double.MaxValue;
    }

    private void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        _previousValue = this.Text;
        _flag = this.SelectedText.Length > 0;
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    //private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        var text = this.Text;
        if (text.Length < 1) return;
        var cursorPosition = SelectionStart == 0 ? SelectionStart : SelectionStart - 1;
        var insertedChar = text[cursorPosition];
        if (IsInvalidInput(insertedChar, cursorPosition, text))
        {
            HandleText(text, cursorPosition);
        }
        ValidateRange(text, cursorPosition);
    }

    private bool IsInvalidInput(char insertedChar, int cursorPosition, string text)
    {
        return !char.IsDigit(insertedChar) && insertedChar != '.' && insertedChar != '-' ||
               insertedChar == '-' && cursorPosition != 0 ||
               text.Count(x => x == '.') > 1 ||
               text.Count(x => x == '-') > 1;
    }

    private void HandleText(string text, int cursorPosition)
    {
        this.Text = _flag ? _previousValue : text.Remove(cursorPosition, 1);
        this.SelectionStart = cursorPosition;
        this.SelectionLength = 0;
    }

    private void ValidateRange(string text, int cursorPosition)
    {
        try
        {
            if (text == "." || _minValue < 0 && text == "-") return;
            var doubleValue = Convert.ToDouble(text);
            if (doubleValue > _maxValue || doubleValue < _minValue)
            {
                HandleText(text, cursorPosition);
            }
        }
        catch (Exception)
        {
            HandleText(text, cursorPosition);
        }
    }

    protected void SetProperties(double minValue = double.MinValue, double maxValue = double.MaxValue)
    {
        _minValue = minValue;
        _maxValue = maxValue;
    }       

}

PositiveNumberTextBox code:

public class PositiveNumberTextBox : NumberTextBox
{
    public PositiveNumberTextBox()
    {
        SetProperties(0);
    }
}

FractionNumberTextBox code:

public class FractionNumberTextBox : NumberTextBox
{
    public FractionNumberTextBox()
    {
        SetProperties(0, 0.999999);
    }
}

you can simply prevent adding non-numerical chars by this simple code

 if (long.TryParse(TextBox.Text,out long isparsable))
        {
          // your code to handle numbers
        }
        else
        {
            TextBox.Text="Only Numbers Allowed";
            TextBox.Focus();
            TextBox.SelectAll();
        }

Use a RegularExpressionValidator:

<asp:TextBox ID="TextBoxNum" runat="server"></asp:TextBox><br />
<asp:RegularExpressionValidator ID="RegularExpressionValidator1"
                        ControlToValidate="TextBoxNum" runat="server"
                        ErrorMessage="Only numbers allowed"
                        ValidationExpression="\d+">
</asp:RegularExpressionValidator>

The simplest and crudest way is to use a combobox instead of textbox with ReadOnly. But of course its only useful if you are expecting the user to use a decent and reasonable set of whole numbers, not try to touch infinity.

Working solution for WPF & a simple TextChangedEventArgs.

private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
    var TextBox = (sender as TextBox);

    // if not a numeric value, remove news characters
    if (Regex.IsMatch(TextBox.Text, "[^0-9]"))
    {
        foreach (TextChange Change in e.Changes)
        {
            TextBox.Text = TextBox.Text.Remove(Change.Offset, Change.AddedLength);

            TextBox.CaretIndex = Change.Offset;
        }
    }
}
Related