ToDo完了後に毎回リスト更新、メール送信――VBAでその手順、削減しませんか?働く価値を上げる“VBA/GAS”術(13)(3/4 ページ)

» 2019年01月07日 05時00分 公開
[高橋宣成プランノーツ]

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

 VBAの一部のオブジェクトには、固有の「イベント」が定義されています。例えば、「Workbook」オブジェクトであれば「ワークブックが開いた後」や「閉じる前」、「Worksheetk」オブジェクトであれば「ワークシートがアクティブになった後」や「削除される前」などといったタイミングがイベントとして定義されています。

 これらのイベントをきっかけに動作するプロシージャをイベントプロシージャと呼びます。例として幾つかのイベントプロシージャを以下にリストアップしています。

動作 イベントプロシージャ
ワークブックが開いた後 Workbook_Open
ワークブックが閉じる前 Workbook_BeforeClose
ワークシートが削除される前 Worksheet_BeforeDelete
ワークシートがアクティブになった後 Worksheet_Activate
ワークシートが変更された後 Worksheet_Change

 イベントプロシージャは標準モジュールではなく、イベントの対象となるオブジェクトモジュールに記述します。Workbookオブジェクトのイベントプロシージャであれば「ブックモジュール」に、Worksheetオブジェクトのイベントプロシージャであれば「シートモジュール」に記述します。

WorksheetオブジェクトのChangeイベント

 今回のToDo完了メール送信マクロでは、「ワークシートが変更された後」のイベントプロシージャを作成するので、シートモジュールに記述します。

 VBE(Visual Basic Editor)のプロジェクトエクスプローラー内の「Sheet1(ToDo)」が対象のシートモジュールになるので、ダブルクリックして開きます。

図3 プロジェクトエクスプローラーからシートモジュールを開く

 コードウィンドウの上部に2つのプルダウンボックスが並んでいます。左側が「オブジェクトボックス」、右側が「プロシージャボックス」と言います。イベントプロシージャは、それぞれオブジェクトとプロシージャを選択することで、簡単に作成できます。

 オブジェクトボックスから「Worksheet」、プロシージャボックスから「Change」を選択して、イベントプロシージャ「Worksheet_Change(ByVal Target As Range)」が挿入されることを確認しましょう。

図4 オブジェクトボックスとプロシージャボックス

 「Target」というRange型の引数が与えられています。これは変更があったセル範囲が格納されます。イベントプロシージャ内で活用して、さまざまな処理を実現できます。

ListColumnオブジェクトのデータ範囲を取得する

 まず、以下の部分に注目しましょう。

Set myRange = Sheet1.ListObjects(1).ListColumns("ステータス").DataBodyRange

 ToDoリストはテーブル化されているので、「Sheet1.ListObjects(1)」にすることで、「ListObject」オブジェクトとして取得できます。続く、「ListColumns」プロパティで、「ListColumns」コレクションを取得します。

ListObjectオブジェクト.ListColumns

 また「カラム名」を指定することで、コレクションから特定の「ListColumn」オブジェクトを取得しています。

ListColumnsコレクション(カラム名)

 ListColumnは、テーブルの単体の列を表すオブジェクトで、カラム名はその見出しになります。従って、カラム名「ステータス」を指定して取り出したListColumnオブジェクトは、テーブル内のステータス列を表します。

 さらに、「DataBodyRange」プロパティを使用しています。これはListColumnオブジェクト内のデータ範囲のみを「Range」オブジェクトで取得するプロパティです。

ListColumnオブジェクト.DataBodyRange

 つまり、今回のToDoリストの見出しを除いて「E2:E4」のセル範囲を取得できます。

セル範囲にセルが含まれるか判定する

 次に以下の部分を見ていきましょう。

Dim r As Range
For Each r In Target
    If Not Intersect(myRange, r) Is Nothing And r.Value = "完了" Then
        Call ToDo完了メール送信(r.row)
    End If
Next r

 「For Each」ステートメントで、変更のあったセル範囲であるTarget内の個々のセルについて繰り返しを行います。その内部にあるIf文ですが、以下2点の判定の「And」をとっています。

  • 現在対象となっているセルがmyRange、つまり「ステータス」列のデータ範囲内かどうか
  • その値が「完了」かどうか

 「Intersect」メソッドは「Application」オブジェクトのメンバーです。このメソッドは、引数で指定した複数の範囲の重なっている部分のセル範囲を返すメソッドです。

Intersect(セル範囲1, セル範囲2,...)

 第1引数は「ステータス列のデータ範囲」を表す「myRange」。第2引数はTargetのうちの単体セルr。そのため、Intersectメソッドの戻り値や、条件式の判定値は以下のようになります。

  • rがmyRangeに含まれている→戻り値はr→「Intersect(myRange, r) Is Nothing」はFalse→If Notで反転するとTrue
  • rがmyRangeに含まれていない→戻り値はNothing→「Intersect(myRange, r) Is Nothing」はTrue→If Notで反転するとFalse

 セルが「ステータス列のデータ範囲」に含まれ、かつその値が「完了」ならば、「Sub ToDo完了メール送信(ByVal row As Long)」を呼び出します。その際に、引数として、該当セルの行番号を渡します。

Outlookでメールを送信する

 「Sub ToDo完了メール送信(ByVal row As Long)」の動作は、これまでの連載を読んでいる人なら、簡単に読み解けると思います。

 今回は、Outlookでのメール送信を紹介します。メールを送信するには、「MailItem」オブジェクトに対して、各プロパティを設定した上で「Send」メソッドを使います。

MailItemオブジェクト.Send

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。