【ExcelVBA・マクロ】Worksheet_Changeイベントの使い方|セル変更を検知して自動処理するマクロの作り方【コピペOK】

ExcelVBA

「特定のセルに入力したら自動で日付を記録したい」「入力値が条件を満たさなければ警告を出したい」——こうした処理は、セルの変更をリアルタイムに検知できるWorksheet_Changeイベントを使うと簡単に実現できます。この記事では、Worksheet_Changeイベントの基本的な書き方から、複数セルの変更に対応する方法、そして無限ループを防ぐための注意点まで、実務で使えるコード例とともに解説します。

スポンサーリンク
スポンサーリンク

Worksheet_Changeイベントとは

Worksheet_Changeイベントは、シート上のセルの値が変更されたタイミングで自動的に実行されるイベントプロシージャです。ボタンをクリックしたりマクロを手動実行したりする必要がなく、ユーザーが手入力・削除・貼り付けなどでセルの値を変えた瞬間に処理が走ります。

このイベントは標準モジュールではなく、対象シートの「シートモジュール」に記述する点が特徴です。VBEのプロジェクトエクスプローラーで対象シート(例:Sheet1)をダブルクリックし、右上のイベント一覧からWorksheetChangeを選択すると、以下のひな型が自動生成されます。

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点を押さえておけば、更新日時の自動記録や入力チェックなど、実務でよく使う自動化処理に安心して応用できます。

スポンサーリンク
スポンサーリンク
ExcelVBA
シェアする
いがぴをフォローする

コメント

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