在 TextBox 中輸入數據,實時判斷是否超出範圍(如 0-1000),超出則提示並恢復/阻止輸入。
| 事件 | 觸發時機 | 優點 | 缺點 | 推薦場景 |
|---|---|---|---|---|
TextChanged | 文本改變後 | 簡單直觀、可獲取完整文本 | 易觸發遞歸、光標位置重置、用戶體驗差 | 簡單驗證、非數字輸入 |
KeyPress | 按鍵按下(字符輸入前) | 可攔截非法字符、光標不動 | 無法處理粘貼、拖拽、IME 輸入 | 僅允許數字/特定字符 |
Validating | 失去焦點前 | 標準驗證流程、配合 ErrorProvider | 不即時、需手動觸發焦點轉移 | 表單提交前整體驗證 |
PreviewKeyDown + TextChanged | 組合使用 | 兼顧即時攔截與粘貼處理 | 代碼較複雜 | 生產級嚴格輸入控制 |
public partial class Form1 : Form {
private string lastValidText = "0";
public Form1() {
InitializeComponent();
txtNumber.KeyPress += TxtNumber_KeyPress;
txtNumber.TextChanged += TxtNumber_TextChanged;
txtNumber.Validating += TxtNumber_Validating;
}
// 1. 僅允許數字、退格、刪除、小數點(可選)
private void TxtNumber_KeyPress(object sender, KeyPressEventArgs e) {
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && e.KeyChar != '.') {
e.Handled = true; // 忽略該按鍵
}
// 限制小數點只能出現一次
if (e.KeyChar == '.' && ((TextBox)sender).Text.Contains(".")) {
e.Handled = true;
}
}
// 2. 實時範圍檢查(文本改變後)
private void TxtNumber_TextChanged(object sender, EventArgs e) {
var tb = (TextBox)sender;
if (decimal.TryParse(tb.Text, out decimal value)) {
if (value > 1000) {
MessageBox.Show("輸入值不能超過 1000", "範圍錯誤",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
// 恢復上次合法值
tb.Text = lastValidText;
tb.SelectionStart = tb.Text.Length; // 光標移至末尾
} else {
lastValidText = tb.Text;
}
} else if (tb.Text != "" && tb.Text != ".") {
// 處理粘貼導致的非法文本
tb.Text = lastValidText;
tb.SelectionStart = tb.Text.Length;
}
}
// 3. 失去焦點時最終驗證(防漏網)
private void TxtNumber_Validating(object sender, CancelEventArgs e) {
var tb = (TextBox)sender;
if (decimal.TryParse(tb.Text, out decimal value)) {
if (value < 0 || value > 1000) {
e.Cancel = true; // 取消焦點轉移
errorProvider1.SetError(tb, "範圍必須在 0-1000 之間");
} else {
errorProvider1.SetError(tb, "");
}
} else {
e.Cancel = true;
errorProvider1.SetError(tb, "請輸入有效數字");
}
}
}
public class NumericTextBox : TextBox {
public decimal MinValue { get; set; } = decimal.MinValue;
public decimal MaxValue { get; set; } = decimal.MaxValue;
public int DecimalPlaces { get; set; } = 2;
private string _lastValidText = "0";
protected override void OnKeyPress(KeyPressEventArgs e) {
base.OnKeyPress(e);
// 允許:控制字符、數字、負號(首位)、小數點(唯一)
if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar)) {
if (e.KeyChar == '-' && SelectionStart == 0 && !Text.Contains("-")) return;
if (e.KeyChar == '.' && !Text.Contains(".") && DecimalPlaces > 0) return;
e.Handled = true;
}
}
protected override void OnTextChanged(EventArgs e) {
base.OnTextChanged(e);
if (decimal.TryParse(Text, out decimal value)) {
if (value < MinValue || value > MaxValue) {
Text = _lastValidText;
SelectionStart = Text.Length;
} else {
_lastValidText = Text;
}
} else if (Text != "" && Text != "-" && Text != ".") {
Text = _lastValidText;
SelectionStart = Text.Length;
}
}
protected override void OnValidating(CancelEventArgs e) {
base.OnValidating(e);
if (!decimal.TryParse(Text, out _) ||
decimal.Parse(Text) < MinValue || decimal.Parse(Text) > MaxValue) {
e.Cancel = true;
}
}
public decimal GetValue() => decimal.TryParse(Text, out var v) ? v : 0;
}
// 設計器中拖入 NumericTextBox,屬性設置:
numericTextBox1.MinValue = 0;
numericTextBox1.MaxValue = 1000;
numericTextBox1.DecimalPlaces = 0; // 整數
// 代碼中獲取值
decimal val = numericTextBox1.GetValue();
// 表單載入時綁定
errorProvider1.BlinkStyle = ErrorBlinkStyle.NeverBlink;
errorProvider1.ContainerControl = this;
// Validating 事件中
private void txtInput_Validating(object sender, CancelEventArgs e) {
if (!ValidateInput((TextBox)sender)) {
e.Cancel = true;
errorProvider1.SetError((TextBox)sender, "輸入超出允許範圍 (0-1000)");
} else {
errorProvider1.SetError((TextBox)sender, "");
}
}
TextChanged 中直接修改 Text 會再次觸發事件 → 使用標誌位或比較 lastValidText 避免遞歸KeyPress 無法攔截右鍵粘貼、拖拽文本 → 必須配合 TextChangedint.Parse 拋異常性能差 → 用 decimal.TryParse 或 int.TryParseValidating 事件 → 表單提交前最後一道防線CausesValidation = true(默認)確保驗證鏈生效System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator