kurukuru-papaのブログ

主に、ソフトウェア開発に関連したメモを書き溜めたいと思います。

入力制限付きテキストボックス

Visual C#の標準のTextBoxに対して、入力制限をかけられる拡張テキストボックスを作成してみました。入力制限は、正規表現で表します。

ソース

InputLimitTextBox.cs

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ClassLibrary
{
    /// <summary>
    /// 入力制限ありテキストボックス
    /// </summary>
    public class InputLimitTextBox : System.Windows.Forms.TextBox
    {
        private string _InputLimit = string.Empty;
        private System.Text.RegularExpressions.Regex _InputLimitRegex = null;

        [Description("許可する入力文字列を表す正規表現を設定します。例:英数字 \\w*、浮動小数点 \\d{0,5}(\\.\\d{0,2})?")]
        public virtual string InputLimit
        {
            get { return _InputLimit; }
            set
            {
                _InputLimit = value;
                _InputLimitRegex = new System.Text.RegularExpressions.Regex("^" + _InputLimit + "$");
                Text = string.Empty;
            }
        }

        /// <summary>
        /// テキスト変更中イベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void TextChangingEventHandler(object sender, TextChangingEventArgs e);

        [Description("Text プロパティの値がコントロールで変更される直前に発生します。")]
        public event TextChangingEventHandler TextChanging;

        #region protectedメソッド

        protected virtual void OnTextChanging(TextChangingEventArgs e)
        {
            // 入力チェック
            if (_InputLimitRegex.Match(e.NewText).Success != true)
            {
                e.Cancel = true;
            }

            // ユーザ定義のイベントを呼ぶ
            if (TextChanging != null)
            {
                TextChanging(this, e);
            }
        }

        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            const int WM_KEYDOWN = 0x100;
            const int WM_CHAR = 0x102;
            const int WM_PASTE = 0x302;

            switch (m.Msg)
            {
                case WM_CHAR:
                    // 文字が入力された場合
                    {
                        TextChangingEventArgs e = new TextChangingEventArgs(
                            this.CreateNewText(Convert.ToString((char)(m.WParam.ToInt32()))));
                        this.OnTextChanging(e);
                        if (e.Cancel == true)
                        {
                            // 入力された文字をキャンセルする
                            return;
                        }
                    }
                    break;

                case WM_KEYDOWN:
                    if (m.WParam.ToInt32() == (int)Keys.Delete)
                    {
                        // Deleteキーが押された場合

                        TextChangingEventArgs e = new TextChangingEventArgs(
                            this.CreateNewText(string.Empty, true));
                        this.OnTextChanging(e);
                        if (e.Cancel == true)
                        {
                            // Deleteキーをキャンセルする
                            m.Result = new IntPtr(1); // true
                            return;
                        }
                    }
                    break;

                case WM_PASTE:
                    // 貼り付けされた場合
                    // クリップボードから入力内容を取得する
                    string clipText = (string)Clipboard.GetDataObject().GetData(DataFormats.Text);
                    if (clipText != null)
                    {
                        TextChangingEventArgs e = new TextChangingEventArgs(this.CreateNewText(clipText));
                        this.OnTextChanging(e);
                        if (e.Cancel == true)
                        {
                            // 入力された内容をキャンセルする
                            return;
                        }
                    }
                    break;
            }

            // 親クラスのWndProcを呼ばないと、イベント内容がなかったことになる。
            base.WndProc(ref m);
        }

        #endregion

        #region privateメソッド

        /// <summary>
        /// 擬似的に変更後のテキストを作成する
        /// </summary>
        /// <param name="inputText"></param>
        /// <returns></returns>
        private string CreateNewText(string inputText)
        {
            return this.CreateNewText(inputText, false);
        }

        /// <summary>
        /// 擬似的に変更後のテキストを作成する
        /// </summary>
        /// <param name="inputText"></param>
        /// <param name="deleteKey"></param>
        /// <returns></returns>
        private string CreateNewText(string inputText, bool deleteKey)
        {
            // 画面表示されているテキストを初期設定し、
            // 範囲選択されている文字列を削除する。
            string newText = this.Text;
            newText = newText.Remove(this.SelectionStart, this.SelectionLength);

            // Delete キーを入力
            if (deleteKey)
            {
                if (this.SelectionLength == 0 && this.SelectionStart < this.TextLength)
                {
                    newText = newText.Remove(this.SelectionStart, 1);
                }

                return newText;
            }

            // テキストボックスが複数行対応してなければ、
            // 引数のテキストから改行コードを削除する。
            if (this.Multiline == false)
            {
                int crIndex = inputText.IndexOf("\r");
                if (crIndex > 0)
                {
                    inputText = inputText.Remove(crIndex);
                }

                int lrIndex = inputText.IndexOf("\n");
                if (lrIndex > 0)
                {
                    inputText = inputText.Remove(lrIndex);
                }
            }

            // ここまでで、引数のテキストがブランクとなっていれば、処理終了する。
            if (inputText == string.Empty)
            {
                return newText;
            }

            // BackSpace キーを入力
            if (Convert.ToInt32(inputText[0]) == (int)Keys.Back)
            {
                if (this.SelectionLength == 0 && this.SelectionStart > 0)
                {
                    newText = newText.Remove(this.SelectionStart - 1, 1);
                }
            }
            else
            {
                newText = newText.Insert(this.SelectionStart, inputText);
            }

            return newText;
        }

        #endregion
    }
}


TextChangingEventArgs.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClassLibrary
{
    /// <summary>
    /// テキスト変更中に発生するイベントクラス。
    /// </summary>
    public class TextChangingEventArgs : System.ComponentModel.CancelEventArgs
    {
        /// <summary>
        /// 変更後のテキスト
        /// </summary>
        private string _NewText;

        public TextChangingEventArgs()
        {
            this._NewText = string.Empty;
        }

        public TextChangingEventArgs(string text)
        {
            this._NewText = text;
        }

        /// <summary>
        /// 変更後のテキスト
        /// </summary>
        public string NewText
        {
            get { return this._NewText; }
        }
    }
}


NumericLimitTextBox.cs
※InputLimitTextBoxを数値入力制限に特化したもの

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace ClassLibrary
{
    /// <summary>
    /// 数値入力制限テキストボックス
    /// </summary>
    public class NumericLimitTextBox : InputLimitTextBox
    {
        private int _IntLength = 10;
        private int _ScaleLength = 2;

        [DefaultValue(10)]
        [Description("整数部の桁数を設定します。")]
        public int IntLength
        {
            get { return _IntLength; }
            set { _IntLength = value; SetInputLimit(); }
        }

        [DefaultValue(2)]
        [Description("小数部の桁数を設定します。")]
        public int ScaleLength
        {
            get { return _ScaleLength; }
            set { _ScaleLength = value; SetInputLimit(); }
        }

        public override string InputLimit
        {
            get { return base.InputLimit; }
        }

        #region protectedメソッド

        protected void SetInputLimit()
        {
            base.InputLimit = "\\d{0," + _IntLength + "}(\\.\\d{0," + _ScaleLength + "})?";
        }

        #endregion
    }
}

環境

参考サイト