在日常中,很多地方输入需要限制输入数字,甚至是阿拉伯数字,为了各处代码的统一,写了个数字控件,效果如图
主要原理是在数字控件中重写OnKeyPress事件,判断当前输入的字符是否为数字(if (!char.IsDigit(e.KeyChar))),不是的话就放弃输入。
实现源代码如下:
/// <summary> /// 文件:LiuYun.Beanver.UI.Control.LyNumber /// 说明:数字控件,增加小数点、负数处理 /// 事前处理模式:做录入操作时屏蔽非数字字符 /// 参考 http://blog.youkuaiyun.com/hulihui/article/details/3055907 /// 创建者 操作 日期 /// liuyun 新建 2013/2/21 /// </summary> [ToolboxBitmap(typeof(TextBox))]//使用系统工具箱里的图片 public class LyNumber : TextBox { private const int WM_CHAR = 0x0102; // 字符消息 private const int WM_PASTE = 0x0302; // 上下文菜单"粘贴"消息 public LyNumber() { } public LyNumber(IContainer container) : this() { container.Add(this); } protected override void Dispose(bool disposing) { base.Dispose(disposing); } private bool _isScientificNotation =false; [Browsable(true)] [EditorBrowsable(EditorBrowsableState.Always)] [Category("数字控件")] [Description("是否使用科学计数,默认不使用")] public bool IsScientificNotation { get { return _isScientificNotation; } set { _isScientificNotation = value; } } #region 重写事件 bool isError = false;//当前的text值是否是数值型 /// <summary> /// 重写获取值,因在值中有分号分隔 /// </summary> /// <returns>在控件中显示的文本。</returns> public override string Text { get { string str = base.Text; if (str.Contains(',')&&_isScientificNotation)//去除分号 str.Replace(",",""); return str; } set { if (_isScientificNotation) { bool isPositiveNumber;//正数 if (string.IsNullOrEmpty(value)) return; double num; //判断数字格式是否正确 isError = !Double.TryParse(value.Replace(",", ""), out num); if (!isError)//格式正确,是数值型 { //判断数值正负 if (num < 0) isPositiveNumber = false; else isPositiveNumber = true; value = Math.Abs(num).ToString(); //数值拆分为整数和小数部分,分别用分隔符分隔 string[] strs = value.Split('.'); value = GetSeparateStr(strs[0]); if (strs.Length == 2) { if (string.IsNullOrEmpty(value)) value = "0"; value += "." + GetSeparateStr(strs[1]); } //最后为负数值加上负号 if (!isPositiveNumber) value = "-" + value; } } base.Text = value; } } /// <summary> /// 得到分隔后的字符串 /// </summary> /// <param name="str">The STR.</param> /// <returns></returns> private string GetSeparateStr(string str) { if (!string.IsNullOrEmpty(str)&&!str.Equals("0")) { int length = 3; string[] intStrs = new string[(int)Math.Ceiling((float)str.Length / 3)]; for (int i = 0; i < intStrs.Length; i++) { if (i == intStrs.Length - 1) length = str.Length % 3; if (length == 0) length = 3; intStrs[i] = str.Substring(0, length); str = str.Remove(0, length); } str = string.Empty; for (int i = 0; i < intStrs.Length; i++) { if (i == 0) str += intStrs[i]; else str += "," + intStrs[i]; } }//end if (!string.IsNullOrEmpty(str)&&!str.Equals("0")) return str; } /// <summary> /// 捕获Mouse的Paste消息 /// </summary> protected override void WndProc(ref Message m) { if (m.Msg == WM_PASTE) // 选择上下文菜单的"粘贴"操作 { this.ClearSelection(); SendKeys.Send(Clipboard.GetText()); // 模拟键盘输入 } else base.WndProc(ref m); } /// <summary> /// 捕获Ctrl+V快捷键操作 /// </summary> protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == (Keys)Shortcut.CtrlV) // 快捷键 Ctrl+V 粘贴操作 { this.ClearSelection(); string text = Clipboard.GetText(); for (int k = 0; k < text.Length; k++) // can not use SendKeys.Send { // 通过消息模拟键盘输入, SendKeys.Send()静态方法不行 SendCharKey(text[k]); } return true; } return base.ProcessCmdKey(ref msg, keyData); } /// <summary> /// 屏蔽非数字键 /// </summary> protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); if (this.ReadOnly) return; // 特殊键, 不处理 if ((int)e.KeyChar <= 31) return; // 非数字键, 放弃该输入 if (!char.IsDigit(e.KeyChar)) { //防止重复输入特殊符号 if ((e.KeyChar == 46 && this.Text.Contains('.')) || (e.KeyChar == 45 && (this.Text.Contains('-')))) { e.Handled = true; return; } switch (e.KeyChar) { case '.'://小数点 if (this.Text.Length == 0) { this.Text = "0."; e.Handled = true; //控制光标在小数点之后 this.SelectionStart += 2; this.SelectionLength = 0; return; } else break; case '-'://负数 if (this.Text.Length > 0) { e.Handled = true; return; } else break; default : e.Handled = true; return; }//end switch }//end if (!char.IsDigit(e.KeyChar)) #region 科学计数 if (_isScientificNotation) { //增加科学计数,每3位“,”分隔 if (this.Text.Length > 2) { double num; bool isAdd = false;//是否加分号 string currentText; //判断数字格式是否正确 isError = !Double.TryParse(Text.Replace(",", ""), out num); if (!isError)//格式正确,是数值型 { currentText = Math.Abs(num).ToString(); if (!currentText.Contains('.'))//整数 { if (currentText.Length % 3 == 0) isAdd = true; else isAdd = false; } else { int i=currentText.IndexOf('.'); currentText = currentText.Substring(i+1, currentText.Length - i-1); if (currentText.Length % 3 == 0) isAdd = true; else isAdd = false; } } if (isAdd) { this.Text += "," + e.KeyChar; this.SelectionStart += this.Text.Length; this.SelectionLength = 0; e.Handled = true; }//end if (isAdd) }//end if (this.Text.Length > 2) }//end if (_isScientificNotation) #endregion } /// <summary> /// 通过消息模拟键盘录入 /// </summary> private void SendCharKey(char c) { Message msg = new Message(); msg.HWnd = this.Handle; msg.Msg = WM_CHAR; msg.WParam = (IntPtr)c; msg.LParam = IntPtr.Zero; base.WndProc(ref msg); } /// <summary> /// 清除当前TextBox的选择 /// </summary> private void ClearSelection() { if (this.SelectionLength == 0) return; int selLength = this.SelectedText.Length; this.SelectionStart += this.SelectedText.Length; // 光标在选择之后 this.SelectionLength = 0; for (int k = 1; k <= selLength; k++) { this.DeleteText(Keys.Back); } } /// <summary> /// 删除当前字符, 并计算SelectionStart值 /// </summary> private void DeleteText(Keys key) { int selStart = this.SelectionStart; if (key == Keys.Delete) // 转换Delete操作为BackSpace操作 { selStart += 1; if (selStart > base.Text.Length) return; } if (selStart == 0 || selStart > base.Text.Length) // 不需要删除 return; if (selStart == 1 && base.Text.Length == 1) { base.Text = ""; base.SelectionStart = 0; } else // selStart > 0 { base.Text = base.Text.Substring(0, selStart - 1) + base.Text.Substring(selStart, base.Text.Length - selStart); base.SelectionStart = selStart - 1; } } #endregion }
注:有些地方可能还不是很完善,欢迎批评指正。