65.9K
CodeProject 正在变化。 阅读更多。
Home

创建一个数字文本框控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.61/5 (15投票s)

2009年9月15日

CPOL

5分钟阅读

viewsIcon

88046

downloadIcon

2458

本文将逐步介绍如何创建一个只接受数字字符(整数或小数)的自定义文本框。

Numeric TextBox

引言

在本文中,我们将逐步学习如何创建一个自定义文本框控件,该控件只接受数字字符。该控件将允许客户端代码指定它是作为整数文本框还是小数文本框工作。

此自定义控件将使用 Visual Studio 2008 实现。但是,使用早期版本的 Visual Studio 也可以编写相同的代码。我将使用的编程语言是:C# 和 JavaScript。

准备项目

首先,让我们准备将要使用的项目和文件。请遵循以下简单步骤

  1. 创建一个类型为“类库”的新项目,并将其命名为MyCustomControls
  2. Create new ClassLibrary project

  3. 确保添加了对 System.Web 库的引用。
  4. Add reference to System.Web

  5. 默认情况下,Visual Studio 会添加一个名为“Class1.cs”的文件。请删除此文件,并添加两个名为:NumericTextBox.csNumericTextBoxExtention.cs 的文件。此外,创建一个名为“Resources”的新文件夹,并添加一个名为“NumericTextBox.js”的 JavaScript 文件。
  6. Insert the required files

  7. 为了使 JavaScript 嵌入到组件中,我们需要将其 BuildAction 属性更改为“嵌入的资源”。请参阅下图。
  8. Change javascript file BuildAction property to Embedded Resource

C# 代码

为什么有两个 C# 文件?

您可能已经注意到,我们有两个 C# 文件。它们都将用于实现同一个类:NumericTextBox。但是,将在定义中添加关键字 partial,以便编译器知道这两个类最终代表一个类。NumericTextBox.cs 文件将用于实现我们控件的逻辑,而 NumericTextBoxExtention.cs 将包含任何字段或属性。

请看下面的代码,了解我们将在 NumericTextBox.cs 文件中如何定义 NumericTextBox 类。代码片段 2 显示了 NumericTextBoxExtention.cs 文件中的代码。

请注意,partial 类是 .NET 2.0 及更高版本添加的一项功能。如果您使用的是 .NET 1.* 版本,也可以将所有代码放在一个类中。

//
// 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
    {
        
    }
}

字段和属性

代码片段 1 中添加了一些属性。现在不用担心它们,我们将在本文后面讨论它们。现在,我们想看看在 NumericTextBoxExtention.cs 文件中需要实现哪些属性。

让我们考虑一下我们可能需要的属性。首先,我们之前说过我们的控件可以用于整数和小数。因此,添加一个名为 Type 的属性是明智的。此属性的类型可以是 intstring。但是,我认为添加一个名为 TextBoxTypeenum,并将我们的属性类型设置为该 enum 是一个更好的做法。

此外,我们需要一个属性来指定允许的整数位数。并且,在小数文本框的情况下,我们还需要一个属性来指定小数点后允许的最大分数位数。此外,我们将添加一个属性来指定此控件是否应接受负值。

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

属性

为了让编译器知道有一个资源应该附加在这里,我们需要在命名空间中添加一个属性,如代码片段 1 所示。我们已按照“项目名称.文件夹名称.文件名”的模式指定了 JavaScript 文件的路径。

在 Visual Studio 中,将任何 ASP.NET 控件拖放到页面上。它会自动创建必要的标签和属性。为了让我们的控件具有相同的行为,我们在类名中添加了一个属性。请参阅代码片段 1。{0} 将被您在 web.config 或任何配置文件中指定的值替换。对于 ASP.NET 控件,默认为 asp

请参阅代码片段 4,了解如何将此值指定到 web.config。因此,当将我们的控件拖放到网页上时,它看起来会像代码片段 5。

<!--//
// Code Snippet 4
//-->

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

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

重写 OnPreRender

我们需要重写 OnPreRender 来实现我们希望控件预呈现的方式。换句话说,我们希望 OnPreRender 方法知道我们的控件不仅执行普通文本框的功能,还具有其他功能。

我们将在 OnPreRender 方法中进行两件事。第一件事是添加代码,让该方法添加对我们 JavaScript 文件的引用。第二件事是在特定事件上添加对 JavaScript 函数的调用。请参阅代码片段 6。

//
// 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代码

我们将有三个 JavaScript 函数。第一个是 CheckInteger,如果控件作为整数文本框工作,它将在 keydown 事件上调用。第二个是 CheckDecimal,在控件作为小数文本框工作时,将在 keydown 事件上调用。最后,第三个函数 CheckNegative 将在 keyup 事件上调用,无论控件如何工作。

CheckInteger 将简单地检查按键的 ASCII 码是否在允许的列表中。它还将确保文本框中只有一个破折号(-)。

此函数之所以在 keydown 事件上调用,而不是在 keyup 事件上调用,是因为 keydown 事件在设置文本框值之前触发,所以我仍然有机会终止此操作。因此,如果函数在 keydown 事件上返回 false,则该字符不会被添加到文本框中。

另一方面,如果函数在 keyup 事件上返回 false,则不会发生任何变化。但是,keyup 事件的好处在于它允许我读取文本框的值,因为它是在值设置之后触发的。

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

CheckDecimalCheckInteger 非常相似。但它们在某些方面有所不同。CheckDecimal 会以不同于其他允许按键的方式处理数字字符。这是因为当按下数字时,我们需要检查整数和小数的位数是否仍在限制范围内。

此外,它对小数点字符有特殊处理。它不应该是文本框中的第一个字符,并且不应该出现多次。

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

当按键抬起时,CheckNegative 函数将确保破折号/减号字符被添加到左侧。

//
// Code Snippet 9
//

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