How to limit or prevent characters in a TextBox in C#? Or How to create a NumberTextBox or DigitBox object?

Let say you want to have a TextBox in which you only want to allow integers (0-9) or maybe you only want to allow strings, A-Za-z.

Well, lets play around with this for a second and see what we can do.

To get started do this:

  1. Create a new WPF Application project.
  2. In the designer, add a TextBox from the Toolbox.
  3. In the properties field for the TextBox click the icon that look like a lightening bolt to bring up events.
  4. Find the KeyDown event and double-click on it.

Ok, so you should now have the following function:

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
        }

The key that was pressed is accessible from the KeyEventArgs variable, e. Specifically e.Key.

Key just happens to be an enum, which means they can basically be treated as integers.

The key press can be ignored by telling setting e.Handled=true. This way it is already marked as handled and will not be added to the TextBox.

Allow only number keys 0-9 in a TextBox

Here is a simple function to allow only natural numbers or number keys 0-9 in a TextBox. Be aware that the keys may be different in other languages.

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key < Key.D0 || e.Key > Key.D9)
            {
                e.Handled = true;
            }
        }

Wow, that was pretty simple, right?  WRONG! It is not that easy.

You realize that there are two sets of numbers on a keyboard right? You have numbers in row above your QWERTY keys and you likely have a Number pad on the right of your keyboard as well.  That is not all either.

What else did we forget, you might ask?  Well, of the seven requirements we need to handle, we only handled one.  For an application to be considered release quality or enterprise ready or stable, all seven of these should be handled.

  1. Numbers 1234567890 above QWERTY keys.
  2. Numpad numbers.
  3. You may want to allow pressing of the Delete, Backspace, and Tab keys.
  4. What about pasting?
  5. What about drag and drop?
  6. What about someone else calling your code and changing the text?
  7. Mnemonics should work.

Requirements

Here are the six requirements in clear statements.

  1. Allow pressing Numbers 1234567890 above QWERTY keys.
  2. Allow pressing Numpad numbers.
  3. Allow pressing of the Delete, Backspace, and Tab keys.
  4. Allow pasting so that only numbers in a string are added: A1B2C3 becomes 123.
  5. Allow drag and drop so that only numbers in a string are added: A1B2C3 becomes 123.
  6. Allow change in code at runtime so that only numbers in a string are added: A1B2C3 becomes 123.
  7. When another control has a mnemonic, such as Alt + S, pressing Alt + S, should property change the focus and place the cursor in the Alt + S control.

Remembering lists like this is something that comes with experience.  If you thought of these on your own, good work.  If you didn’t think of them on your own, don’t worry, experience comes with time.

So lets enhance this to handle each of these.

Handling both Number keys and Numpad keys

        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
                e.Handled = !IsNumberKey(e.Key);
        }

        private bool IsNumberKey(Key inKey)
        {
            if (inKey < Key.D0 || inKey > Key.D9)
            {
                if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
                {
                    return false;
                }
            }
            return true;
        }

All right, we now have two of the six requirements down.

Handling Delete, Backspace, and Tab, and Mnemonics

You can probably already guess how easy it will be to do something similar to handle these two keys.

        protected void OnKeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key);
        }

        private bool IsNumberKey(Key inKey)
        {
            if (inKey < Key.D0 || inKey > Key.D9)
            {
                if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
                {
                    return false;
                }
            }
            return true;
        }

        private bool IsActionKey(Key inKey)
        {
            return inKey == Key.Delete || inKey == Key.Back || inKey == Key.Tab || inKey == Key.Return || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
        }

Ok, now we have four of six requirements handled.

Handling Paste (Ctrl + V) and Drag and Drop

Yes, I can handle both at the same time with a new event TextChanged.

This is setup so that if someone pastes both letters and number, only the numbers are pasted: A1B2C3 becomes 123.

This event is not configured so we have to set it up.

  1. In the designer, click the TextBox.
  2. In the properties field for the TextBox click the icon that look like a lightening bolt to bring up events.
  3. Find the TextChanged event and double-click on it.

You should now have this stub code for the event function.


        private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
        {
        }

Here is some easy code to make sure each character is actually a digit.

        protected void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            base.Text = LeaveOnlyNumbers(Text);
        }

        private string LeaveOnlyNumbers(String inString)
        {
            String tmp = inString;
            foreach (char c in inString.ToCharArray())
            {
                if (!IsDigit(c))
                {
                    tmp = tmp.Replace(c.ToString(), "");
                }
            }
            return tmp;
        }

        public bool IsDigit(char c)
        {
            return (c >= '0' && c <= '9');
        }

Guess what else? This last function actual handles the first five requirements all by itself. But it is less efficient so we will leave the previous requirements as they are.

Handling Direct Code Change

Ok, so some somehow your TextBox is passed inside code during runtime a string that contains more than just numbers.  How are you going to handle it.

This is setup so that if someone pastes both letters and number, only the numbers are pasted: A1B2C3 becomes 123.  Well, we need to run the same function as for Drag and Drop, so to not duplicate code, it is time to create a class or object.

Creating a NumberTextBox object

Now we need to make our code reusable. Lets create a class called NumberTextBox and it can do everything automagically.

NumberTextBox

using System;
using System.Windows.Controls;
using System.Windows.Input;

namespace System.Windows.Controls
{
    public class DigitBox : TextBox
    {
        #region Constructors
        /// <summary> 
        /// The default constructor
        /// </summary>
        public DigitBox()
        {
            TextChanged += new TextChangedEventHandler(OnTextChanged);
            KeyDown += new KeyEventHandler(OnKeyDown);
        }
        #endregion

        #region Properties
        new public String Text
        {
            get { return base.Text; }
            set
            {
                base.Text = LeaveOnlyNumbers(value);
            }
        }

        #endregion

        #region Functions
        private bool IsNumberKey(Key inKey)
        {
            if (inKey < Key.D0 || inKey > Key.D9)
            {
                if (inKey < Key.NumPad0 || inKey > Key.NumPad9)
                {
                    return false;
                }
            }
            return true;
        }

        private bool IsActionKey(Key inKey)
        {
            return inKey == Key.Delete || inKey == Key.Back || inKey == Key.Tab || inKey == Key.Return || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
        }

        private string LeaveOnlyNumbers(String inString)
        {
            String tmp = inString;
            foreach (char c in inString.ToCharArray())
            {
                if (!IsDigit(c))
                {
                    tmp = tmp.Replace(c.ToString(), "");
                }
            }
            return tmp;
        }

        public bool IsDigit(char c)
        {
            return (c >= '0' && c <= '9');
        }
        #endregion

        #region Event Functions
        protected void OnKeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key);
        }

        protected void OnTextChanged(object sender, TextChangedEventArgs e)
        {
            base.Text = LeaveOnlyNumbers(Text);
        }
        #endregion
    }
}

Now I can delete the events and functions from the Window1.xaml.cs file. I don’t have to add any code to the Window1.xaml.cs. Instead I need to reference my local namespace in the Window1.xaml and then change the TextBox to a local:NumberTextBox. Here is the XAML.

<Window x:Class="TextBoxIntsOnly.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TextBoxIntsOnly"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <local:DigitBox Margin="87,27,71,0" VerticalAlignment="Top" x:Name="textBox1" />
        <Label Height="28" HorizontalAlignment="Left" Margin="9,25,0,0" Name="label1" VerticalAlignment="Top" Width="72">Integers:</Label>
        <TextBox Height="23" Margin="87,56,71,0" Name="textBox2" VerticalAlignment="Top" />
        <Label Height="28" HorizontalAlignment="Left" Margin="9,54,0,0" Name="label2" VerticalAlignment="Top" Width="72">Alphabet:</Label>
    </Grid>
</Window>

And now all seven requirements are met.

33 Comments

  1. mandz says:

    is there a way to allow only numbers and prevent copy and paste?

  2. Sriram says:

    Thanks for such a nice piece of code. I can say today i learnt some validatin techniques.

  3. Stephen says:

    Hi

    This article has been really helpful to me but I also noticed that in the IsDigit, you needed to make it a logical AND not OR which someone else pointed out in the comments. The code in the article above still needs to be changed tho.

    Thanks so much for this! Helped heaps.

  4. Daniel Wiebe says:

    I really like what you have done here. Two things:

    In the final bit of code, why not just override the OnKeyDown and OnKeyChanged methods of the TextBox? Or is

    Second, what code license is this code under? I cannot use it in our commercial app unless it is under some compatible license such as MIT, BSD, CPOL, Apache license, MS-PL, etc.

    • Rhyous says:

      Oh, right. Most my code is under a BSD License unless otherwise specified. I added a license page stating such:
      http://www.rhyous.com/programming-development/code-licensing/

      • Daniel Wiebe says:

        Ok, thanks! Also, if you have the time (don't worry about it otherwise), I would be interested to know if there was a reason for subscribing directly to the KeyDown/TextChanged events rather than overriding the OnKeyDown and OnTextChanged handlers in the DigitBox class.

        • Rhyous says:

          There was not thought put into that really. I wish I could say there was. It probably happened that way because my first attempt to do this was as an attached property to TextBox that subscribed to the events.

  5. pelda says:

    And what if i want to allow numbers and + - * / chars?

    • Rhyous says:

      Such as for a calculator? I would create a third method called IsOperatorKey() and then change to use the next line:

      e.Handled = !IsNumberKey(e.Key) && !IsActionKey(e.Key) && !IsOperatorKey()

      Then add a method IsOperatorCharacter. Then edit this line too:

      return (c >= '0' || c <= '9') || IsOperatorCharacter(c);

      Or at this point, I may create a similar object with a list or table of keys and a list of table of characters and then just call it a LimitedTextBox but you actually have to pass in the list of allowed keys and allowed characters.

  6. Fastidious says:

    Hi!

    What is the advantages of this compared to:

    private void TextBox1_TextChanged(object sender, EventArgs e)
    {
    TextBox1.Text = Regex.Replace(TextBox1.Text, "[^0-9]", "", RegexOptions.Compiled);
    }

    I know the regex isn't perhaps the most efficient, but all that code.

    • Rhyous says:

      You got one reason right on. There was a goal of ultimate efficiency and Regex is not very efficient. However, if that were the only reason, it would not have been enough.

      The other reason is that we needed the Text value to NOT change. See, while your method works, it also has a much more serious problem. It allows the Text value of the TextBox to change and then removes the change. You created two changes. Now assume you have code that runs when the Text changes. This code will run twice your way. It won't run at all my way. This object prevents the change on the key press.

      Unfortunately there are other ways to change the value, and in those situations we use a method similar to yours, just without the regex.

  7. someMan says:

    Hi there,
    nice code, but how do i implement this in my wpf-application, into the xaml??? I dont get it.
    The part which describes that is missing... can jou add it please?

    And in the last complete code snippet are a few syntaxy error, j2iu.

    Best regards

  8. [...] http://www.rhyous.com/2010/06/18/how-to-limit-or-prevent-characters-in-a-textbox-in-csharp/ [...]

  9. VecTor says:

    Hi Rhyous

    your code simply works great thanks for it :)

  10. Rhyous says:

    I think there is a bug where a keyboard shortcut or mnemonic cannot be used because Alt + Key is not allowed.

  11. My first idea was to rely on final text and avoid getting into details of how that text could have appeared. There is also text copy/paste.
    I would do it the following way.
    1. Get current text
    2. Convert.ToInt get the number and check if it is within limit
    3. If no, already bad
    4. If yes, check if int.ToString == original text

  12. Thanks for the great example.

    I really think however that something this commonly needed should be implimented as a Control as a part of the .net toolset.

    But my grunting aside, thanks again :)

    Xavier.

  13. zin says:

    Hi Sr!

    I write code for Handling Paste (Ctrl + V) as you mension above.
    But i have an error that is i cannot write any character in that textbox.
    Please solve my problem..

    Thank for ur kindness...........

  14. Alan says:

    Works a treat! Really nice solid code. Cheers!

Leave a Reply

Powered by sweet Captcha