用户输入验证的最佳实践

在软件开发中,用户输入验证是一个至关重要的环节。它不仅关系到数据的准确性和完整性,还直接影响到应用程序的稳定性和用户体验。本文将探讨用户输入验证的最佳实践,包括何时进行验证、如何处理不同数据类型,以及如何避免常见的输入错误。

实时验证用户输入

实时验证用户输入,即在用户输入数据的同时进行验证,可以带来诸多优势。这种方式可以尽早发现并阻止无效数据的输入,从而减少后续处理中的复杂性和潜在错误。然而,如果在用户完成输入后进行验证,例如在失去焦点(LostFocus)事件中进行,开发者将面临一些困难的选择。例如,如果数据无效,是否应该将焦点返回到当前控件,这可能会导致用户“卡”在某个控件中;或者允许焦点移动,但用户必须返回到无效的控件以纠正问题。

为了在数据输入之前阻止无效数据的输入,可以考虑实现一个名为 NumEdit 的控件。这个控件可以轻松处理 .NET 基础的多种数值数据类型,如不同大小的浮点数和整数类型,以及货币(内部处理为 Decimal)。

首先,仅过滤数字并不能允许小数点或负号("-")字符。通过一些修改,可以允许负数和小数点,但代码处理重复的小数点和仅在首位的负号字符时会变得复杂。其次,更重要的是输入的数值。如果正在处理 .NET 32位整数值,那么一旦输入的数值达到最大值,就需要停止进一步输入,否则将面临溢出错误。

为此,选择了使用 Type.Parse() 方法来测试有效的输入。如果 Parse() 方法抛出异常,那么知道 KeyPress 字符是无效的,应该被取消。这种技术在将 TextBox 字符串转换为数值时可以阻止溢出错误。它还可以阻止重复的小数点和重复的负号("-")字符。

最后是粘贴操作。在 KeyPress 事件中使用 IsDigit() 会阻止 Ctrl+V 粘贴键,但用户可能喜欢使用 Ctrl+V 粘贴。如果从 VS.NET 中移除 Ctrl+V 粘贴,会有什么感觉?添加对 Ctrl+V 粘贴的处理将使控件成为一个完整的、生产就绪的组件。

使用 NumEdit 控件

这个控件的美妙之处在于,再也不需要考虑数值数据输入的问题。只需添加控件,设置 InputType 属性,就可以开始使用了。

IsValid() 方法是 NumEdit 控件的核心。该方法在开始时进行一些初步检查,处理初始的负号("-")字符、任何前导 "0" 字符和空字符串。方法的其余部分只是尝试将字符串解析为选定的数据类型,并在抛出任何异常时返回 false。如果 IsValid() 方法返回 false,KeyPress 事件将移除当前的按键输入。

private bool IsValid(string val) { // this method validates the ENTIRE string // not each character bool ret = true; if (val.Equals("") || val.Equals(String.Empty)) return ret; if (user) { // allow first char == '-' if (val.Equals("-")) return ret; if (Min < 0 && val.Equals("-")) return ret; } // parse into dataType, errors indicate invalid value // NOTE: parsing also validates data type min/max try { switch (m_inpType) { case NumEditType.Currency: decimal dec = decimal.Parse(val); int pos = val.IndexOf("."); ret = val.Substring(pos).Length <= 3; ret &= Min <= (double)dec && (double)dec <= Max; break; case NumEditType.Single: float flt = float.Parse(val); ret &= Min <= flt && flt <= Max; break; case NumEditType.Double: double dbl = double.Parse(val); ret &= Min <= dbl && dbl <= Max; break; case NumEditType.Decimal: decimal dec2 = decimal.Parse(val); ret &= Min <= (double)dec2 && (double)dec2 <= Max; break; case NumEditType.SmallInteger: short s = short.Parse(val); ret &= Min <= s && s <= Max; break; case NumEditType.Integer: int i = int.Parse(val); ret &= Min <= i && i <= Max; break; case NumEditType.LargeInteger: long l = long.Parse(val); ret &= Min <= l && l <= Max; break; default: throw new ApplicationException(); } } catch { ret = false; } return ret; }

有趣的发现

在添加了 Ctrl+V 粘贴代码后,发现基础类 TextBox 有一个默认的上下文菜单,包括粘贴命令。这个命令绕过了精心编写的所有代码,允许非数值输入进入 NumEdit 控件。找不到拦截这个粘贴命令的方法。解决方案是简单地用一个空菜单替换 TextBox 上下文菜单,从而移除上下文菜单。如果有人有关于如何覆盖这个上下文菜单的想法,会很乐意听到。

可能的增强

在完成 NumEdit 控件后,想到一些可能的增强。

  • Min/Max 属性。这将允许在数据类型最小/最大值内限制最小/最大值。
  • 仅正数/负数属性。这可以通过 Min/Max 属性处理,因此是多余的,但可能更容易使用。
  • 科学或工程表示法,如 12.345E10,可能会很有用。
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485