【VBA】指定したセルに変更があった場合に処理を実行する方法まとめ(シートイベント)

Worksheet_Changeイベントを使うことで、コードを記載したシートのセルが変更されたときに処理を実行することができます。

この処理で実用上ポイントになるのは、動作範囲の指定方法です。

Worksheet_Changeイベントへの動作範囲の指定方法はいくつかありますが、わかりやすいものに絞ってご紹介します。

目次

基本コード

Intersectメソッドを使う方法

このサンプルコードの条件部分は、Intersectメソッドで、変更セル(Target)が、指定範囲内(A1~A10)にあるかどうかを判定しています。

Private Sub Worksheet_Change(ByVal Target As Range)
'A1~A10のセル値が変更されたときに処理を実行

    If Intersect(Target, Range("A1:A10")) Is Nothing Then
       '指定範囲外のセルが変更された場合の処理を必要に応じて記載
    Else
        MsgBox "指定セルに変更がありました。"
    End If

End Sub

“If Intersect(Target, Range(“A1:A10″)) Is Nothing Then”の部分を訳すと、「変更セルがA1~A10にない場合」です。

そのため、指定範囲以外のセルの変更を無視する(何もしない)場合は次のコードでも大丈夫です。

Private Sub Worksheet_Change(ByVal Target As Range)
'A1~A10のセル値が変更されたときに処理を実行

    If Not Intersect(Target, Range("A1:A10")) Is Nothing Then
        MsgBox "指定セルに変更がありました。"
    End If

End Sub

Intersectの前にNotを付けることで「変更セルがA1~A10にある場合」という意味になります。(否定の否定=肯定)

上記は指定範囲内での実行を前提にしていますが、処理内容の記載位置やNotの有無によって、指定範囲内ではなく、逆に指定範囲外をトリガーとしてイベント処理を実行させることも可能です

なお、(変数などの利用を前提に)範囲を数値指定したい場合、Range内にCellsを配置する一般的な方法でOKです。

Private Sub Worksheet_Change(ByVal Target As Range)
'A1~A10のセル値が変更されたときに処理を実行

    If Not Intersect(Target, Range(Cells(1, 1), Cells(10, 1))) Is Nothing Then
        MsgBox "指定セルに変更がありました。"
    End If

End Sub

Intersectメソッドを使わない方法1

Intersectメソッドを使わない方法のひとつに、Taeget変数にセルアドレスを直接指定する方法があります。

コードの記載例は次のとおりですが、セル番地は絶対参照で指定する必要があります。(“$A$1″=○ ”A1″=×

Private Sub Worksheet_Change(ByVal Target As Range)
'A1のセル値が変更されたときに処理を実行

    If Target.Address = "$A$1" Then
        MsgBox "指定セルに変更がありました。"
    End If

End Sub

極めてシンプルに指定できますね。

ですがこの方法には注意したい点があります。

この場合、指定したアドレスとまったく同じ範囲に変更を与えないと動作しません

例えば、Targetに指定したアドレスが”$A$1″であればA1セルを変更すれば反応しますが、コピペやデリートなどでA1セル以外も含めた範囲を一気に変更した場合は反応しません

また、仮にTargetに”$A$1:$B$10″を指定した場合、A1:B10セルすべての範囲に同時に変更を与えないと反応しません

つまり、結合セルの場合は結合範囲を正確に指定する必要がありますし、その結合セル範囲を含んでいたとしても、その他のセルも一緒に変更を与えた場合は動きません。

シンプルで便利な反面、この方法を採用するときは、保護などで他の範囲を選択できないようにしておくなどの対策もしておいたほうがトラブルは少なくなるでしょう。

Intersectメソッドを使わない方法2

上記と同様に、Intersectを使わずに動作範囲を指定する方法ですが、Intersectのように範囲に幅を持たせることもできます。

具体的には、Target(変更セルのアドレス)を行番号と列番号の要素に分けて、両方が指定範囲に収まっている場合だけ処理を実行するというものです。

Private Sub Worksheet_Change(ByVal Target As Range)
'B4~E10のセル値が変更されたときに処理を実行

    If Target.Row >= 4 And Target.Row <= 10 And Target.Column >= 2 And Target.Column <= 5 Then
        MsgBox "指定セルに変更がありました。"
    End If

End Sub

行番号と列番号の指定を分けて次のように記載することもできます。

Private Sub Worksheet_Change(ByVal Target As Range)
'B4~E10のセル値が変更されたときに処理を実行

    If Target.Row >= 4 And Target.Row <= 10 Then '行範囲:4~10
        If Target.Column >= 2 And Target.Column <= 5 Then '列範囲:2(B)~5(E)
            MsgBox "指定セルに変更がありました。"
        End If
    End If

End Sub

少し可読性が上がりますが、どちらもややこしいですね。

なお、これらの方法にも注意点があります。

コピペやデリートなどの範囲変更を行う場合、変更範囲の一番左上のセルがTargetの指定範囲内に入っていないと動作しません

例えば上のコードの動作範囲はB4:E10ですが、仮にA3:F15を選択してデリートキーを押したとしても、A3セルは動作範囲外のため動作しません。

E10:G15を選択してデリートキーを押した場合は、E10セルが動作範囲にぎりぎり入っているので動作します。

Intersectを使う方法と比較して、上記のように動作条件にクセがあることに加え、コードが特別短いわけではなく、可読性も良いとは言えないので、特殊な状況を除いてこの方法を使用するメリットはあまりなさそうです。

使用例

カーソルの自動遷移

上のような順番に入力していくシートがあったとします。

入力順1のボックス(B3セル)に変更があった場合に、入力順2のボックス(D3セル)にカーソルを自動的に移動し、同様に入力順2のボックス(D3セル)に変更があった場合に、入力順3のボックス(F3セル)にカーソルを自動的に移動するという簡単なコードを2通り紹介します。

Intersectメソッドを使う場合

Private Sub Worksheet_Change(ByVal Target As Range)

    '入力順1の変更時
    If Not Intersect(Target, Range("B3")) Is Nothing Then
        Range("D3").Select '入力順2に移動
    End If
    
    '入力順2の変更時
    If Not Intersect(Target, Range("D3")) Is Nothing Then
        Range("F3").Select '入力順3に移動
    End If
    
End Sub

このように指定範囲ごとの処理ごとのコードを単純に追記していく形で大丈夫です。

Targetにアドレス指定する場合

Private Sub Worksheet_Change(ByVal Target As Range)

    '入力順1・2の変更を条件分岐
    If Target.Address = "$B$3" Then
        Range("D3").Select '入力順2に移動
    ElseIf Target.Address = "$D$3" Then
        Range("E3").Select '入力順3に移動
    End If

End Sub

シンプルですね。

入力セル以外の選択や変更がシート保護などで制限されているようなケースでは、短く書けるこのようなコードにも実用性があると言えます。

動作範囲外での処理例

例えば、下図のように入力範囲外に間違って入力した場合に、その入力をクリアして、入力欄にクリアした値を自動的に入れるサンプルコードを記載します。

Intersectメソッドを使う場合

Private Sub Worksheet_Change(ByVal Target As Range)
    
    If Intersect(Target, Range("B2:B10")) Is Nothing Then
        
        '入力値の記録
        Dim Str As String
        Str = Target
    
        '範囲外の入力をクリア
        Application.EnableEvents = False 'ループ回避のためイベントを一旦停止
            Target.ClearContents
        Application.EnableEvents = True 'イベントを再開
        
        '入力欄の最終行調査
        Dim Er As Long
        Er = Cells(1, "B").End(xlDown).Row
        
        '入力欄への追記とカーソル移動
        Cells(Er + 1, "B") = Str '最初に記録した入力値を追記
        Cells(Er + 1, "B").Select
        
    End If
   
End Sub

本題の動作条件の解説から逸れている気もしますし、実務的にはこんな使い方をすることはあまり考えられませんが、参考程度に見ていただければ幸いです。

Targetに動作条件を直接指定する場合

Private Sub Worksheet_Change(ByVal Target As Range)
    
    If Not (Target.Row >= 2 And Target.Row <= 10 And Target.Column >= 2 And Target.Column <= 2) Then
    
        '入力値の記録
        Dim Str As String
        Str = Target
    
        '範囲外の入力をクリア
        Application.EnableEvents = False 'ループ回避のためイベントを一旦停止
            Target.ClearContents
        Application.EnableEvents = True 'イベントを再開
        
        '入力欄の最終行調査
        Dim Er As Long
        Er = Cells(1, "B").End(xlDown).Row
        
        '入力欄への追記とカーソル移動
        Cells(Er + 1, "B") = Str '最初に記録した入力値を追記
        Cells(Er + 1, "B").Select
        
    End If
   
End Sub

動作条件の指定方法以外はIntersectの例と同じです。

まとめ

セルの変更をトリガーにしてイベント処理を実行する方法として、Intersectメソッドを使う方法と使わない方法の2通りを紹介しました。

解説しておいて何ですが、このChangeイベントの動作範囲指定に関しては、Intersectを使う方法だけ覚えておけば困ることはないと思っています。

本文中でも触れましたが、動作の確実性はIntersectを使って指定するほうが明らかに高いからです。

とはいえ、この手の意見に正解はなく、コードを短くできる、可読性が良い、などそれぞれの状況と好みで選べば良いと思います。

複数の指定方法と特徴を比べた上で、シーンに適した方法を選ぶ参考になれば幸いです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次