在MFC裡需要獲取文本輸入時,經常會用到CEdit或者它的子類。可以通過設置它的Edit Control Styles來控制Edit控件的一些行為,例如說設置ES_NUMBER標識使控件只允許接受數字(雖然可以復制-粘貼非數字字符串到這個控件中)。
在.NET中,用於獲取文本輸入的控件是TextBox,但TextBox本身並不包含可以直接調用的方法或屬性來將其設置為只接受數字輸入。這個問題有好幾種方法來解決:
繼承TextBox並覆蓋其CreateParams屬性,對該屬性的Style成員添加ES_NUMBER標識;這個方法與MFC中用Edit Control Styles來初始化CEdit一樣。可以說是最偷懶的方法。
自行監聽TextBox的KeyDown事件,實現輸入驗證(但不保證復制-粘貼輸入的正確性);其實設置ES_NUMBER做的也是這件事,如果要實現的功能與Windows控件中默認的一樣的話沒必要自己監聽KeyDown事件;如果需要些額外的功能,例如說允許輸入負數、十六進制數等默認沒有的功能時,則監聽KeyDown事件是個有效的方式。
使用第三方編寫的繼承自TextBox的控件;這是擁有“不重復發明輪子”精神的人們的做法。既然獲取數字輸入應該是個常見問題,之前也肯定有人解決過,那麼利用別人的解決方案就行。
光是CodeProject上就有好幾個與這個相關的實現:
Validating Edit Controls。包括了NumericTextBox、AlphanumericTextBox、DateTextBox等許多版本的TextBox子類。值得一看。
A numeric textbox with a twist。
Numeric TextBox : Allow your users to enter numeric data the easy way。
使用MaskedTextBox 控件;這是.NET Framework自帶的一個TextBox的子類,實現了一個帶過濾功能的TextBox,可以自定義接受的輸入內容的格式。只要設置其string Mask屬性即可。如果覺得ES_NUMBER的功能不夠用,而自行監聽KeyDown事件來做驗證不夠優雅的話,這個MaskedTextBox絕對是值得考慮的選擇。例如說,要接受0到999999的數字,只要把Mask屬性設為"999,999.00"就行(意味著六位的可選十進制數字,一個小數點,和兩位必須輸入的小數)。MSDN上對這個控件有個簡單的walkthrough。
使用NumericUpDown控件。當需要獲取簡單數字輸入時,在.NET世界中最直接的方法不是去想辦法與TextBox搏斗,而應該換個控件來用——NumericUpDown。這個控件不但能接受來自鍵盤的數字輸入,還有一組上下箭頭來步進。它包含了許多可以設置的屬性,例如顯示分隔符逗號的bool ThousandsSeparator、控制最小/最大值的decimal Minimum/decimal Maximum屬性等。
下面對這幾種解決方法的其中一些稍微討論一下。
一、繼承TextBox並覆蓋其CreateParams屬性
使用這種方法的NumericTextBox的實現(代碼的第1-12行)及用例:
C#代碼
public class NumericTextBox : System.Windows.Forms.TextBox
{
private const int ES_NUMBER = 0x2000; // ( defined in WinUser.h )
protected override System.Windows.Forms.CreateParams CreateParams {
get {
System.Windows.Forms.CreateParams cp = base.CreateParams;
cp.Style |= ES_NUMBER;
return cp;
}
}
}
#region use case code sample
sealed class TestForm : System.Windows.Forms.Form
{
private NumericTextBox m_ntxt;
public TestForm() {
InitializeComponent();
}
private void InitializeComponent() {
this.m_ntxt = new NumericTextBox();
this.m_ntxt.Dock = System.Windows.Forms.DockStyle.Fill;
this.ClientSize = new System.Drawing.Size(100, 60);
this.Controls.Add(this.m_ntxt);
this.PerformLayout();
}
[System.STAThread]
static void Main(string[] args) {
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
System.Windows.Forms.Application.Run(new TestForm());
}
}
#endregion
運行程序,在輸入任意非0-9的字符時的樣子:
(截圖反映的是在我的簡體中文Windows XP上的運行效果;若系統語言不是簡體中文的話會根據系統語言而不同)
如果這個文本框已經能滿足需求,就沒必要自己監聽KeyDown事件那麼麻煩了。
二、自行監聽KeyDown事件
可以參考CodeProject上Numeric TextBox : Allow your users to enter numeric data the easy way的實現方式。基本原理就是在KeyDown的響應方法中對e.KeyCode進行判斷,如果輸入不滿足條件則設置某個標識,然後再KeyPress的響應方法裡設置e.Handled = true;來取消該次事件。
最簡單來說類似這樣:
C#代碼
using System;
using System.Drawing;
using System.Windows.Forms;
sealed class TestForm : Form
{
private TextBox m_textBox;
private bool m_nonNumberEntered = false;
public TestForm() {
InitializeComponent();
}
private void InitializeComponent() {
this.m_textBox = new TextBox();
this.m_textBox.Dock = DockStyle.Fill;
this.m_textBox.KeyDown += m_textBox_KeyDown;
this.m_textBox.KeyPress += m_textBox_KeyPress;
this.ClientSize = new Size(100, 60);
this.Controls.Add(this.m_textBox);
this.PerformLayout();
}
private void m_textBox_KeyDown(object sender, KeyEventArgs e) {
// Initialize the flag to false.
m_nonNumberEntered = false;
// Determine whether the keystroke is a number from the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9) {
// Determine whether the keystroke is a number from the keypad.
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9) {
// Determine whether the keystroke is a backspace.
if(e.KeyCode != Keys.Back) {
// A non-numerical keystroke was pressed.
// Set the flag to true and evaluate in KeyPress event.
m_nonNumberEntered = true;
}
}
}
}
private void m_textBox_KeyPress(object sender, KeyPressEventArgs e) {
// Check for the flag being set in the KeyDown event.
if (m_nonNumberEntered) {
// Stop the character from being entered into the control
// since it is non-numerical.
e.Handled = true;
}
}
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
(判斷邏輯來自KeyEventArgs在MSDN文檔上的范例代碼)
得到的文本框外觀與一般的TextBox沒區別,只是無法由鍵盤輸入數字字符以外的字符。要避免任意字符串被復制-粘貼進來的話,要另外做些判斷。這裡就不詳細寫了。
三、使用MaskedTextBox
使用例子:
C#代碼
using System;
using System.Windows.Forms;
sealed class TestForm : Form
{
private MaskedTextBox m_maskedTextBox;
private ToolTip m_toolTip;
public TestForm() {
InitializeComponent();
}
private void InitializeComponent() {
this.m_maskedTextBox = new MaskedTextBox();
this.m_maskedTextBox.Mask = "999,999.00";
this.m_maskedTextBox.Dock = DockStyle.Fill;
this.m_maskedTextBox.MaskInputRejected += m_maskedTextBox_InputRejected;
this.m_maskedTextBox.KeyDown += m_maskedTextBox_KeyDown;
this.m_toolTip = new ToolTip();
this.ClientSize = new Size(100, 60);
this.Controls.Add(this.m_maskedTextBox);
this.PerformLayout();
}
private void m_maskedTextBox_InputRejected(object sender,
MaskInputRejectedEventArgs e) {
toolTip.ToolTipTitle = "Invalid Input";
toolTip.Show("Only digits (0-9) are allowed.",
m_maskedTextBox, m_maskedTextBox.Location, 5000);
}
private void m_maskedTextBox_KeyDown(object sender, KeyEventArgs e) {
m_toolTip.Hide(maskedTextBox);
}
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
這段代碼是手寫的;要是用VS2005/VS2008的設計器的話,這個例子的所有功能都能直接在設計器裡指定。
輸入內容(可以看到分隔符都不需要自己寫了,已經寫好在輸入框裡;只要填空就行):
輸入內容不符合Mask屬性指定的模式時:
四、使用NumericUpDown
C#代碼
using System;
using System.Drawing;
using System.Windows.Forms;
sealed class TestForm : Form
{
private NumericUpDown m_numericUpDown;
public TestForm() {
InitializeComponent();
}
private void InitializeComponent() {
this.m_numericUpDown = new NumericUpDown();
this.m_numericUpDown.Value = 100;
this.m_numericUpDown.Dock = DockStyle.Fill;
this.m_numericUpDown.ThousandsSeparator = true;
this.m_numericUpDown.Maximum = int.MaxValue;
this.ClientSize = new Size(100, 60);
this.Controls.Add(this.m_numericUpDown);
this.PerformLayout();
}
[System.STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
這段代碼是手寫的;要是用VS2005/VS2008的設計器的話,這個例子的所有功能都能直接在設計器裡指定。
NumericUpDown的內容的值可以用Value屬性來設置或獲取,類型為decimal。
截圖:(輸入不符合要求的字符時,默認行為是beep一下,沒有工具條的提示)