输入限制的文本框控件

分享于 

18分钟阅读

Web开发

  繁體

例子

Three textboxes that each allow different characters

介绍

有许多关于代码项目的文章,它们是允许有限输入的HTML TextBox。 我使用的代码的思想和部分是基于 Tadas Budvytis ( )的文章。 虽然这篇文章很好,但我希望代码更加简单和可以重用。 结果是一个从 Sytem.Web.UI.WebControls.TextBox 继承并实现了执行大部分( 但其实不是那么多) 任务的抽象类。 只有在基本类中,你才可以使用 inherit write write write代码,并且你将拥有只接受正整数。数值或者只接受字符串"dekale"的字符串:d。 我已经包含了这类文本框的三个实现。

引用

安全问题

关于安全性的一个大问题。 控件在客户端使用 JavaScript。 用户可以轻松地禁用客户端脚本,因这里永远不能信任只在客户端上检查的输入。 你必须检查服务器的值,最好和最简单的方法是通过验证控件来完成这项工作。

但是,可以轻松创建包含 TextBoxCompareValidator 控件的复合控件,但我选择不将这些。 原因是你可能希望设计器完全控制控件的位置。 但是请注意这可能会带来安全风险,因为设计器可能忘记将验证控件包含在你的TextBox 中。

我所选择的方法是为每个 TextBox 实现创建一个定制的CompareValidator 控件。 这需要大约 3到 4行代码,每个验证程序。 当然,你可以决定将这两个控件再次组合到一个复合控件中。 那由你决定。

使用代码

源代码中包含的类包括 CharacterFilteringTextBoxSignedIntegerTextBoxNumericTextBoxUnsignedIntegerTextBox。 类关系图如下所示:

The classdiagram

CharacterFilteringTextBox控件

基类为 CharacterFilteringTextBox。 代码看起来像这样( 我去掉了一些注释和 protected 属性,它们位于源代码中:

///<summary>/// Base class for all TextBoxes that have use a filter on the client (javascript)/// to enable a subset of the keys.///</summary>publicabstractclass CharacterFilteringTextBox : TextBox
{
 privatestaticstring _altKey = "if (evt.altKey) return;";
 privatestaticstring _ctrlKey = "if (evt.ctrlKey) return;";
 privatestaticstring _specialKeys = "if (charCode<32) return;";
 privatestaticstring _arrowKeys = "if (charCode>=33 &&" + 
 " charCode<=40) return;";
 privatestaticstring _functionKeys = "if (charCode>=112 &&" + 
 " charCode<=123) return;";
 privatestaticstring _numericKeys = "if(charCode>=48 &&" + 
 " charCode<=57) return;";
 privatestaticstring _spaceKey = "if (charCode==32) return;";
 privatestaticstring _negativeSignKey
 {
 get {
 return"if (ch=='" + 
 NumberFormatInfo.CurrentInfo.NegativeSign + "') return;";
 }
 }
 privatestaticstring _numberDecimalSeparatorKey
 {
 get { 
 return"if (ch=='" + 
 NumberFormatInfo.CurrentInfo.NumberDecimalSeparator + 
 "') return;";
 }
 }
 ///<summary>/// Gets the JS function name for the OnKeyPress event of the TextBox.///</summary>protectedstring OnKeyPressFunctionName
 {
 get { return"Filter" + this.GetType().Name; }
 }
 ///<summary>/// Method could be implemented by descendants and must call the/// CreateJavascriptFunction(string innerCode) function. Overriding this/// method is only needed, when special javascript is needed, that goes/// behond the standard protected virtual boolean properties.///</summary>protectedvirtualvoid CreateJavascriptFunction()
 {
 this.RegisterClientScriptFunction(String.Empty);
 }
 ///<summary>/// Creates default code for the descendants and adds the special js code/// from the descendants (innerCode) inside.///</summary>protectedvoid RegisterClientScriptFunction(string innerCode)
 {
 // Check if the function has already been registered, // before we're doing more than strictly needed.if (this.Page.IsClientScriptBlockRegistered(OnKeyPressFunctionName) 
 == false)
 {
 StringBuilder jsCode = new StringBuilder();
 jsCode.Append("<script language="""javascript"><!--n");
 jsCode.Append("function" + OnKeyPressFunctionName + "(evt)");
 jsCode.Append(
 "{" +
 " evt = (evt)? evt : ((window.event)? event : null);" +
 " if (evt)" +
 " {" +
 " var charCode = (evt.charCode)? evt.charCode :" +
 " ((evt.keyCode)? evt.keyCode :" +
 " ((evt.which)? evt.which : 0));" +
 " var ch = String.fromCharCode(charCode);" );
 // Add the code from the descendant whitch implements valid// key's and characters. jsCode.Append(innerCode);
 // Add the code for the corresponding property that returns true.if (this.AllowAltKey) jsCode.Append(_altKey);
 if (this.AllowCtrlKey) jsCode.Append(_ctrlKey);
 if (this.AllowArrowKeys) jsCode.Append(_arrowKeys);
 if (this.AllowDecimalSeparatorKey)
 jsCode.Append(_numberDecimalSeparatorKey);
 if (this.AllowFunctionKeys) jsCode.Append(_functionKeys);
 if (this.AllowNegativeSignKey) jsCode.Append(_negativeSignKey);
 if (this.AllowNumericKeys) jsCode.Append(_numericKeys);
 if (this.AllowSpaceKey) jsCode.Append(_spaceKey);
 if (this.AllowSpecialKeys) jsCode.Append(_specialKeys);
 jsCode.Append(
 " if (window.event) evt.returnValue = false;" +
 " else evt.preventDefault();" +
 " }" +
 "}" );
 jsCode.Append("n--></script>");
 // Add the javascript to the pagethis.Page.RegisterClientScriptBlock(
 OnKeyPressFunctionName, // the name of the function jsCode.ToString() // convert to string );
 }
 }
 ///<summary>/// Overridden. Registrers JavaScript function/// to Page and Adds the OnKeyPress event.///</summary>protectedoverridevoid OnPreRender(EventArgs e)
 {
 this.CreateJavascriptFunction();
 // Adding the TextAlign property to the control's stylesheet.if (this.TextAlign!= null && this.TextAlign!= String.Empty)
 this.Style.Add("text-align", this.TextAlign);
 this.Attributes.Add("onkeypress", OnKeyPressFunctionName + "(event)");
 base.OnPreRender(e);
 }
}

就像你在类图中所看到的, protected 代码中缺少布尔属性。 我已经把他们,了但是你可以下载源代码看看他们。 每个属性都返回 true 或者 false 所以这里没有什么壮观的东西。 稍后我将详细介绍这些属性。 我将很快通过这些方法和这个基类的一个属性。 我将从 OnKeyPressFunctionName 属性开始。

protectedstring OnKeyPressFunctionName
{
 get { return"Filter" + this.GetType().Name; }
}

OnKeyPressFunctionName 是一个只读属性,它返回JavaScript函数的NAME。 这里的技巧是为每个实现提供唯一的NAME。 继承的每个类都有它的惟一的类 NAME ( 在将所有实现保持在同一命名空间中时),这个方法返回与单词"筛选器"连接的类 NAME。 虽然函数名可能比较长,但 Type.Name 属性有一些性能问题,但代码却比较短,但代码相当简短和可读。

下面讨论的是重写的OnPreRender 方法:

protectedoverridevoid OnPreRender(EventArgs e)
{
 this.CreateJavascriptFunction();
 if (this.TextAlign!= null && this.TextAlign!= String.Empty)
 this.Style.Add("text-align", this.TextAlign);
 this.Attributes.Add("onkeypress", OnKeyPressFunctionName + "(event)");
 base.OnPreRender(e);
}

OnPreRender 方法并不真正做到。 它调用 CreateJavascriptFunction() 方法,该方法可以由后代( 关于这里功能的更多信息) 实现。 然后,它将 TextAlign 属性插入控制样式表( CSS )的级联。 接下来,OnKeyPress 事件将添加到 TextBox 中,最后一个基 OnPreRender 方法将被调用。 控件将生成类似于下面这样的HTML代码:

<inputtype="text"onkeypress="FilterNumericTextBox(event)"/>

下一个方法是 CreateJavascriptFunction():

protectedvirtualvoid CreateJavascriptFunction()
{
 this.RegisterClientScriptFunction(String.Empty);
}

这里方法只调用 RegisterClientScriptFunction() 方法。 后代可以重写这个函数并创建一些特定的JavaScript代码,这些代码可以添加到控制函数的JavaScript中。 仅当抽象 CharacterFilteringTextBox的基本功能不足时,才需要重写这里方法。 通常情况下你只需要覆盖 protected 要更改的布尔属性。

最后一个和最长的方法是 RegisterClientScriptFunction():

protectedvoid RegisterClientScriptFunction(string innerCode)
{
 // Check if the function has already been registered, if (this.Page.IsClientScriptBlockRegistered(OnKeyPressFunctionName) == false)
 {
 StringBuilder jsCode = new StringBuilder();
 jsCode.Append("<script language="""javascript"><!--n");
 jsCode.Append("function" + OnKeyPressFunctionName + "(evt)");
 jsCode.Append(
 "{" +
 " evt = (evt)? evt : ((window.event)? event : null);" +
 " if (evt)" +
 " {" +
 " var charCode = (evt.charCode)? evt.charCode :" +
 " ((evt.keyCode)? evt.keyCode :" +
 " ((evt.which)? evt.which : 0));" +
 " var ch = String.fromCharCode(charCode);" );
 // Add the code from the descendant whitch implements valid// key's and characters. jsCode.Append(innerCode);
 if (this.AllowAltKey) jsCode.Append(_altKey);
 if (this.AllowCtrlKey) jsCode.Append(_ctrlKey);
 if (this.AllowArrowKeys) jsCode.Append(_arrowKeys);
 if (this.AllowDecimalSeparatorKey)
 jsCode.Append(_numberDecimalSeparatorKey);
 if (this.AllowFunctionKeys) jsCode.Append(_functionKeys);
 if (this.AllowNegativeSignKey) jsCode.Append(_negativeSignKey);
 if (this.AllowNumericKeys) jsCode.Append(_numericKeys);
 if (this.AllowSpaceKey) jsCode.Append(_spaceKey);
 if (this.AllowSpecialKeys) jsCode.Append(_specialKeys);
 jsCode.Append(
 " if (window.event) evt.returnValue = false;" +
 " else evt.preventDefault();" +
 " }" +
 "}" );
 jsCode.Append("n--></script>");
 this.Page.RegisterClientScriptBlock(
 OnKeyPressFunctionName, // the name of the function jsCode.ToString() // convert to string );
 }
}

方法首先检查JavaScript函数是否已经注册到页面,如果没有,则生成JavaScript函数,并将JavaScript注册到页面。 浏览器中的final 代码将如下所示:

function FilterCLASSNAME(evt)
{
 evt = (evt)? evt : ((window.event)? event : null);
 if (evt)
 {
 var charCode = (evt.charCode)? evt.charCode : (
 (evt.keyCode)? evt.keyCode : 
 ((evt.which)? evt.which : 0));
 var ch = String.fromCharCode(charCode);
 // here the input will be checked and if allowed, returned.if (window.event) evt.returnValue = false;
 else evt.preventDefault();
 }
}

这些代码也可以在 Tadas Budvytis文章中找到,但是我做了一些小的改进,使它在其他浏览器上工作,。 你可以比较代码以查看差异。

仔细查看JavaScript代码 上面 时,你会发现这段代码并没有真正做到。 我的意思是,当你在浏览器中使用这个代码时,你会看到你不能在 TextBox 中插入任何字符。 更糟糕的是,像Tab和Return这样的特殊字符。 那就是 protected 虚拟布尔属性出现在。

CharacterFilteringTextBox 提供了一些标准设置。 CharacterFilteringTextBox 标准允许Alt+Key组合,Ctrl+Key组合,箭头键和特殊密钥(。像制表符,退格键,返回)。 后代现在必须做的就是重写它想要改变的属性。 因此,如果后代想要允许数字字符,它必须重写 AllowNumericKeysAllowNumericKeys 应该只返回 true

后代

就像我说的,后代不需要做什么。 对于 UnsignedIntegerTextBox ( 只允许字符 0到 9 ),它大约有 2行。 UnsignedIntegerTextBox的代码如下所示:

publicclass UnsignedIntegerTextBox : CharacterFilteringTextBox
{
 ///<summary>Overridden.</summary>protectedoverridebool AllowNumericKeys { get { returntrue; } }
 ///<summary>Overridden.</summary>protectedoverridestring TextAlign { get { return"right"; } }
}

所以这是你唯一要写的东西。 不。还有一个。 你一定要忘记,你必须为你的TextBox 写一个正确的验证控件。 对于 UnsignedIntegerTextBox,它的外观如下:

publicclass UnsignedIntegerTextBoxValidator : CompareValidator
{
 public UnsignedIntegerTextBoxValidator() : base()
 {
 this.Operator = ValidationCompareOperator.GreaterThanEqual;
 this.Type = ValidationDataType.Integer;
 this.ValueToCompare = "0";
 }
}

因此,在设计器的HTML tab中,可以像这样实现验证程序:

<kale:UnsignedIntegerTextBoxValidatorRunat="server"ControlToCompare="UnsignedIntegerTextBox1"/>

但当然,你可以在设计器中拖放验证程序,但是哪个程序员想要这样做? ;- )

缺点

尽管这里实现适合 ASP.NET的控制模型,但这种实现仍然存在一些缺点:

  • 安全性。设计器不能忘记包含验证控件。
  • 每个调用都生成JavaScript代码。 有一些性能损失( 参见参考) 在这个和更多的字节上发送给用户,每个请求都发送给用户。

解决方案解决方案

在这里,我提供了可能的解决方案的解决方案:

  • 缺点 1你可以创建既包含 TextBox 控件又包含所需验证控件的复合控件。
  • 缺点 2你可以创建保存 OnPreRender 方法中. js 文件的所有不同过滤器JavaScript函数和 register的. js 文件。 这样你就有了一个只包含三行代码的控件,你可以直接从 TextBox 中进行 inherit。

Bugs

在这个实现中有一个小 Bug。 当允许功能键( F1.。 通过重写 AllowFunctionKeys 属性,这也允许,数字字符'q'到'z',因为它们具有与函数键相同的字符串。 我没有太多时间来找出如何纠正这个问题。 也许有人已经解决了这个问题。

结束语

在浏览器中显示了将特定客户端代码写到允许选定字符范围的文本框控件。 这个实现有一些缺点,我描述了一些解决方法。 如何实现它,完全依赖于项目和其他设计决策。

开心编码。

历史记录

  • 22 2005年08月 - 版本 1.1. 改进的实现使创建后代更容易。 添加了可以重写的TextAlign 属性。
  • 30 2005年07月 - 版本 1.0. 初始版本。

控制  文本  TEX  Controls  输入  Limit  
相关文章