ワークシートのイベントプロシージャ – ExcelVBAの散策とruby

2016/04/09

参考サイト:  Excel VBA 入門講座 ワークシートのイベントプロシージャ


《目次》


1. 概要

 イベントプロシージャは、ExcelをGUI操作している最中に何らかの出来事(イベント)が発生した時に呼び出される手続き(プロシージャ)のことです。

 イベントは、たとえば「該当のワークシートがアクティブになったとき」とか、「ワークシートの選択セルが変更されたとき」などいくつかあります。

 Excelのユーザーがマウスやキーボードを操作すると、操作の種類によってはイベントが発生することになります。

 イベントが発生した場合に、それに応じて実行されるプロシージャを所定のところに登録しておくと、どのタイミングで発生するか分からないイベントに対応できるようになります。

    

 これまで示したマクロは、いずれも標準モジュールというところに登録してきました。多くのマクロは、標準モジュールに登録します。

 それに対して、イベントプロシージャは、シートモジュールというところに登録します。それがどこにあるかはともかく、要は標準モジュールと違うところだということです。

 VBEを起動してシートモジュールを選択する方法については、冒頭の参考サイト「Excel VBA 入門講座 ワークシートのイベントプロシージャ」に詳しく書かれています。

 ここでは、これまでやってきたのと同様に、rubyによってシートモジュールを登録する方法を採ります。VBEを立ち上げてコードを入力する必要はありません。

    

 イベントプロシージャとしては最も簡単と思われるもの = Sheet2がアクティブシートになった時に、そのA1セルに現在時刻を書き込む、というのを取り上げます。

 Sheet2がアクティブシートになる度にA1セルに書き込みが行われるので、アクティブシートになった時点の時刻がA1セルに表示されることになります。

 プロシージャ部分だけ掲げると次のとおり。

    

Private Sub Worksheet_Activate()
    Range("A1").Value = "=NOW()"
    Range("A1").NumberFormatLocal = "HH:MM:SS"
End Sub

    

 Private Sub Worksheet_Activate() のWorksheet_Activateというのは、一定の規則に従ってつけられている名前です。

 標準モジュールの場合は Macro1 とか SetValue など自分の好みの名前をつけることができますが、イベントプロシージャの場合は使える名前が決まっています。

 その辺の事情をきちんと説明すると長くなります。また、ExcelのGUI操作を苦手とする私には荷が重い分野です。詳しく知りたい方は、たとえば下のサイトを参照して下さい。

エクセルExcel大事典 VBAマクロ イベントプロシージャ Open Target Cancel EnableEvents Volatile

    

 イベントプロシージャは、全自動一括処理で扱ってもあまり意味がないので、win32oleのスクリプト、exlapのスクリプトは掲げません。その代わりといっては何ですが、簡単なイベントプロシージャの例をいくつか取り上げようと思います。

目次に戻る


2. Worksheet_Activate

 「概要」で述べた現在時刻表示のスクリプトを掲げます。

 Sheet2をアクティブシートにする度に、そのA1セルを書き換えて現在時刻を表示します。

 これまでのようにマクロにショートカットキーを割り当てても意味がないので割り当てません。

    

 1# encoding: Windows-31J
 2require "./exlap"
 3
 4macro_str = <<'EOS'
 5Private Sub Worksheet_Activate()
 6    Range("A1").Value = "=NOW()"
 7    Range("A1").NumberFormatLocal = "HH:MM:SS"
 8End Sub
 9EOS
10
11File.delete("Book1.xls")  if test(?e, "Book1.xls")  # 既存のBook1.xlsを削除
12Exlap.new("Book1.xls") do |wb|
13    cmod = wb.VBProject.VBComponents.Item("Sheet2").CodeModule
14    cmod.AddFromString(macro_str)
15    wb.save
16end

    

 上のrubyスクリプトに出てくる Item("Sheet2") のSheet2をSheet3に変更すれば、プロシージャがSheet3に登録されます。

 そうすると、Sheet3がアクティブシートになった時にそのA1セルに現在時刻が表示されるようになります。

目次に戻る


3. Worksheet_SelectionChange

 SelectionChangeは、該当のワークシート内において選択セルが変更された時呼び出されるプロシージャです。

 カーソルを動かして、注目のセルをA1からB1に移したような場合にSelectionChangeが呼び出されます。

 下のプロシージャをシートモジュールSheet1に登録したとしましょう。

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Target.value = 123
End Sub

 この場合、選択セルをA1からB1に移すと、B1に123が書き込まれます。更にC1に移れば、C1にも123が書き込まれます。

    

 以下のサンプルでは、多少のゲーム性を持たせるため次のような仕様にしてみます。

    

 VBAにおいてRnd関数は、0以上・1未満の乱数を返します。これに30を掛け算すると、0以上・30未満の数を得ることができます。

 Int() は、与えられた数値の小数部分を切り捨てて整数部分を返します。

 If文が入れ子になったりして少しごちゃごちゃしますが、サンプルスクリプトを掲げます。

    

 1# encoding: Windows-31J
 2require "./exlap"
 3
 4macro_str = <<'EOS'
 5Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 6    Randomize
 7    If Target.Value = "" Then
 8        Target.value = Int(Rnd*30)+1
 9        If Target.Value > 25 Then
10            MsgBox Target.Value & ">25  Over!"
11            Worksheets("Sheet2").UsedRange.Clear
12            ActiveSheet.UsedRange.Copy Worksheets("Sheet2").Range("A1")
13            Worksheets("Sheet2").Activate
14        End If
15    End If
16End Sub
17
18Private Sub Worksheet_Activate()
19    ActiveSheet.UsedRange.Clear
20End Sub
21EOS
22
23File.delete("Book1.xls")  if test(?e, "Book1.xls")  # 既存のBook1.xlsを削除
24Exlap.new("Book1.xls") do |wb|
25    cmod = wb.VBProject.VBComponents.Item("Sheet1").CodeModule
26    cmod.AddFromString(macro_str)
27    wb.save
28end

    

 前の項で取り上げた Worksheet_Activate も一緒にシートモジュールに登録しています。

 Sheet2から移って再びSheet1がアクティブシートになった時に、Sheet1の内容をクリアして、ゲームを最初からやれるようにするためです。

    

 ruby利用者には If Target.Value = "" Then というのに少し違和感を覚えるかもしれません。

 rubyではif文における比較で、’==’ を用いるからです。イコール記号を二つ書きます。

 それに対して、VBAではイコール記号を一つしか書きません。知識としては知っていても、rubyになじんでいる私の場合は、ついイコール記号を二つ書いてしまいます。

目次に戻る


4. Worksheet_Change

 Worksheet_Changeは、セルに書き込まれている中身が変化すると呼び出されます。

 空欄だったA1に ABC と書き込むと Worksheet_Change が呼び出されます。

 前の項で取り上げた Worksheet_SelectionChange は、セルの中身に関係なく、選択セルを移すと呼び出されますが、Worksheet_Change の方は、選択セルが移ったかどうかに関係なく、中身が変化すると呼び出されます。

    

 以下では一種の数当ての例を掲げます。

    

 ここで少々ややこしい話を一つ。

 スクリプト内に Application.EnableEvents = False というのが出てきます。これはイベントプロシージャの呼び出しを抑制するための指示・命令です。Trueにすれば再び呼び出しが行われるようになります。

 プロシージャ内で、最下行・1列のセルに正解となる数値を書き込む訳ですが、セルに書き込みを行う度に Worksheet_Change が呼び出されるので、いわばプロシージャ内から同じプロシージャを呼び出すことになってしまいます。そうすると、その後の処理に不都合が生じます。

 そこで、Application.EnableEvents の値を調整して、最下行・1列への正解の書き込みの時は Worksheet_Change がよびだされないようにしています。

    

 1# encoding: Windows-31J
 2require "./exlap"
 3
 4macro_str = <<'EOS'
 5Private Sub Worksheet_Change(ByVal Target As Range)
 6    If Cells(ActiveSheet.Rows.Count, 1).Value = "" Then
 7        Randomize
 8        Application.EnableEvents = False
 9        Cells(ActiveSheet.Rows.Count, 1).value = Int(Rnd*20)+1
10        Application.EnableEvents = True
11    End If
12    If Str(Target.Value) = Str(Cells(ActiveSheet.Rows.Count, 1).Value) Then
13        MsgBox Target.Value & "  Hit!"
14    End If
15End Sub
16
17Private Sub Worksheet_Activate()
18    Application.EnableEvents = False
19    ActiveSheet.UsedRange.Clear
20    Application.EnableEvents = True
21End Sub
22EOS
23
24File.delete("Book1.xls")  if test(?e, "Book1.xls")  # 既存のBook1.xlsを削除
25Exlap.new("Book1.xls") do |wb|
26    cmod = wb.VBProject.VBComponents.Item("Sheet1").CodeModule
27    cmod.AddFromString(macro_str)
28    wb.save
29end

    

 Str() は、与えられたパラメータが数値であっても文字型に変換する関数です。

 If文で二つのセルの中身を比較する際、一方が文字で他方が数値だと比較がうまくいかないので、両法とも文字に変換した上で比較するようにしています。


前のページ:ワークシートの削除

次のページ:ワークブックの新規作成と保存

「ExcelVBAの散策とruby」トップページ