【VBA】セルの入力値をチェック・制限する方法(数値のみ・日付のみ等)

VBA
スポンサーリンク
スポンサーリンク
  1. この記事でわかること
  2. どんな場面で使う?
  3. 完成イメージ(Before / After)
  4. 実行前の準備
    1. バックアップを取る
    2. Excelをマクロ有効ブック(.xlsm)で保存する
  5. 手順(コピペ → 動作確認まで約5分)
    1. コードの貼り付け場所: シートモジュール
    2. 動作確認
  6. コード(最小版)– 数値のみ入力を許可する基本チェック
    1. 書き換えポイント
  7. コード(実務版)– 複数列の複合バリデーション(数値・日付・文字列長)
    1. 書き換えポイント
    2. シート構成の例
  8. よくある落とし穴5選
    1. 1. EnableEventsをFalseにし忘れてExcelがフリーズする
    2. 2. コードを標準モジュールに貼り付けてしまい、何も起きない
    3. 3. 複数セルの貼り付けでエラーが出る
    4. 4. IsNumericが空文字列をTrueと返してしまう
    5. 5. EnableEventsがFalseのまま戻らなくなった
    6. VBAのWorksheet_Changeでフリーズするときの対処法
    7. VBAのバリデーションで複数セル貼り付け時にエラーになるときの対処法
  9. FAQ
    1. Q1: 入力規則(Validation)とWorksheet_Changeバリデーション、どちらを使うべき?
    2. Q2: 入力チェックに引っかかったとき、セルを元の値に戻す方法はある?
    3. Q3: 特定のシートだけにバリデーションを効かせるには?
    4. Q4: EnableEventsがFalseのまま戻らなくなったらどうする?
    5. Q5: UserFormの入力チェックとWorksheet_Changeバリデーション、どう使い分ける?
  10. まとめ
    1. 関連記事
  11. 次にやりたくなること

この記事でわかること

  • セルに入力された値をVBAでリアルタイムにチェックし、不正なら即座に警告・クリアできる
  • 数値のみ・日付のみ・文字数制限など、列ごとに異なるバリデーションを設定できる
  • EnableEventsの無限ループを防ぐ安全なコードの書き方がわかる

対象: VBAを少し触ったことがある人。入力規則だけでは足りないと感じている人。

所要時間: コピペ → 動作確認まで約5分

どんな場面で使う?

  • 数値しか入力してほしくない列にテキストが入力されるのを防ぎたい
  • 日付列に不正な値が入ったらリアルタイムで警告を出したい
  • Worksheet_Changeイベントで入力直後にバリデーションを走らせたい
  • 入力規則(Validation)とVBAバリデーションの使い分けを知りたい—

完成イメージ(Before / After)

Before(チェックなし):

  • A列「検査値」に “OK” や “済” などの文字列が混入
  • 月末の集計マクロが「型が一致しません」エラーで停止
  • 手作業で不正データを探して修正 → 再実行

After(バリデーションあり):

  • A列に数値以外を入力 → 即座にMsgBox警告 → セルがクリアされる
  • B列に日付以外を入力 → 警告 → クリア
  • C列に11文字以上を入力 → 警告 → クリア
  • 正しい値ならそのまま確定。後工程のエラーがなくなる

自分も以前、品質管理の一覧表で「検査値」の列に”済”や”OK”と文字を入れる人がいて、月末の集計マクロがエラーで止まるのが地味にストレスだった。Worksheet_Changeで入力チェックを入れてからは、不正データが混入しなくなり、月末の修正作業がゼロになった。同じように「入力ミスのせいで後工程が壊れる」経験がある人が、この記事でサクッとバリデーションを組めるようになればうれしい。

Worksheet_Changeイベントを使えば、セルへの入力と同時にバリデーションが走る。入力規則では対応できない複雑な条件にも柔軟に対応できる。

Worksheet_Changeイベントの基本的な使い方は セルの値が変わったら自動実行する方法 を参照。この記事ではバリデーション(入力チェック)に特化して解説する。

実行前の準備

バックアップを取る

このマクロはセルの値をクリアする操作を含む。必ずファイルのコピーを別フォルダに保存してから実行する。

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロが保存できない。

  1. 「ファイル」→「名前を付けて保存」
  2. ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  3. 保存

手順(コピペ → 動作確認まで約5分)

コードの貼り付け場所: シートモジュール

重要: このコードはシートモジュールに貼り付ける。標準モジュールではない。

  1. バリデーションを設定したいシートのタブを右クリック
  2. 「コードの表示」をクリック → VBE(コードを書く画面)が開く
  3. 表示されたコードウィンドウに、下のコードをそのままコピペする

標準モジュールに貼り付けてもイベントは発火しない。 必ずシートモジュールに貼り付けること。

動作確認

  1. セルに正しい値(数値)を入力 → そのまま確定されることを確認
  2. セルに不正な値(文字列)を入力 → MsgBox警告が出てセルがクリアされることを確認

コード(最小版)– 数値のみ入力を許可する基本チェック

A列に数値以外の値が入力されたら、MsgBoxで警告してセルをクリアする。まずはこれで動きを確認する。


'============================================================
' ■ A列の数値バリデーション(最小版)
'   → A列に数値以外が入力されたら警告してクリア
'   → 貼り付け場所: シートモジュール
'============================================================
Private Sub Worksheet_Change(ByVal Target As Range)

    '--- 対象列(A列=1)以外は無視
    If Target.Column <> 1 Then Exit Sub

    '--- 複数セル同時変更(貼り付け等)は無視
    If Target.Count > 1 Then Exit Sub

    '--- 空セルは無視(削除操作を許可)
    If Target.Value = "" Then Exit Sub

    '--- 数値チェック
    If Not IsNumeric(Target.Value) Then
        On Error GoTo ErrHandler

        '--- イベントの無限ループを防止
        Application.EnableEvents = False

        MsgBox "A列には数値のみ入力できます。" & vbCrLf & _
               "入力値: " & Target.Value, vbExclamation, "入力エラー"

        Target.ClearContents  '← セルをクリア
        Target.Select         '← カーソルを戻す

        Application.EnableEvents = True
    End If

    Exit Sub

ErrHandler:
    '--- エラーが起きてもEnableEventsを確実にTrueに戻す
    Application.EnableEvents = True

End Sub

書き換えポイント

変数・定数 説明 初期値
Target.Column <> 1 チェック対象の列番号 1(A列)
IsNumeric(Target.Value) チェック条件 数値判定
MsgBoxのメッセージ エラー表示文 用途に合わせて変更

ポイント: Application.EnableEvents = False を入れないと、Target.ClearContents でセル値が変わったときに再びWorksheet_Changeが発火し、無限ループになる。

型変換やIsNumericの詳細は 文字列⇔数値の型変換で計算エラーを防ぐ方法 を参照。

コード(実務版)– 複数列の複合バリデーション(数値・日付・文字列長)

この仕組みを入れてからは、入力者から「間違って入れたらすぐ教えてくれるから助かる」と言われるようになった。入力ミスの問い合わせ対応も激減して、もっと早く入れておけばよかったと思った。

A列(数値のみ)、B列(日付のみ)、C列(10文字以内)の3列に、それぞれ異なるバリデーションを設定する。

※ 不正な入力値はクリアされます。実行前にバックアップを取ってください。


'============================================================
' ■ 複数列の複合バリデーション(実務版)
'   → A列: 数値のみ
'   → B列: 日付のみ(2020/1/1〜2030/12/31)
'   → C列: 10文字以内
'   → 貼り付け場所: シートモジュール
'============================================================
Private Sub Worksheet_Change(ByVal Target As Range)

    '--- ★書き換えポイント: チェック対象の列番号 ---
    Const COL_NUMBER As Long = 1   '← A列: 数値チェック
    Const COL_DATE As Long = 2     '← B列: 日付チェック
    Const COL_TEXT As Long = 3     '← C列: 文字数チェック
    Const MAX_TEXT_LEN As Long = 10 '← C列の最大文字数
    '--- ★ここまで ---

    '--- ヘッダー行(1行目)は無視
    If Target.Row = 1 Then Exit Sub

    '--- 複数セル同時変更(貼り付け等)は無視
    If Target.Count > 1 Then Exit Sub

    '--- 空セルは無視(削除操作を許可)
    If Target.Value = "" Then Exit Sub

    '--- 対象列以外は無視
    If Target.Column <> COL_NUMBER And _
       Target.Column <> COL_DATE And _
       Target.Column <> COL_TEXT Then
        Exit Sub
    End If

    Dim errMsg As String
    errMsg = ""

    '--- 列ごとのバリデーション
    Select Case Target.Column

        Case COL_NUMBER  '--- A列: 数値チェック
            If Not IsNumeric(Target.Value) Then
                errMsg = "A列には数値のみ入力できます。"
            End If

        Case COL_DATE  '--- B列: 日付チェック
            If Not IsDate(Target.Value) Then
                errMsg = "B列には日付を入力してください。" & vbCrLf & _
                         "例: 2026/04/01"
            Else
                '--- 日付の範囲チェック(追加)
                Dim inputDate As Date
                inputDate = CDate(Target.Value)
                If inputDate < DateSerial(2020, 1, 1) Or _
                   inputDate > DateSerial(2030, 12, 31) Then
                    errMsg = "B列の日付は 2020/1/1〜2030/12/31 の範囲で入力してください。"
                End If
            End If

        Case COL_TEXT  '--- C列: 文字数チェック
            If Len(Target.Value) > MAX_TEXT_LEN Then
                errMsg = "C列は" & MAX_TEXT_LEN & "文字以内で入力してください。" & vbCrLf & _
                         "現在: " & Len(Target.Value) & "文字"
            End If

    End Select

    '--- エラーがある場合のみ処理
    If errMsg <> "" Then
        On Error GoTo ErrHandler

        Application.EnableEvents = False

        MsgBox errMsg & vbCrLf & vbCrLf & _
               "入力値: " & Target.Value, vbExclamation, "入力エラー"

        Target.ClearContents
        Target.Select

        Application.EnableEvents = True
        Exit Sub
    End If

    Exit Sub

ErrHandler:
    '--- エラーが起きてもEnableEventsを確実にTrueに戻す
    Application.EnableEvents = True
    MsgBox "入力チェック中にエラーが発生しました。" & vbCrLf & _
           "エラー内容: " & Err.Description, vbCritical, "システムエラー"

End Sub

書き換えポイント

定数 説明 初期値
COL_NUMBER 数値チェック対象の列 1(A列)
COL_DATE 日付チェック対象の列 2(B列)
COL_TEXT 文字数チェック対象の列 3(C列)
MAX_TEXT_LEN 最大文字数 10
DateSerial(2020, 1, 1)DateSerial(2030, 12, 31) 許可する日付の範囲 業務に合わせて変更

シート構成の例

A列(検査値)数値のみ B列(検査日)日付のみ C列(備考)10文字以内
98.5 2026/04/01 異常なし
← “OK” を入力すると警告・クリア ← “明日” を入力すると警告・クリア ← 11文字以上を入力すると警告・クリア

入力規則(ドロップダウンリスト等)と併用すると、さらに堅牢な入力制限になる。入力規則の設定方法は 入力規則(ドロップダウンリスト)をVBAで一括設定する方法 を参照。

よくある落とし穴5選

1. EnableEventsをFalseにし忘れてExcelがフリーズする

自分もこれで痛い目にあった。Worksheet_Change内でセルをクリアしたら、そのクリア操作でまたWorksheet_Changeが発火して無限ループ。Excelが固まってタスクマネージャーで強制終了する羽目になった。保存前のデータが飛んだときは正直泣きそうだった。

原因: Worksheet_Change内でセルの値を変更すると、再びWorksheet_Changeが発火する。

対策: セルの値を変更する前に Application.EnableEvents = False を設定し、変更後に True に戻す。


Application.EnableEvents = False  '← イベントを一時停止
Target.ClearContents               '← これでイベントが再発火しない
Application.EnableEvents = True   '← イベントを再開

EnableEventsの詳しい解説は 画面更新・再計算を止めてマクロを高速化する方法 も参考になる。

2. コードを標準モジュールに貼り付けてしまい、何も起きない

原因: Worksheet_Changeはシートモジュールのイベントプロシージャ。標準モジュール(Module1等)に書いても発火しない。

対策: シートタブを右クリック →「コードの表示」でシートモジュールを開き、そこに貼り付ける。

3. 複数セルの貼り付けでエラーが出る

原因: 複数セルをコピー&ペーストすると、Targetが複数セルの範囲になる。Target.Value を直接 IsNumeric() に渡すと配列になり、意図通りに動かない。

対策: If Target.Count > 1 Then Exit Sub で複数セル変更を無視する。複数セルにも対応させたい場合は For Each でセルごとに処理する。

4. IsNumericが空文字列をTrueと返してしまう

原因: VBAの IsNumeric("")True を返す仕様。空セルが「正常な数値」として通過してしまう。

対策: 空文字列チェックを先に行う。


If Target.Value = "" Then Exit Sub  '← 空セルは無視

5. EnableEventsがFalseのまま戻らなくなった

原因: バリデーション処理中にエラーが発生し、EnableEvents = True に到達しないまま処理が終了した。以降、セルを変更してもイベントが一切動かなくなる。

対策: On Error GoTo ErrHandler でエラーハンドラを設け、エラー時も確実に EnableEvents = True に戻す。


On Error GoTo ErrHandler
Application.EnableEvents = False
' ... 処理 ...
Application.EnableEvents = True
Exit Sub

ErrHandler:
    Application.EnableEvents = True

もしEnableEventsがFalseのまま戻らなくなったら、VBEのイミディエイトウィンドウ(Ctrl+G)で以下を入力してEnterキーを押す。


Application.EnableEvents = True

VBAのWorksheet_Changeでフリーズするときの対処法

「Worksheet_Changeイベント内でセルに値を書き込んだらExcelがフリーズした」という場合、原因はChangeイベント内の値書き込みが再びChangeイベントを発火させ、無限ループになっていることだ。イベント内で値を書き込む前に Application.EnableEvents = False を設定し、書き込み後に True に戻すこと。

VBAのバリデーションで複数セル貼り付け時にエラーになるときの対処法

「Worksheet_Changeで1セルずつチェックしていたら複数セルのコピペでエラーが出る」という場合、原因は Target が複数セルの Range になっていることだ。For Each cell In Target でTargetの各セルをループ処理するか、If Target.Count > 1 Then Exit Sub で複数セル変更時はスキップすること。

FAQ

Q1: 入力規則(Validation)とWorksheet_Changeバリデーション、どちらを使うべき?

比較 入力規則 Worksheet_Change
設定の手軽さ マクロ不要で設定可能 VBAの知識が必要
柔軟性 定型的な制限のみ 複雑な条件も自由に書ける
複数列の個別ルール 列ごとに設定が必要 1つのイベントでまとめて処理
回避されるリスク VBAやマクロで上書き可能 同様に回避可能

結論: 単純な制限(数値範囲やリスト選択)は入力規則で十分。列ごとに異なる複雑な条件や、他のセル値との相関チェックが必要ならWorksheet_Change。両方を併用するのがベスト。

入力規則の設定方法は 入力規則(ドロップダウンリスト)をVBAで一括設定する方法 を参照。

Q2: 入力チェックに引っかかったとき、セルを元の値に戻す方法はある?

Application.Undo を使えば直前の操作を元に戻せる。ただし EnableEvents = False の状態で実行し、Undoの直後に EnableEvents = True に戻すこと。


Application.EnableEvents = False
Application.Undo  '← 入力前の値に戻る
Application.EnableEvents = True

実務的にはセルをクリアしてMsgBoxで再入力を促す方がシンプルで確実。

Q3: 特定のシートだけにバリデーションを効かせるには?

コードを該当シートのシートモジュールに書けば、そのシートだけで動作する。複数シートに同じチェックを適用したい場合は、ThisWorkbookモジュールの Workbook_SheetChange イベントを使う方法もある。

Q4: EnableEventsがFalseのまま戻らなくなったらどうする?

VBEを開き(Alt+F11)、イミディエイトウィンドウ(Ctrl+G)に以下を入力してEnterキーを押す。


Application.EnableEvents = True

これで復帰する。予防策として、On Error処理で確実にTrueに戻すコードを入れておく。

Q5: UserFormの入力チェックとWorksheet_Changeバリデーション、どう使い分ける?

比較 Worksheet_Change UserForm
入力方法 セルに直接入力 フォーム画面から入力
操作感 Excelの通常操作と同じ 専用画面で案内付き入力
チェックのタイミング セル変更後 登録ボタン押下時
適用場面 既存の一覧表にチェックを追加 新規データの登録フォーム

既存のシートにバリデーションを追加するならWorksheet_Change。新規入力の画面を作るならUserFormが向いている。UserFormの作り方は 入力フォーム(UserForm)で手入力ミスを防ぐ方法 を参照。

まとめ

  • Worksheet_Changeイベントでセルへの入力をリアルタイムにチェックできる
  • EnableEvents = False/True を必ず入れて無限ループを防止する
  • On Error でエラー時もEnableEventsを確実にTrueに戻す
  • 列ごとに異なるチェック(数値・日付・文字数)を Select Case で分岐する
  • 入力規則と併用するとさらに堅牢

関連記事

次にやりたくなること

コメント

タイトルとURLをコピーしました