นี่คือการควบคุมแบบกำหนดเอง Winforms แบบสแตนด์อโลนที่เรียบง่ายซึ่งได้มาจากกล่องข้อความมาตรฐานที่อนุญาตเฉพาะอินพุต System.Int32 (สามารถปรับให้เข้ากับประเภทอื่นเช่น System.Int64 และอื่น ๆ ได้อย่างง่ายดาย) รองรับการคัดลอก / วางและจำนวนลบ:
public class Int32TextBox : TextBox
{
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
NumberFormatInfo fi = CultureInfo.CurrentCulture.NumberFormat;
string c = e.KeyChar.ToString();
if (char.IsDigit(c, 0))
return;
if ((SelectionStart == 0) && (c.Equals(fi.NegativeSign)))
return;
// copy/paste
if ((((int)e.KeyChar == 22) || ((int)e.KeyChar == 3))
&& ((ModifierKeys & Keys.Control) == Keys.Control))
return;
if (e.KeyChar == '\b')
return;
e.Handled = true;
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int WM_PASTE = 0x0302;
if (m.Msg == WM_PASTE)
{
string text = Clipboard.GetText();
if (string.IsNullOrEmpty(text))
return;
if ((text.IndexOf('+') >= 0) && (SelectionStart != 0))
return;
int i;
if (!int.TryParse(text, out i)) // change this for other integer types
return;
if ((i < 0) && (SelectionStart != 0))
return;
}
base.WndProc(ref m);
}
อัปเดต 2017 : คำตอบแรกของฉันมีปัญหา:
- คุณสามารถพิมพ์สิ่งที่ยาวกว่าจำนวนเต็มของประเภทที่กำหนด (ตัวอย่างเช่น 2147483648 มากกว่า Int32.MaxValue);
- โดยทั่วไปแล้วไม่มีการตรวจสอบความถูกต้องของผลลัพธ์ของสิ่งที่พิมพ์
- จัดการกับ int32 เท่านั้นคุณจะต้องเขียนกล่องข้อความที่ควบคุมมาสำหรับแต่ละประเภท (Int64 และอื่น ๆ )
ดังนั้นฉันจึงพบกับรุ่นอื่นที่เป็นแบบทั่วไปมากกว่าซึ่งยังคงรองรับการคัดลอก / วาง + และ - เซ็น ฯลฯ
public class ValidatingTextBox : TextBox
{
private string _validText;
private int _selectionStart;
private int _selectionEnd;
private bool _dontProcessMessages;
public event EventHandler<TextValidatingEventArgs> TextValidating;
protected virtual void OnTextValidating(object sender, TextValidatingEventArgs e) => TextValidating?.Invoke(sender, e);
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (_dontProcessMessages)
return;
const int WM_KEYDOWN = 0x100;
const int WM_ENTERIDLE = 0x121;
const int VK_DELETE = 0x2e;
bool delete = m.Msg == WM_KEYDOWN && (int)m.WParam == VK_DELETE;
if ((m.Msg == WM_KEYDOWN && !delete) || m.Msg == WM_ENTERIDLE)
{
DontProcessMessage(() =>
{
_validText = Text;
_selectionStart = SelectionStart;
_selectionEnd = SelectionLength;
});
}
const int WM_CHAR = 0x102;
const int WM_PASTE = 0x302;
if (m.Msg == WM_CHAR || m.Msg == WM_PASTE || delete)
{
string newText = null;
DontProcessMessage(() =>
{
newText = Text;
});
var e = new TextValidatingEventArgs(newText);
OnTextValidating(this, e);
if (e.Cancel)
{
DontProcessMessage(() =>
{
Text = _validText;
SelectionStart = _selectionStart;
SelectionLength = _selectionEnd;
});
}
}
}
private void DontProcessMessage(Action action)
{
_dontProcessMessages = true;
try
{
action();
}
finally
{
_dontProcessMessages = false;
}
}
}
public class TextValidatingEventArgs : CancelEventArgs
{
public TextValidatingEventArgs(string newText) => NewText = newText;
public string NewText { get; }
}
สำหรับ Int32 คุณสามารถหาได้จากมันเช่นนี้
public class Int32TextBox : ValidatingTextBox
{
protected override void OnTextValidating(object sender, TextValidatingEventArgs e)
{
e.Cancel = !int.TryParse(e.NewText, out int i);
}
}
หรือไม่มีการสืบทอดให้ใช้เหตุการณ์ TextValidating ใหม่เช่นนี้:
var vtb = new ValidatingTextBox();
...
vtb.TextValidating += (sender, e) => e.Cancel = !int.TryParse(e.NewText, out int i);
แต่สิ่งที่ดีคือมันใช้ได้กับสตริงใด ๆ และรูทีนการตรวจสอบความถูกต้องใด ๆ