直接写代码
public class DigitTextBox : TextBox, ISupportInitialize { [DllImport("user32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] [ResourceExposure(ResourceScope.None)] public static extern bool MessageBeep(int type); private static readonly decimal DefaultValue = decimal.Zero; private static readonly decimal DefaultMinimum = decimal.MinValue; private static readonly decimal DefaultMaximum = decimal.MaxValue; private const bool DefaultHexadecimal = false; private const int InvalidValue = -1; [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), Bindable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public override string Text { get => base.Text; set { base.Text = value; ChangingText = false; // 通常,“文本已更改”事件处理程序中的代码将“ ChangeingText”设置回false。 // 如果文本实际上没有更改,则事件处理程序将永远不会触发。 // 从此属性退出时,ChangeingText应该始终为false。 if (UserEdit) { ValidateEditText(); } } } /// <summary> /// 获取或设置分配给上下控件的值。 /// </summary> [Category(nameof(Appearance)), Bindable(true), Description("当前值")] public decimal Value { get { if (UserEdit) { ValidateEditText(); } return currentValue; } set { if (value != currentValue) { if (!initializing && ((value < minimum) || (value > maximum))) { throw new ArgumentOutOfRangeException("Value", $"值必须在{minimum}-{maximum}之间"); } else { currentValue = value; OnValueChanged(EventArgs.Empty); currentValueChanged = true; UpdateEditText(); } } } } public bool userEdit; /// <summary> /// 获取或设置一个值,该值指示用户是否输入了值。 /// </summary> protected bool UserEdit { get { return userEdit; } set { userEdit = value; } } ////////////////////////////////////////////////////////////// // 成员变量 ////////////////////////////////////////////////////////////// // 最小值和最大值 private decimal minimum = DefaultMinimum; private decimal maximum = DefaultMaximum; // 内部存储当前值 private decimal currentValue = DefaultValue; private bool currentValueChanged; public bool ChangingText { get => changingText; private set => changingText = value; } // onValueChanged事件的事件处理程序 private event EventHandler ValueChanged = null; // 初始化控件时禁用值范围检查 private bool initializing = false; private bool changingText; protected override void OnKeyPress(KeyPressEventArgs e) { base.OnKeyPress(e); NumberFormatInfo numberFormatInfo = System.Globalization.CultureInfo.CurrentCulture.NumberFormat; string decimalSeparator = numberFormatInfo.NumberDecimalSeparator; string groupSeparator = numberFormatInfo.NumberGroupSeparator; string negativeSign = numberFormatInfo.NegativeSign; string keyInput = e.KeyChar.ToString(); if (char.IsDigit(e.KeyChar)) { // 数字 } else if ((keyInput.Equals(decimalSeparator) && !Text.Contains(decimalSeparator)) || (keyInput.Equals(groupSeparator) && !(Text.Length > 0 && Text.LastOrDefault().ToString() == (groupSeparator))) || (keyInput.Equals(negativeSign) && !Text.Contains(negativeSign) && SelectionStart == 0)) { // 小数点分隔符/负号 } else if (e.KeyChar == '\b') { // 退格键 } else if ((ModifierKeys & (Keys.Control | Keys.Alt)) != 0) { // 让编辑控件处理控件和alt组合键 } else { // 吃掉这个无效的键并发出哔声 e.Handled = true; MessageBeep(0); } } protected void UpdateEditText() { // 如果要初始化,我们还不想更新编辑文本,以防万一该值无效。 if (initializing) { return; } // 如果当前值是用户编辑的,则在重新格式化之前解析该值 if (UserEdit) { ParseEditText(); } // 尝试设置Value属性之前,请验证用户是否没有以“-”开头的字符串,因为“-”是有效的字符,可用来以该字符开头的字符串代表负数。 if (currentValueChanged || (!string.IsNullOrEmpty(Text) && !(Text.Length == 1 && Text == "-"))) { currentValueChanged = false; ChangingText = true; Text = GetNumberText(currentValue); } } private string GetNumberText(decimal num) { string text; text = num.ToString(CultureInfo.CurrentCulture); return text; } protected void ParseEditText() { try { // 尝试设置Value属性之前,请验证用户是否没有以“-”开头的字符串,因为“-”是有效的字符,可用来以一个负数开头的字符串。 if (!string.IsNullOrEmpty(Text) && !(Text.Length == 1 && Text == "-")) { Value = Constrain(decimal.Parse(Text, CultureInfo.CurrentCulture)); } } catch { // Leave value as it is } finally { UserEdit = false; } } private decimal Constrain(decimal value) { if (value < minimum) { value = minimum; } if (value > maximum) { value = maximum; } return value; } protected virtual void OnValueChanged(EventArgs e) { ValueChanged?.Invoke(this, e); } protected override void OnTextChanged(EventArgs e) { base.OnTextChanged(e); OnTextBoxTextChanged(this, e); } protected virtual void OnTextBoxTextChanged(object source, EventArgs e) { if (changingText) { ChangingText = false; } else { UserEdit = true; } } protected override void OnLostFocus(EventArgs e) { base.OnLostFocus(e); if (UserEdit) { ValidateEditText(); } } private void ValidateEditText() { ParseEditText(); UpdateEditText(); } public void EndInit() { initializing = false; Value = Constrain(currentValue); UpdateEditText(); } public void BeginInit() { initializing = true; } }