Coder Perfect

What is the best way to create a textbox that only accepts numbers?

Problem

I have a Windows forms application with a textbox control that I want to accept only integer values. I’ve done this type of validation in the past by overloading the KeyPress event and simply eliminating characters that didn’t meet the criteria. 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.

In an ideal world, pressing a non-numerical character would either have no effect or offer prompt notification to the user about the erroneous character.

Asked by Mykroft

Solution #1

Two options:

If your TextBox should not accept decimal places, you can omit the check for ‘.’ (and the subsequent check for more than one ‘.’). If your TextBox should support negative values, you may also include a check for ‘-‘.

Use textBox1 to limit the number of digits the user can enter. MaxLength = 2; / the user will be limited to entering only two digits.

Answered by Matt Hamilton

Solution #2

And just because doing things in one line is always more fun…

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

This does not prohibit users from copying and pasting into this textbox. It’s not a foolproof method of data sanitization.

Answered by BFree

Solution #3

I’m guessing you’re developing a.NET C# app based on the context and tags you used. You can subscribe to the text changed event in this scenario and validate each keystroke.

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);
    }
}

Answered by Anthony D

Solution #4

Here’s a small standalone Winforms custom control that’s based on the regular TextBox and only accepts System.Int32 input (it could be easily adapted for other types such as System.Int64, etc.). Negative numbers and copy/paste operations are supported:

public class Int32TextBox : TextBox
{
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);

        NumberFormatInfo fi = CultureInfo.CurrentCulture.NumberFormat;

        string c = e.KeyChar.ToString();
        if (char.IsDigit(c, 0))
            return;

        if ((SelectionStart == 0) && (c.Equals(fi.NegativeSign)))
            return;

        // copy/paste
        if ((((int)e.KeyChar == 22) || ((int)e.KeyChar == 3))
            && ((ModifierKeys & Keys.Control) == Keys.Control))
            return;

        if (e.KeyChar == '\b')
            return;

        e.Handled = true;
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        const int WM_PASTE = 0x0302;
        if (m.Msg == WM_PASTE)
        {
            string text = Clipboard.GetText();
            if (string.IsNullOrEmpty(text))
                return;

            if ((text.IndexOf('+') >= 0) && (SelectionStart != 0))
                return;

            int i;
            if (!int.TryParse(text, out i)) // change this for other integer types
                return;

            if ((i < 0) && (SelectionStart != 0))
                return;
        }
        base.WndProc(ref m);
    }

2017 update: My first response has some flaws:

So I created a new version that is more generic but still supports copy/paste, the + and – signs, and other features.

public class ValidatingTextBox : TextBox
{
    private string _validText;
    private int _selectionStart;
    private int _selectionEnd;
    private bool _dontProcessMessages;

    public event EventHandler<TextValidatingEventArgs> TextValidating;

    protected virtual void OnTextValidating(object sender, TextValidatingEventArgs e) => TextValidating?.Invoke(sender, e);

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (_dontProcessMessages)
            return;

        const int WM_KEYDOWN = 0x100;
        const int WM_ENTERIDLE = 0x121;
        const int VK_DELETE = 0x2e;

        bool delete = m.Msg == WM_KEYDOWN && (int)m.WParam == VK_DELETE;
        if ((m.Msg == WM_KEYDOWN && !delete) || m.Msg == WM_ENTERIDLE)
        {
            DontProcessMessage(() =>
            {
                _validText = Text;
                _selectionStart = SelectionStart;
                _selectionEnd = SelectionLength;
            });
        }

        const int WM_CHAR = 0x102;
        const int WM_PASTE = 0x302;
        if (m.Msg == WM_CHAR || m.Msg == WM_PASTE || delete)
        {
            string newText = null;
            DontProcessMessage(() =>
            {
                newText = Text;
            });

            var e = new TextValidatingEventArgs(newText);
            OnTextValidating(this, e);
            if (e.Cancel)
            {
                DontProcessMessage(() =>
                {
                    Text = _validText;
                    SelectionStart = _selectionStart;
                    SelectionLength = _selectionEnd;
                });
            }
        }
    }

    private void DontProcessMessage(Action action)
    {
        _dontProcessMessages = true;
        try
        {
            action();
        }
        finally
        {
            _dontProcessMessages = false;
        }
    }
}

public class TextValidatingEventArgs : CancelEventArgs
{
    public TextValidatingEventArgs(string newText) => NewText = newText;
    public string NewText { get; }
}

You can either derive from Int32, as in this:

public class Int32TextBox : ValidatingTextBox
{
    protected override void OnTextValidating(object sender, TextValidatingEventArgs e)
    {
        e.Cancel = !int.TryParse(e.NewText, out int i);
    }
}

Use the new TextValidating event like this with or without derivation:

var vtb = new ValidatingTextBox();
...
vtb.TextValidating += (sender, e) => e.Cancel = !int.TryParse(e.NewText, out int i);

But the best part is that it can be used with any string and any validation method.

Answered by Simon Mourier

Solution #5

The Validated/Validating events were created specifically for this purpose.

The following is a link to the MSDN article on the subject: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validating.aspx

The short version: in the Validating event, examine the.Text property and set e.Cancel=True if the data is invalid.

When you set e.Cancel=True, the user won’t be able to leave the field, but you’ll need to notify them that something is wrong. To identify an issue, I alter the background color of the box to light red. When Validating is called with a good value, make sure to set it back to SystemColors.Window.

Answered by TomXP411

Post is based on https://stackoverflow.com/questions/463299/how-do-i-make-a-textbox-that-only-accepts-numbers