Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Creating a Numeric TextBox Control

4.61/5 (15 votes)
29 Mar 2010CPOL5 min read 87.5K   2.4K  
This article explains step-by-step how to create a custom textbox that only accepts numeric characters (integer or decimal).

Numeric TextBox

Introduction

In this article, we'll learn step-by-step how to create a custom textbox control that only accepts numeric characters. The control will allow the client code to specify whether it should work as an integer or decimal textbox.

This custom control will be implemented using Visual Studio 2008. However, the same code can be written using previous versions of Visual Studio. The programming languages I'm going to use are: C# and JavaScript.

Prepare the Project

First of all, let's prepare the project and the files that we're going to use. Please follow these simple steps:

  1. Create a new project of type Class Library and name it MyCustomControls.
  2. Create new ClassLibrary project

  3. Make sure that you add a reference to the System.Web library.
  4. Add reference to System.Web

  5. By default, Visual Studio will add a file named 'Class1.cs'. Please delete this file and add two files with the following names: NumericTextBox.cs and NumericTextBoxExtention.cs. In addition, create a new folder with the name 'Resources' and add a JavaScript file with the name 'NumericTextBox.js'.
  6. Insert the required files

  7. In order to make the JavaScript embedded in the component, we need to change its BuildAction property to 'Embedded Resource'. Please refer to the figure below:
  8. Change javascript file BuildAction property to Embedded Resource

The C# Code

Why Two C# Files?

As you may have already noticed, we have two C# files. Both of them will be used to implement the same class: NumericTextBox. However, the keyword partial will be added to the definition so the compiler will know that both classes represent one class in the end. The NumericTextBox.cs file will be used to implement the logic of our control, while the NumericTextBoxExtention.cs will contain any fields or properties.

Please have a look at the code below to see how we're going to define the NumericTextBox class in the NumericTextBox.cs file. Code Snippet 2 shows the code in the NumericTextBoxExtention.cs file.

Please note that the partial classes is a feature added to .NET version 2.0 and later versions. If you're using a 1.* version of .NET, you can alternatively place all your code in one class.

C#
//
// Code Snippet 1
//

using System;
using System.Web.UI;

[assembly: WebResource("MyCustomControls.Resources.NumericTextBox.js", "text/javascript")]
namespace MyCustomControls
{
    [ToolboxData(@"<{0}:NumericTextBox Text="""" runat=""server""></{0}:NumericTextBox>")]
    public partial class NumericTextBox : System.Web.UI.WebControls.TextBox
    {
        
    }
}    

//
// Code Snippet 2
//

namespace MyCustomControls
{
    public partial class NumericTextBox : System.Web.UI.WebControls.TextBox
    {
        
    }
}

Fields and Properties

There are some attributes added in Code Snippet 1. Don't worry about them now, we'll talk about them later in this article. Now we want to see what properties we need to implement in the NumericTextBoxExtention.cs file.

Let's think of properties that we might need. First of all, we've said previously that our control will work for both integers and decimals. So, it would be wise to add a property named Type. This property can be of type int or string. However, I think it's a better practice to add an enum named TextBoxType and let our property's type be of this enum.

Additionally, we'll need a property to specify the number of integers allowed. And, in the case of the decimal textbox, we'll also need a property to specify the maximum number of fractions allowed after the decimal point. Furthermore, we'll add a property to specify whether this control should accept negative values or not. Please have a look at Code Snippet 3 to see how this will be implemented.

C#
//
// Code Snippet 3
//

public enum TextBoxType
{
    Integer,
    Decimal
}

private int _NumberOfFraction = 0;
private int _NumberOfInteger = 0;
private bool _AllowNegative = false;
private TextBoxType _Type = TextBoxType.Integer;

public int NumberOfFraction
{
    get { return _NumberOfFraction; }
    set { _NumberOfFraction = value; }
}

public int NumberOfInteger
{
    get { return _NumberOfInteger; }
    set { _NumberOfInteger = value; }
}

public bool AllowNegative
{
    get { return _AllowNegative; }
    set { _AllowNegative = value; }
}
        
public TextBoxType Type
{
    get { return _Type; }
    set { _Type = value; }
}

Attributes

In order to let the compiler know that there's a resource that should be attached here, we need to add an attribute to the namespace, as you can see in Code Snippet 1. We've specified the path of the JavaScript file following the pattern: "Project Name.Folder Name.File Name".

In Visual Studio, drag and drop any ASP.NET control to your page. It will automatically create the necessary tags and attributes. In order to let our control have the same behavior, we've added an attribute to the class name. Please refer to Code Snippet 1. {0} will be replaced by a value that you specify in your web.config or in any config file. For ASP.NET controls, it's by default asp.

Please refer to Code Snippet 4 to see how you can specify this value in your web.config. So, when dragging and dropping our control to a web page, it will look like Code Snippet 5.

ASP.NET
<!--//
// Code Snippet 4
//-->

<pages>
   <controls>
      <add tagPrefix="mcc" namespace="MyCustomControls" assembly="MyCustomControls"/>
        .
        .
        .
<--//
// Code Snippet 5
//-->

<mcc:NumericTextBox Text="" runat="server"></mcc:NumericTextBox>

Override OnPreRender

We need to override OnPreRender to implement the way we want the control to be pre-rendered. In other words, we want to let the OnPreRender method know that we want our control not only do what a normal textbox does, but also have other features.

There are basically two things that we're going to do in the OnPreRender method. The first thing is to add code that will let the method add a reference to our JavaScript file. And, the second this is adding calls to JavaScript functions on certain events. Please refer to Code Snippet 6.

C#
//
// Code Snippet 6
//

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    ClientScriptManager scriptManager = this.Page.ClientScript;
    string resourceFilePath = "MyCustomControls.Resources.NumericTextBox.js";

    // This will register a Javascript block witht the name 'NumericTextBoxScript'
    scriptManager.RegisterClientScriptInclude("NumericTextBoxScript", 
       scriptManager.GetWebResourceUrl(this.GetType(), resourceFilePath));

    if (this.Type == TextBoxType.Decimal)
        this.Attributes.Add("onkeydown", 
           string.Format("return CheckDecimal(this,'{0}','{1}', {2})", 
           NumberOfInteger, NumberOfFraction, _AllowNegative.ToString().ToLower()));
    else if (this.Type == TextBoxType.Integer)
        this.Attributes.Add("onkeydown", string.Format("return CheckInteger({0})", 
                            _AllowNegative.ToString().ToLower()));

    this.Attributes.Add("onkeyup", string.Format("return CheckNegative(this)", 
                        _AllowNegative.ToString().ToLower()));
}

JavaScript Code

We're going to have three JavaScript functions. The first one, CheckInteger, will be called on the keydown event if the control is working as an integer textbox. The second, CheckDecimal, will be called on the keydown event when the control is working as a decimal textbox. Finally, the third function, CheckNegative, will be called on the keyup event no matter how the control is working.

CheckInteger will simply check whether the ASCII code of the character pressed is within the allowed list. It will also make sure that there's no more than one dash (-) in the textbox.

The reason why this function is being called on the keydown event, not the keyup, is that the keydown event is raised prior to setting the value of the textbox, so I still have a chance to terminate this action. Therefore, if the function returns false on the keydown event, the character won't be added to the textbox.

On the other hand, if the function returns false on the keyup event, nothing will change. However, the benefit of the keyup event is that it allows me to read the value of the textbox, since it's raised after the value has been set.

JavaScript
//
// Code Snippet 7
//

function CheckInteger(allowNegative) {
    if ((event.keyCode >= 48 && event.keyCode <= 57 && event.shiftKey == false) ||
         // 0-9 numbers        
        (event.keyCode >= 96 && event.keyCode <= 105 && event.shiftKey == false) ||
         // 0-9 numbers (the numeric keys at the right of the keyboard)
        (event.keyCode >= 37 && event.keyCode <= 40) || // Left, Up, Right and Down        
        event.keyCode == 8 || // backspaceASKII
        event.keyCode == 9 || // tabASKII
        event.keyCode == 16 || // shift
        event.keyCode == 17 || // control
        event.keyCode == 35 || // End
        event.keyCode == 36 || // Home
        event.keyCode == 46) // deleteASKII
        return true;
    else if (event.keyCode == 189 && allowNegative == true) { // dash (-)
        if (sender.value.indexOf('-', 0) > -1)
            return false;
        else
            return true;
    }
    else
        return false;
}

CheckDecimal is very similar to CheckInteger. But they are different in certain things. CheckDecimal will treat a numeric character in a different way than the other allowed keys. This is because when a number is pressed, we need to check if the number of integers and fractions is still within the limits.

In addition, it has a special treatment for the decimal point character. It shouldn't be the first character in the textbox, and it shouldn't appear more than once.

JavaScript
//
// Code Snippet 8
//

function CheckDecimal(sender, numberOfInteger, numberOfFrac, allowNegative) {
    var valueArr;

    if ((event.keyCode >= 37 && event.keyCode <= 40) || // Left, Up, Right and Down
        event.keyCode == 8 || // backspaceASKII
        event.keyCode == 9 || // tabASKII
        event.keyCode == 16 || // shift
        event.keyCode == 17 || // control
        event.keyCode == 35 || // End
        event.keyCode == 36 || // Home
        event.keyCode == 46) // deleteASKII
        return true;
    else if (event.keyCode == 189 && allowNegative == true) { // dash (-)
        if (sender.value.indexOf('-', 0) > -1)
            return false;
        else
            return true;
    }

    valueArr = sender.value.split('.');

    if (event.keyCode == 190) { // decimal point (.)
        if (valueArr[0] != null && valueArr[1] == null)
            return true;
        else
            return false;
    }

    if ((event.keyCode >= 48 && event.keyCode <= 57 && event.shiftKey == false) ||
         // 0-9 numbers        
        (event.keyCode >= 96 && event.keyCode <= 105 && event.shiftKey == false)) {
         // 0-9 numbers (the numeric keys at the right of the keyboard)
        if (valueArr[1] == null) {
            if (valueArr[0].indexOf('-', 0) > -1)
                numberOfInteger++;

            if (valueArr[0].length <= numberOfInteger)
                return true;
        }
        else {
            if (valueArr[1].length <= numberOfFrac)
                return true;
        }
    }

    return false;
}

When the key is up, the CheckNegative function will make sure that the dash/minus character is added to the left.

JavaScript
//
// Code Snippet 9
//

function CheckNegative(sender) {
    if (event.keyCode == 189) { // dash (-)
        if (sender.value.indexOf('-', 0) > 0)
            sender.value = sender.value.replace('-', '');
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)