VBAでワークシートにイベントプロシージャを動的に設定する2つの方法

みなさん,こんにちは。
シンノユウキ(shinno1993)です。

最近はかなりレガシーなVBAを読んでいるせいか,新しい発見が多くあり,それをアウトプットしたくて仕方ない状況になっています.ので,それをちょっとずつアウトプットしていきます.

今回は,VBAでワークシートにイベントプロシージャを動的に設定する2つの方法紹介します.

では行きましょう!

イベントプロシージャとは?

はじめに,イベントプロシージャについて紹介します.

イベントプロシージャというのは,オブジェクトに何らかのイベントが発生した際に実行されるプロシージャのことです.たとえば,ワークシートの値が変更された際などに起こるChangeイベント,この際に実行されるのがイベントプロシージャです.

具体的に,たとえば以下のコードは,ワークシートに変化が起こった際に,そのセルのアドレスをプリントするイベントプロシージャです.

Private Sub Worksheet_Change(ByVal Target As Range)
    Debug.Print Target.Address
End Sub

このようなイベントプロシージャは通常,コードを書いて生成するため,後から動的に生成したワークシートにはイベントプロシージャが設定されていません.今回はそれを設定するための方法を紹介します.

方法① シートにコードを追加する

最も簡単なのは,作成したシートにそっくりコードを挿入してしまうことです.これにはCodeModuleオブジェクトのInsertLinesメソッドを活用します.それぞれのCodeModuleオブジェクトは,VBComponentに含まれます.InsertLinesメソッドは,以下のように使用します:

Workbook.VBProject.VBComponents.Item("シート名").CodeModule.InsertLines 挿入する行, 挿入するコード

ちなみに,挿入する行のデータ型は,麗しのIntegerです.Longだと駄目です.昨今のVBAではIntegerを使用するのは却って効率を下げるという話があります.ので,大体はLongを使うのですが,今回はIntegerを使わなければなりません.Integerを使用する必然性が生まれて嬉しいです.

これを,上で示した3行のイベントプロシージャを挿入するために使ってみましょう.以下のようになります:

Sub insertCode()
    Dim code(3) As String
    Dim i As Integer
    
    Const sheetname As String = "Sheet1"
    
    code(1) = "Private Sub Worksheet_Change(ByVal Target As Range)"
    code(2) = "Debug.print Target.Address"
    code(3) = "End Sub"
    
    With ThisWorkbook.VBProject.VBComponents.Item(sheetname).CodeModule
        .InsertLines 1, code(1)
        .InsertLines 2, code(2)
        .InsertLines 3, code(3)
    End With
End Sub

せっかくなのでFor Nextステートメントを使うと,InsertLineのところは以下のような感じになります:

    With ThisWorkbook.VBProject.VBComponents.Item(sheetname).CodeModule
        For i = 1 To 3
            .InsertLines i, code(i)
        Next
    End With

また,シートからコードを削除するためには,DeleteLinesメソッドを使用します.以下のような感じになります:

Sub deleteCode()
    Dim i As Integer
    Const sheetname As String = "sheet1"
    With ThisWorkbook.VBProject.VBComponents.Item(sheetname).CodeModule
        .DeleteLines 1, 3
    End With
End Sub

DeleteLinesの1つ目の引数は削除の開始行です.そして2つ目の引数は削除する行数を示しています.つまり,今回の場合だと1行目から3行を削除するということになります.これで,先ほど挿入したコードは削除できます.

方法② On***プロパティを使う

2つ目の紹介する方法はWorksheetオブジェクトのOn***プロパティです.この方法,少しネットで調べてみてもどうも情報が少ない.あまり推奨されていない方法かも?という一抹の不安をいだきながらですが,取り急ぎ紹介させていただきます.

これに該当するプロパティは多くあります(詳しくはこちらの番外1-1にまとめられています).Worksheetのプロパティに設定することで,シートでのイベントを拾えるようになります.たとえば,OnEntryプロパティでは,セルにデータを入力(Entry)した際に動くプロシージャを設定できます.以下の様に使用します:

Worksheet.OnEntry = "プロシージャ名"

こうすることで,セルにデータを入力した際,指定したプロシージャを動かすことができます.

具体的なサンプルコードを上げますと,たとえば,Sheet1にデータが入力された際,そのセルのアドレスを知りたい場合,以下のようなコードとなります:

Sub addEventProc()
    ThisWorkbook.Worksheets("Sheet1").OnEntry = "logEntryAddress"
End Sub

Private Sub logEntryAddress()
    Debug.Print ActiveCell.Address
End Sub

Sheet1にデータが入力された際,logEntryAddressというプロシージャが実行されるようになります.

もし,これを取り消したい場合,以下のようにOnEntryプロパティを空にします:

Sub deleteEventProc()
    ThisWorkbook.Worksheets("Sheet1").OnEntry = ""
End Sub

まとめ

今回はVBAでワークシートにイベントプロシージャを動的に設定する2つ方法を紹介しました.そのどちらも,トリッキーな方法で,積極的に活用する場面は少ないかと思いますが,参考になれば!

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