DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

TextBoxに数字しか入力できないようにする

ここでは、テキストボックス(TextBoxコントロール)に数字以外の文字が入力できないようにする方法を幾つか紹介します。

注意:ここで紹介した方法だけでは、数字以外の文字列が入力されることを完全に防げることはできません。入力された文字列が本当に数字だけか確かめ、そうでなければ拒否するためには、必ずTextBoxのValidatingイベントを使用してください。文字列が数値に変換できるか調べる方法に関しては、「文字列が数字に変換できるか調べる」をご覧ください。

KeyPressイベントによる方法

テキストボックス(TextBox1)のKeyPressイベントで数字以外のキー入力をキャンセルするには、次のようなコードを書きます。

VB.NET
コードを隠すコードを選択
'TextBox1のKeyPressイベントハンドラ
Private Sub TextBox1_KeyPress(sender As Object, _
        e As System.Windows.Forms.KeyPressEventArgs)
    If e.KeyChar < "0"c OrElse "9"c < e.KeyChar Then
        '押されたキーが 0〜9でない場合は、イベントをキャンセルする
        e.Handled = True
    End If
End Sub
C#
コードを隠すコードを選択
//TextBox1のKeyPressイベントハンドラ
private void TextBox1_KeyPress(object sender,
    System.Windows.Forms.KeyPressEventArgs e)
{
    if (e.KeyChar < '0' || '9' < e.KeyChar)
    {
        //押されたキーが 0〜9でない場合は、イベントをキャンセルする
        e.Handled = true;
    }
}
補足:「e.KeyChar < '0' || e.KeyChar > '9'」(VB.NETでは「e.KeyChar < "0"c Or e.KeyChar > "9"c」)の条件式は、「!Char.IsDigit(e.KeyChar)」(VB.NETでは「Not Char.IsDigit(e.KeyChar)」)とすることもできます。
補足:.NET Framework 2.0からは、KeyDownイベントハンドラでKeyEventArgs.SuppressKeyPressプロパティをTrueにすることによっても、キー入力を無効にすることができます。KeyDownイベントハンドラでSuppressKeyPressプロパティをTrueにすると、KeyPressイベントは発生しなくなります。

上記のようにしたテキストボックスではDeleteキーを使用して文字を削除することはできますが、バックスペースは使用できなくなります。バックスペースが使えるようにするには、次のようにして数字に加えてバックスペース文字も許可するようにします。テキストボックスに入力できる文字を増やしたい時は、同様にしてIf文の条件式に許可する文字を追加してください。

VB.NET
コードを隠すコードを選択
'TextBox1のKeyPressイベントハンドラ
Private Sub TextBox1_KeyPress(sender As Object, _
        e As System.Windows.Forms.KeyPressEventArgs)
    '0〜9と、バックスペース以外の時は、イベントをキャンセルする
    If (e.KeyChar < "0"c OrElse "9"c < e.KeyChar) AndAlso _
            e.KeyChar <> ControlChars.Back Then
        e.Handled = True
    End If
End Sub
C#
コードを隠すコードを選択
//TextBox1のKeyPressイベントハンドラ
private void TextBox1_KeyPress(object sender,
   System.Windows.Forms.KeyPressEventArgs e)
{
    //0〜9と、バックスペース以外の時は、イベントをキャンセルする
    if ((e.KeyChar < '0' || '9' < e.KeyChar) && e.KeyChar != '\b')
    {
        e.Handled = true;
    }
}
補足:入力された文字が制御文字か調べるには、「Char.IsControl(e.KeyChar)」とすることもできます。
補足:このようにしてもテキストボックスでDeleteキーが使用できるのは、Deleteキーが押されてもKeyPressイベントが発生しないからです。Deleteキーが押されても何もしないようにする方法は、「コントロールで矢印、Tab、Enter、Escキーが押されたことを知る」をご覧ください。

IMEのオン、クリップボードからの貼り付けを防ぐ

しかしこれだけでは、IMEがオンになっている時は、数字以外の文字が入力されてしまいます。これを出来ないようにするには、テキストボックスのImeModeプロパティをDisableにしておきます。

また、クリップボードにコピーされた文字列を貼り付けることによっても数字以外を入力できてしまいます。クリップボードからのペーストを防ぐ方法は、「TextBoxにペースト(貼り付け)できないようにする」で説明しています。

TextBoxコントロールを継承して、数字とバックスペース以外の入力を無効にしたTextBoxの例を以下に示します。ここでは、数字のみで構成された文字列がクリップボードにあるときしか貼り付けできないようにしています。このコントロールを使う方法は、『「○○○クラスの代わりに派生クラスを使用します」の意味は?』をご覧ください。

VB.NET
コードを隠すコードを選択
Imports System.Windows.Forms
Imports System.Security.Permissions
Imports System.ComponentModel

''' <summary>
''' 数字とバックスペース以外の入力を無効にしたTextBox
''' </summary>
Public Class NumericTextBox
    Inherits TextBox
    Const WM_PASTE As Integer = &H302

    <SecurityPermission(SecurityAction.Demand, _
        Flags:=SecurityPermissionFlag.UnmanagedCode)> _
    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_PASTE Then
            Dim iData As IDataObject = Clipboard.GetDataObject()
            '文字列がクリップボードにあるか
            If Not iData Is Nothing AndAlso _
                    iData.GetDataPresent(DataFormats.Text) Then
                Dim clipStr As String = _
                    DirectCast(iData.GetData(DataFormats.Text), String)
                'クリップボードの文字列が数字のみか調べる
                If Not System.Text.RegularExpressions.Regex.IsMatch( _
                    clipStr, "^[0-9]+$") Then
                    Return
                End If
            End If
        End If

        MyBase.WndProc(m)
    End Sub

    Public Sub New()
        MyBase.New()
        'IMEを無効にする
        MyBase.ImeMode = ImeMode.Disable
        '数字以外で入力が可能な文字
        Me.SetAllowKeyChars(New Char() {ControlChars.Back})
    End Sub

    <Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
    Public Shadows Property ImeMode() As ImeMode
        Get
            Return MyBase.ImeMode
        End Get
        Set(value As ImeMode)
        End Set
    End Property

    Private _allowKeyChars As Char()
    ''' <summary>
    ''' 数字以外で入力が可能な文字を設定する
    ''' </summary>
    Public Sub SetAllowKeyChars(keyChars As Char())
        Me._allowKeyChars = keyChars
    End Sub
    ''' <summary>
    ''' 数字以外で入力が可能な文字を取得する
    ''' </summary>
    Public Function GetAllowKeyChars() As Char()
        Return Me._allowKeyChars
    End Function

    Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
        MyBase.OnKeyPress(e)
        '数字以外が入力された時はキャンセルする
        If (e.KeyChar < "0"c OrElse "9"c < e.KeyChar) AndAlso _
            Array.IndexOf(Me._allowKeyChars, e.KeyChar) < 0 Then
            e.Handled = True
        End If
    End Sub
End Class
C#
コードを隠すコードを選択
using System.Windows.Forms;
using System.Security.Permissions;
using System.ComponentModel;

/// <summary>
/// 数字とバックスペース以外の入力を無効にしたTextBox
/// </summary>
public class NumericTextBox : TextBox
{
    private const int WM_PASTE = 0x302;

    [SecurityPermission(SecurityAction.Demand,
        Flags = SecurityPermissionFlag.UnmanagedCode)]
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_PASTE)
        {
            IDataObject iData = Clipboard.GetDataObject();
            //文字列がクリップボードにあるか
            if (iData != null && iData.GetDataPresent(DataFormats.Text))
            {
                string clipStr = (string)iData.GetData(DataFormats.Text);
                //クリップボードの文字列が数字のみか調べる
                if (!System.Text.RegularExpressions.Regex.IsMatch(
                    clipStr,
                    @"^[0-9]+$"))
                {
                    return;
                }
            }
        }

        base.WndProc(ref m);
    }

    public NumericTextBox()
        : base()
    {
        //IMEを無効にする
        base.ImeMode = ImeMode.Disable;
        //数字以外で入力が可能な文字
        this.SetAllowKeyChars(new char[] { '\b' });
    }

    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
    public new ImeMode ImeMode
    {
        get { return base.ImeMode; }
        set { }
    }

    private char[] _allowKeyChars;
    /// <summary>
    /// 数字以外で入力が可能な文字を設定する
    /// </summary>
    public void SetAllowKeyChars(char[] keyChars)
    {
        this._allowKeyChars = keyChars;
    }
    /// <summary>
    /// 数字以外で入力が可能な文字を取得する
    /// </summary>
    public char[] GetAllowKeyChars()
    {
        return this._allowKeyChars;
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        base.OnKeyPress(e);
        //数字以外が入力された時はキャンセルする
        if ((e.KeyChar < '0' || '9' < e.KeyChar) &&
            Array.IndexOf(this._allowKeyChars, e.KeyChar) < 0)
        {
            e.Handled = true;
        }
    }
}
補足:上記NumericTextBoxクラスのSetAllowKeyChars、GetAllowKeyCharsメソッドをプロパティにすると、NumericTextBoxをフォームデザイナでフォームに配置した時、フォームデザイナが開かなくなってしまうというご報告を掲示板にいただきました。ただし、Char型の配列をList<char>に変更すれば、プロパティにしても大丈夫なようです。

NumericUpDownコントロールを使う方法

TextBoxコントロールの代わりに、NumericUpDownコントロールを使うという手も考えられます。NumericUpDownコントロールならば、Valueプロパティから入力値を取得することで、確実に数値を取得できます。

NumericUpDownコントロールでも、IMEを有効にしていると数字以外の文字が入力できますし、コピー&ペーストもできます。しかし数字以外の文字が入力された場合、Valueプロパティで値を取得すると、それ以前に入力されていた数字が返され、NumericUpDownコントロールの表示もその数字に戻ります。

MaskedTextBoxコントロールを使う方法

.NET Framework 2.0以降では、TextBoxコントロールの代わりにMaskedTextBoxコントロールを使う方法も考えられます。

例えばMaskedTextBoxコントロールのMaskプロパティを「99999」とすると、5文字の数字か空白しか入力できなくなります。Maskプロパティの書式については、MSDNの「MaskedTextBox.Mask プロパティ」をご覧ください。

しかしこの方法でも全角の数字が入力できてしまいますし、空白文字が入ることもできます。

MaskedTextBox.ValidatingTypeプロパティを使うと、MaskedTextBoxコントロールに入力された文字列が指定された型に変換できるかの確認を、コントロールがフォーカスを失う時に行うことができます。例えばValidatingTypeプロパティを「Int32」にすると、コントロールがフォーカスを失う時、入力された文字列がInt32.Parseメソッドで変換できるかが確かめられ、その結果をTypeValidationCompletedイベントで知ることができます。

以下の例では、MaskedTextBox1に入力された文字列がInt32型に変換できない時は、メッセージボックスを表示して、MaskedTextBox1からフォーカスが移動しないようにしています。

VB.NET
コードを隠すコードを選択
'Form1のLoadイベントハンドラ
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    '数字と空白しか入力できないようにする
    Me.MaskedTextBox1.Mask = "99999"
    'Int32型に変換できるか検証する
    Me.MaskedTextBox1.ValidatingType = GetType(Integer)
    'TypeValidationCompletedイベントハンドラを追加する
    AddHandler Me.MaskedTextBox1.TypeValidationCompleted, _
        AddressOf MaskedTextBox1_TypeValidationCompleted
End Sub

Private Sub MaskedTextBox1_TypeValidationCompleted( _
        sender As Object, e As TypeValidationEventArgs)
    'Int32型に変換できるか確かめる
    If Not e.IsValidInput Then
        'Int32型への変換に失敗した時は、フォーカスが移動しないようにする
        MessageBox.Show("数値を入力してください")
        e.Cancel = True
    End If
End Sub
C#
コードを隠すコードを選択
//Form1のLoadイベントハンドラ
private void Form1_Load(object sender, EventArgs e)
{
    //数字と空白しか入力できないようにする
    this.MaskedTextBox1.Mask = "99999";
    //Int32型に変換できるか検証する
    this.MaskedTextBox1.ValidatingType = typeof(int);
    //TypeValidationCompletedイベントハンドラを追加する
    this.MaskedTextBox1.TypeValidationCompleted +=
        MaskedTextBox1_TypeValidationCompleted;
}

private void MaskedTextBox1_TypeValidationCompleted(
    object sender, TypeValidationEventArgs e)
{
    //Int32型に変換できるか確かめる
    if (!e.IsValidInput)
    {
        //Int32型への変換に失敗した時は、フォーカスが移動しないようにする
        MessageBox.Show("数値を入力してください");
        e.Cancel = true;
    }
}

CreateParamsをオーバーライドする方法

最後にもう1つ方法を紹介しましょう。これは、ニュースグループで紹介されていた方法です。CreateParams.StyleにES_NUMBERを追加しています。この方法でもやはりコピー&ペーストで文字列以外の文字を入力できてしまいます。

VB.NET
コードを隠すコードを選択
Imports System.Windows.Forms
Imports System.Security.Permissions

''' <summary>
''' 数字しかキー入力できないTextBox
''' </summary>
Public Class NumericTextBox
    Inherits TextBox
    Private ES_NUMBER As Integer = &H2000

    Protected Overrides ReadOnly Property CreateParams() As CreateParams
        <SecurityPermission(SecurityAction.Demand, _
            Flags:=SecurityPermissionFlag.UnmanagedCode)> _
        Get
            Dim parms As CreateParams = MyBase.CreateParams
            parms.Style = parms.Style Or ES_NUMBER
            Return parms
        End Get
    End Property
End Class
C#
コードを隠すコードを選択
using System.Windows.Forms;
using System.Security.Permissions;

/// <summary>
/// 数字しかキー入力できないTextBox
/// </summary>
public class NumericTextBox : TextBox
{
    const int ES_NUMBER = 0x2000;

    protected override CreateParams CreateParams
    {
        [SecurityPermission(SecurityAction.Demand,
            Flags = SecurityPermissionFlag.UnmanagedCode)]
        get
        {
            CreateParams parms = base.CreateParams;
            parms.Style |= ES_NUMBER;
            return parms;
        }
    }
}
  • 履歴:
  • 2006/3/16 Validatingイベントの使用を勧める注意を追加。
  • 2009/6/12 「IMEのオン、クリップボードからの貼り付けを防ぐ」で数字を表す正規表現パターンを変更。
  • 2010/3/29 文字列がクリップボードにあるか調べるときに、クリップボードにデータがあるか確認するようにした。
  • 2010/6/30 WndProcとCreateParamsにSecurityPermissionAttributeを付けた。
  • 2013/5/15 「MaskedTextBoxコントロールを使う方法」を追加(コメントでご提案をいただきました)。「IMEのオン、クリップボードからの貼り付けを防ぐ」のコードを書き直した。
  • 2013/12/9 SecurityAction.LinkDemandの代わりにSecurityAction.Demandを使うようにした。
  • 2014/6/2 NumericTextBoxクラスで、AllowKeyCharsプロパティをメソッドにした。誤字修正など。
  • 2014/8/19 「Imports System.Windows.Forms;」となっていたのを修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。