來源:中國自學編程網收集整理 發布日期:2008-12-02 最近看到一些關於TextBox中限制只允許輸入數位博文,這類問題常常用事後處理模式:錄入字元結束後在控制項離開(如Exit事件)或確認時進行判斷。本文探討控制項錄入操作的事前處理模式:做錄入操作時屏蔽非數字字元。下面,結合筆者前段時間修改完善的開源數值文框TNumEditBox控制項,介紹一個基於定製TextBox控制項的解決方案。 在定製的TextBox控制項中,如果只允許輸入數字,需要考慮如下三種情況: 1).正常按鍵輸入的字元,包括西文、中文字元等 2).通過鍵盤快速鍵方式貼入的文本,即Ctrl+V操作 3).通過上下文關聯菜單的Mouse操作貼入的文本,即”粘貼“操作 在探討的同類文章中,多數只考慮了第1種情況,忽略得了第2、3種常見的操作。本文探討的處理方法核心思路是重寫事件OnKeyPress()和兩個方法 ProcessCmdKey()與WndProc(),並把Ctrl+V、關聯菜單的Paste操作統一到鍵盤錄入操作中,從而在 OnKeyPress()屏蔽掉非數字鍵。 1、重寫鍵盤事件OnKeyPress() 鍵盤輸入的字元可以通過重寫TextBox控制項的OnKeyPress()事件處理,見如下代碼: protected override void OnKeyPress(KeyPressEventArgs e) // 屏蔽非數字鍵 { base.OnKeyPress(e); if (this.ReadOnly) // 唯讀, 不處理
return; if ((int)e.KeyChar <= 32) // 特殊鍵(含空格), 不處理 return; if (!char.IsDigit(e.KeyChar)) // 非數字鍵, 放棄該輸入 { e.Handled = true;
return; } }
2、重寫命令鍵處理方法ProcessCmdKey() 可以在ProcessCmdKey()中捕獲快速鍵Ctrl+V操作。首先要清除當前的選擇文本,然後讀取剪下板ClipBoard中的內容,最後通過類比鍵盤輸入的方式”輸入“ClipBoard的內容。需要指出,在ProcessCmdKey()方法中不能使用靜態方法 SendKeys.Send(),但可以通過控制項的WndProc()方法發送字元訊息以達到類比鍵盤錄入的目的。見如下代碼: protected override bool ProcessCmdKey(ref Message msg, Keys keyData) // 捕獲Ctrl+V
{ 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); } 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); } 3、重寫訊息處理方法WndProc() 可以在定製TextBox控制項中建立無內容的操作功能表對象,從而屏蔽該菜單,方法是在定製控制項的建構函式中增加如下代碼: public class CustomTextBox : TextBox
{ // 建立無內容菜單對象, 等價屏蔽該控制項的操作功能表 this.ContextMenu = new ConTextMenu(); } 由於操作功能表的Paste操作對應Windows的WM_PASTE訊息,於是可以在控制項的WndProc()方法中捕獲該訊息,然後獲得剪下板 ClipBoard中的內容,最後通過SendKeys.Send()方法類比鍵盤錄入操作。需要注意,這裡不能調用前面ProcessCmdKey() 中類比鍵盤輸入函數SendCharKey()。見如下代碼: protected override void WndProc(ref Message m) // 捕獲Mouse的Paste訊息 { if (m.Msg == WM_PASTE) // 選擇操作功能表的"粘貼"操作 { this.ClearSelection(); SendKeys.Send(Clipboard.GetText()); // 類比鍵盤輸入 } else { base.WndProc(ref m); } } 4、消除選擇ClearSelection()、刪除字元DeleteText()
還必須分析前面代碼中的兩個函數: ClearSelection()用以清除當前的選擇文本,即清除this.SelectedText DeleteText()則刪除當前字元 需要指出其中的技巧,就是轉換Delete鍵操作為BackSpace操作。此外,函數DeleteText()還需要確定當前的this.SelectionStart值。具體代碼如下: private void ClearSelection() // 清除當前TextBox的選擇
{ 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); } private void DeleteText(Keys key) // 刪除字元並計算SelectionStart值 { 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; } } 5、結語
本文探討的是TextBox控制項輸入的事前處理模式,即在輸入字元的同時屏蔽非數字鍵。在實際應用中一般採取事後處理模式,即在TextBox控制項的 Exit、Validate等事件中進行輸入後處理——離開該控時進行驗證。但事後處理模式有如下不足: 與資料來源綁定時輸入非數字字元可能拋出異常,需要考慮異常捕獲 需要判斷資料並給出錯誤提示等處理
上述內容是從筆者的開源數值型資料編輯控制項TNumEditBox中修改刪減而來的,該控制項考慮的情況比只允許數字輸入要複雜得多,感興趣者可以參考並指正。需要指出,TNumEditBox的核心思路來自免費的Delphi控制項PBNumEdit和開源的C#控制項BANumEdit。作為回報,筆者也將 TNumEditBox開源並發布到CodeProject。 這裡探討的是屏蔽非數字鍵輸入,顯然可以推廣到屏蔽其它特殊鍵如Tab和指定字母等。 |