在定制的TextBox控制項中,如果只允許輸入數位,需要考慮如下三種情況:
正常按鍵輸入的字元,包括西文、中文字元等
通過鍵盤快捷鍵方式貼入的文本,即Ctrl+V操作
通過上下文關聯功能表的Mouse操作貼入的文本,即”粘貼“操作
     在探討的同類文章中,多數隻考慮了第1種情況,忽略得了第2、3種常見的操作。本文探討的處理方法核心思路是重載事件OnKeyPress()和兩個法ProcessCmdKey()與WndProc(),並把Ctrl+V、關聯功能表的Paste操作統一到鍵盤錄入操作中,從而在OnKeyPress()遮罩掉非數位鍵。
1、重載鍵盤事件OnKeyPress()
     鍵盤輸入的字元可以通過重載TextBox控制項的OnKeyPress()事件處理,見如下代碼:
     /// <summary>
    /// 遮罩非數字鍵
    /// </summary>
    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);

        if (this.ReadOnly)
        {
            return;
        }
       
        // 特殊鍵, 不處理
        if ((int)e.KeyChar <= 31)
        {
            return;
        }

        // 非數位鍵, 放棄該輸入
        if (!char.IsDigit(e.KeyChar))
        {
            e.Handled = true;
            return;
        }
    }
2、重載命令鍵處理方法ProcessCmdKey()
      可以在ProcessCmdKey()中捕獲快捷鍵Ctrl+V操作。首先要清除當前的選擇文本,然後讀取剪切板ClipBoard中的內容,最後通過類比鍵盤輸入的方式”輸入“ClipBoard的內容。需要指出,在ProcessCmdKey()方法中不能使用靜態方法SendKeys.Send(),但可以通過控制項的WndProc()方法發送字元消息以達到類比鍵盤錄入的目的。見如下代碼:
    /// <summary>
    /// 捕獲Ctrl+V快捷鍵操作
    /// </summary>
    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        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);
    }
    /// <summary>
    /// 通過消息類比鍵盤錄入
    /// </summary>
    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()。見如下代碼:
    /// <summary>
    /// 捕獲Mouse的Paste消息
    /// </summary>
    protected override void WndProc(ref Message m)
    {
        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值。具體代碼如下:
    /// <summary>
    /// 清除當前TextBox的選擇
    /// </summary>
    private void ClearSelection()
    {
        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);
        }
    }

    /// <summary>
    /// 刪除當前字元, 並計算SelectionStart值
    /// </summary>
    private void DeleteText(Keys key)
    {
        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、結語
     上述內容是從筆者的開源數值型資料編輯控制項TNumEditBox中修改刪減而來的,該控制項考慮的情況比只允許數位輸入要複雜得多,感興趣者可以參考並指正。需要指出,TNumEditBox的核心思路來自免費的Delphi控制項PBNumEdit和開源的C#控制項BANumEdit。作為回報(giveback),筆者也將TNumEditBox開源並發佈到CodeProject。。   


資料來源: http://www.cnblogs.com/xiediy/archive/2008/10/10/1307738.html


arrow
arrow
    全站熱搜

    Roger 發表在 痞客邦 留言(0) 人氣()