「特定のセルに入力したら自動で日付を記録したい」「入力値が条件を満たさなければ警告を出したい」——こうした処理は、セルの変更をリアルタイムに検知できるWorksheet_Changeイベントを使うと簡単に実現できます。この記事では、Worksheet_Changeイベントの基本的な書き方から、複数セルの変更に対応する方法、そして無限ループを防ぐための注意点まで、実務で使えるコード例とともに解説します。
Worksheet_Changeイベントとは
Worksheet_Changeイベントは、シート上のセルの値が変更されたタイミングで自動的に実行されるイベントプロシージャです。ボタンをクリックしたりマクロを手動実行したりする必要がなく、ユーザーが手入力・削除・貼り付けなどでセルの値を変えた瞬間に処理が走ります。
このイベントは標準モジュールではなく、対象シートの「シートモジュール」に記述する点が特徴です。VBEのプロジェクトエクスプローラーで対象シート(例:Sheet1)をダブルクリックし、右上のイベント一覧からWorksheetとChangeを選択すると、以下のひな型が自動生成されます。
Private Sub Worksheet_Change(ByVal Target As Range)
End Sub
Target引数には、変更されたセル範囲がRangeオブジェクトとして渡されます。このTargetを使って「どのセルが変更されたか」を判定するのが基本の流れです。
特定のセルが変更されたときだけ処理する
すべてのセル変更に反応してしまうと、意図しない場所の入力でも処理が走ってしまいます。監視したいセルをIntersectメソッドで絞り込むのが定番のテクニックです。
Private Sub Worksheet_Change(ByVal Target As Range)
' A列(A2:A100)が変更されたときだけ処理する
If Not Intersect(Target, Me.Range("A2:A100")) Is Nothing Then
MsgBox "A列のセルが変更されました:" & Target.Address
End If
End Sub
Intersect(Target, 監視範囲)は、変更されたセルと監視範囲が重なっている部分を返します。重なりがない場合はNothingが返るため、If Not ... Is Nothing Thenという書き方で「監視範囲内の変更かどうか」を判定できます。複数セルをまとめて貼り付けた場合でも、Targetには変更された全セルが含まれるため、この判定方法であれば正しく検知できます。
実践例:入力した瞬間に更新日時を自動記録する
Worksheet_Changeイベントの代表的な活用例が、入力があったセルの隣に自動でタイムスタンプを記録する処理です。
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cell As Range
If Intersect(Target, Me.Range("B2:B100")) Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each cell In Intersect(Target, Me.Range("B2:B100"))
If cell.Value <> "" Then
cell.Offset(0, 1).Value = Now ' C列に更新日時を記録
Else
cell.Offset(0, 1).Value = ""
End If
Next cell
Application.EnableEvents = True
End Sub
ここで重要なのがApplication.EnableEvents = Falseです。この処理の中でcell.Offset(0, 1).Value = Nowのようにセルへ値を書き込むと、その書き込み自体が新たなWorksheet_Changeイベントを発生させてしまい、無限ループやエラーの原因になります。イベントを一時的に無効化し、処理が終わったら必ずTrueに戻すことで、この問題を防いでいます。
実践例:入力値が条件外なら自動で元に戻す
入力規則だけでは対応しきれない複雑な条件チェックにも、Worksheet_Changeイベントは有効です。以下は、C列に0〜100以外の数値が入力された場合に警告を出し、入力前の状態に戻す例です。
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Me.Range("C2:C100")) Is Nothing Then Exit Sub
If Target.Cells.Count > 1 Then Exit Sub
Dim isValid As Boolean
isValid = True
If Not IsNumeric(Target.Value) Then
isValid = False
ElseIf Target.Value < 0 Or Target.Value > 100 Then
isValid = False
End If
If Not isValid Then
Application.EnableEvents = False
MsgBox "0〜100の範囲で数値を入力してください。"
Application.Undo
Application.EnableEvents = True
End If
End Sub
Target.Cells.Count > 1のチェックを入れているのは、複数セルへの一括貼り付け時に処理が複雑化するのを避けるためです。単一セルの入力のみを対象とすることで、Application.Undoによる差し戻しも安全に行えます。なお、IsNumeric(Target.Value) Or Target.Value < 0のように1行の条件式にまとめてしまうと、VBAはOrの左右を両方評価するため、数値以外の文字列に対してTarget.Value < 0を評価しようとしてエラーになることがあります。そのためElseIfで条件を分け、数値と判定できた場合だけ範囲チェックを行うようにしています。
注意点:EnableEventsを戻し忘れるとイベントが動かなくなる
Application.EnableEvents = Falseのあとに実行時エラーが発生すると、Application.EnableEvents = Trueの行が実行されずに処理が終了してしまうことがあります。この状態になると、以降すべてのWorksheet_Changeイベントが反応しなくなり、原因が分かりにくいトラブルにつながります。エラーが起きる可能性がある処理では、On Errorと組み合わせて必ずイベントを復帰させる、あるいは処理の最後にApplication.EnableEvents = Trueをまとめて書いておくといった対策をおすすめします。
まとめ
Worksheet_Changeイベントを使うと、セルの変更をきっかけに自動処理を走らせる仕組みを簡単に作れます。ポイントは、Intersectメソッドで監視対象のセルを絞り込むこと、そして処理内でセルを書き換える際はApplication.EnableEventsで無限ループを防ぐことです。この2点を押さえておけば、更新日時の自動記録や入力チェックなど、実務でよく使う自動化処理に安心して応用できます。


コメント