多功能数字输入框控件

GUI设计中,经常需要处理各种格式的数字输入和显示,如二进制、十六进制、十进制等。为了简化这一过程,本文将介绍一个名为NumberBox的控件类,它能够根据指定的标准数字格式字符串(SNFS)来验证键盘输入,并在用户离开控件时执行范围限制检查。这个控件支持包括二进制在内的多种数字格式,并且可以让用户以一种格式输入值,而程序以另一种格式读取值。

在与芯片直接连接的GUI程序设计中,经常需要处理不同大小和宽度的数字,这些数字可能以二进制形式从目标设备读取,但需要以十进制形式显示给用户。为了避免重复编写转换代码,作者创建了一个能够处理所有这些转换的类。

使用代码

在编译后,NumberBox控件将出现在工具箱中,可以像拖放其他控件一样将其拖入GUI设计中。下面是一个演示窗口的示例,左侧面板用于输入数字,键盘输入验证取决于指定的SNFS。当控件的离开事件触发时,值将被写回到左侧面板,并以相应的SNFS格式显示,右侧面板的控件将使用新的值范围进行更新。

C# using System; using System.Windows.Forms; namespace NumberBoxDemo { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void numberBox_Leave(object sender, EventArgs e) { var nb = (NumberBox)sender; foreach (var c in panel2.Controls) if (c.GetType() == typeof(NumberBox)) { var box = (NumberBox)c; if (box.Snfs.StartsWith("D")) box.ValueAsInt64 = nb.ValueAsInt64; if (box.Snfs.StartsWith("F")) box.ValueAsFloat = nb.ValueAsFloat; if (box.Snfs.StartsWith("E")) box.ValueAsDouble = nb.ValueAsDouble; if (box.Snfs.StartsWith("N")) box.ValueAsDouble = nb.ValueAsDouble; if (box.Snfs.StartsWith("X")) box.ValueAsInt64 = nb.ValueAsInt64; if (box.Snfs.StartsWith("B")) box.ValueAsByte = nb.ValueAsByte; } } }

NumberBox控件

NumberBox类是从TextBox派生的。TextBox的Text字段是值的实际存储位置。添加了以下属性:

  • ValueAsByte
  • ValueAsDouble
  • ValueAsFloat
  • ValueAsInt16
  • ValueAsInt32
  • ValueAsInt64
  • ValueAsSByte
  • ValueAsUInt16
  • ValueAsUInt32
  • ValueAsUInt64
  • MaxValue
  • MinValue
  • Prefix
  • Suffix
  • Snfs

ValueAs...属性使用适当的类型转换来获取/设置Text字符串。Min/Max属性用于设置数字的范围限制。Snfs属性是标准数字格式字符串,作者为其添加了一个新的格式'B'用于二进制数字。

NumberBox代码流程

在NumberBox控件实例化并设置SNFS后,会为特定的SNFS创建一个正则表达式。这个正则表达式'_pattern'用于在控制的_KeyDown和_KeyPress事件中验证键盘输入。当用户离开控件时,会发生以下操作:

  1. 控件从NumberBox读取文本。
  2. 移除任何前缀或后缀。
  3. 根据SNFS格式将其转换为double。
  4. 将其限制在Min/Max值范围内。
  5. 以正确的格式将其写回到控件中。

正如主演示程序所示,可以触发Leave事件来执行所需的任何其他操作。在这个演示中,Leave事件更新了右侧面板中的8个NumberBox。两个离开事件按顺序调用,首先是控件触发,然后是主程序事件触发(标准的C#事件链)。

正则表达式构建器

控件的关键部分是构建用于验证键盘输入的正则表达式。以下是代码示例:

C# private string BuildRegularExpression() { if (_snfsEmpty) return string.Empty; // start of line anchor var str = "^"; var len = LengthParameter(_snfs); // add in prefix if (!_prefixEmpty) str = Prefix.Aggregate(str, (current, c) => current + ("[" + c + "]")); // D4 => -?\d\d?\d?\d?\d? if (_snfs.StartsWith("D", StringComparison.OrdinalIgnoreCase)) { str += "-?"; if (len == 0) len = 4; for (var i = 0; i < len; ++i) str += @"\d?"; } // E => ^-?\d*[.,]?\d?\d?\d?\d?\d?\d?[Ee]?[+-]?\d?\d? if (_snfs.StartsWith("E", StringComparison.OrdinalIgnoreCase)) { str += @"-?\d*[.,]"; if (len == 0) len = 6; // SNFS default length for (var i = 0; i < len; ++i) str += @"\d?"; str += @"[Ee]?[+-]?\d?\d?"; } if (_snfs.StartsWith("F", StringComparison.OrdinalIgnoreCase)) { str += @"-?\d*[.,]"; if (len == 0) len = 2; // SNFS default length for (var i = 0; i < len; ++i) str += @"\d?"; } if (_snfs.StartsWith("N", StringComparison.OrdinalIgnoreCase)) { str += @"-?\d*[., ]"; if (len == 0) len = 2; // SNFS default length for (var i = 0; i < len; ++i) str += @"\d?"; } if (_snfs.StartsWith("X", StringComparison.OrdinalIgnoreCase)) { if (len == 0) len = 2; // SNFS default length for (var i = 0; i < len; ++i) str += @"[0-9A-Fa-f]?"; } if (_snfs.StartsWith("B", StringComparison.OrdinalIgnoreCase)) { if (len == 0) len = 1; // SNFS default length for (var i = 0; i < len; ++i) str += @"[0-1]?"; } // add suffix if (!_suffixEmpty) str = _suffix.Aggregate(str, (current, c) => current + ("[" + c + "]")); // and the end of line anchor str += "$"; return str; }

有趣的点

作为一个懒惰的程序员,当需要同时更新数十个NumberBox时,逐个访问每个NumberBox将需要大量的代码。相反,当看到所有NumberBox在一个控件中时,例如上面的右侧面板,发现通过遍历面板中的所有控件并更新它们更容易。即使在这里,将8行代码减少到11行,经常处理包含多达100个项目的控件列表。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485