C#TextBox中只允許輸入數字的解決方法

C#TextBox中只允許輸入數字的解決方法

最近看到一些關於TextBox中限制只允許輸入數字的博文,這類問題常常用事後處理模式:錄入字符結束後在控件離開(如Exit事件)或確認時進行判斷。本文探討控件錄入操作的事前處理模式:做錄入操作時屏蔽非數字字符。下面,結合筆者前段時間修改完善的開源數值文框TNumEditBox控件,介紹一個基於定製TextBox控件的解決方法。
  在定製的TextBox控件中,如果只允許輸入數字,需要考慮如下三種情況:
  正常按鍵輸入的字符,包括西文、中文字符等
  通過鍵盤快捷鍵方式貼入的文本,即Ctrl+V操作
  通過上下文關聯菜單的Mouse操作貼入的文本,即粘貼操作
  在探討的同類文章中,多數只考慮了第1種情況,忽略得了第23種常見的操作。本文探討的處理方法核心思路是重寫事件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; // 輸入鍵盤字符消息

                  0x0102 msg.WParam = (IntPtr)c;

                  msg.LParam = IntPtr.Zero;

                  base.WndProc(ref msg);

           }
  3、重寫消息處理方法WndProc()
  可以在定製TextBox控件中創建無內容的上下文菜單對象,從而屏蔽該菜單,方法是在定製控件的構造函數中增加如下代碼:
  public class CustomTextBox: TextBox{ // 創建無內容菜單對象, 等價屏蔽該控件的上下文單  

             this.ContextMenu = new ConTextMenu();

       }

由於上下文菜單的Paste操作對應WindowsWM_PASTE消息,於是可以在控件的WndProc()方法中捕獲該消息,然後獲得剪切板 ClipBoard中的內容,最後通過SendKeys.Send()方法模擬鍵盤錄入操作。需要注意,這裏不能調用前面ProcessCmdKey() 中模擬鍵盤輸入函數SendCharKey()。見如下代碼:
  protected override void WndProc(ref Message m) // 捕獲MousePaste消息

      {     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控件的ExitValidate等事件中進行輸入後處理——離開該控時進行驗證。但事後處理模式有如下不足:
  與數據源綁定時輸入非數字字符可能拋出異常,需要考慮異常捕獲
  需要判斷數據並給出錯誤提示等處理
  上述內容是從筆者的開源數值型數據編輯控件TNumEditBox中修改刪減而來的,該控件考慮的情況比只允許數字輸入要複雜得多,感興趣者可以參考並指正。需要指出,TNumEditBox的核心思路來自免費的Delphi控件PBNumEdit和開源的C#控件BANumEdit。作爲回報,筆者也將TNumEditBox開源併發布到CodeProject
  這裏探討的是屏蔽非數字鍵輸入,顯然可以推廣到屏蔽其它特殊鍵如Tab和指定字母等。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章