C#で数値入力専用のTextBoxを作る(WinForms)

下記仕様のTextBoxを作成します。

  • 数字のみ入力可能とする

  • 入力可能桁数をTextBoxのMaxLengthプロパティに設定された桁数までとする

  • フォーカスが当たっていないときは3桁毎のカンマ区切りで表示する

クリップボードからペーストされた時のチェックは未対応です。
Validatingイベント、Validatedイベント等でのチェック処理や入力値を用いる処理直前での値チェックは必要不可欠です。

まず、数値のみ入力できるようにするために KeyPress イベントを実装します。

private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    try
    {
        //バックスペースは使用可能とする
        if (e.KeyChar == 0x08)
        {
            return;
        }

        //数字キー以外の入力をキャンセルする
        if (e.KeyChar < 0x30 || e.KeyChar > 0x39)
        {
            e.Handled = true;
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
}

次に、入力可能桁数を制限します。
TextChanged イベントを下記のように実装します。

private void textBox1_TextChanged(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;

    try
    {
        txtBox.TextChanged -= textBox1_TextChanged;

        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            string value = txtBox.Text.Replace(",", "");

            //既に入力可能桁数に達しているなら、今回入力された値を除外する
            if (value.Length > txtBox.MaxLength)
            {
                var currentPoint = txtBox.SelectionStart;
                var left = value.Substring(0
                    , currentPoint > txtBox.MaxLength
                         ? txtBox.MaxLength : currentPoint);
                var right = left.Length >= txtBox.MaxLength
                     ? "" : value.Substring(currentPoint + 1);
                txtBox.Text = left + right;
                txtBox.SelectionStart = currentPoint;
            }
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex);
    }
    finally
    {
        //2桁以上で先頭が0なら先頭0を除外する
        if (txtBox.Text.StartsWith("0") && txtBox.Text != "0")
        {
            var currentPoint = txtBox.SelectionStart;
            txtBox.Text = txtBox.Text.Substring(1);
            if (currentPoint > txtBox.Text.Length)
            {
                txtBox.SelectionStart = txtBox.Text.Length;
            }
        }

        txtBox.TextChanged += textBox1_TextChanged;
    }
}

フォーカスが外れたときに3桁毎のカンマ区切りで表示させるために
Validated イベントを下記のように実装します。

private void textBox1_Validated(object sender, EventArgs e)
{
    try
    {
        var txtBox = sender as TextBox;
        if (!string.IsNullOrWhiteSpace(txtBox.Text))
        {
            txtBox.Text = int.Parse(txtBox.Text.Replace(",", "")).ToString("#,##0");
        }
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.Message);
    }
}

フォーカスが当たったときに3桁毎のカンマ区切りを解除します。
Enter イベントを下記のように実装します。

private void textBox1_Enter(object sender, EventArgs e)
{
    var txtBox = sender as TextBox;

    try
    {
        txtBox.TextChanged -= textBox1_TextChanged;

        txtBox.Text = txtBox.Text.Replace(",", "");
        txtBox.SelectAll();
    }
    catch (Exception ex)
    {
        Console.Error.WriteLine(ex.Message);
    }
    finally
    {
        txtBox.TextChanged += textBox1_TextChanged;
    }
}

最後にテキストボックスで右クリックメニューを表示させないようにするため、
フォームのコンストラクタで下記のようにコードを追加します。

public Form1()
{
    InitializeComponent();

	//これを追加
    textBox1.ContextMenu = new ContextMenu();
}