この記事でわかること
- セルに入力された値を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 のままだとマクロが保存できない。
- 「ファイル」→「名前を付けて保存」
- ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
- 保存
—
手順(コピペ → 動作確認まで約5分)
コードの貼り付け場所: シートモジュール
重要: このコードはシートモジュールに貼り付ける。標準モジュールではない。
- バリデーションを設定したいシートのタブを右クリック
- 「コードの表示」をクリック → VBE(コードを書く画面)が開く
- 表示されたコードウィンドウに、下のコードをそのままコピペする
標準モジュールに貼り付けてもイベントは発火しない。 必ずシートモジュールに貼り付けること。
動作確認
- セルに正しい値(数値)を入力 → そのまま確定されることを確認
- セルに不正な値(文字列)を入力 → 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 で分岐する
- 入力規則と併用するとさらに堅牢
関連記事
- セルの値が変わったら自動実行する方法 — Worksheet_Changeイベントの基本
- 入力規則(ドロップダウンリスト)をVBAで一括設定する方法 — 入力規則との併用
- 入力フォーム(UserForm)で手入力ミスを防ぐ方法 — より本格的な入力画面
- 文字列⇔数値の型変換で計算エラーを防ぐ方法 — IsNumeric, CDate等の型判定
- セルの変更履歴を自動記録して差分管理する方法 — 入力チェック+変更ログの組み合わせ
—
次にやりたくなること
- 入力フォーム(UserForm)で手入力ミスを防ぐ方法: セルへの直接入力ではなく、専用の入力画面でデータ登録を管理したい場合
- 入力規則(ドロップダウンリスト)をVBAで一括設定する方法: 入力値をリスト選択に制限して、そもそも不正な値を入力させたくない場合
- ユーザーフォームで本格的な入力画面を作る方法: セルへの直接入力ではなく専用フォームでデータ登録を管理したい場合


コメント