2016/05/19
参考サイト: Excel VBA 入門講座 ワークブックのイベントプロシージャ
イベントプロシージャは、ExcelをGUI操作している最中に何らかの出来事(イベント)が発生した時に呼び出される手続き(プロシージャ)のことです。
ワークシートのイベントプロシージャについては既に触れましたが、今回はワークブックのイベントプロシージャです。
イベントは、たとえば「該当のワークブックがオープンされるとき」とか、「ワークブックが保存されるとき」あるいは「ワークブックがクローズされるとき」などです。
Excelのユーザーがマウスやキーボードを操作すると、操作の種類によってはイベントが発生することになります。
イベントが発生した場合に、それに応じて実行されるプロシージャを所定のところに登録しておくと、どのタイミングで発生するか分からないイベントに対応できるようになります。
ワークブックのイベントプロシージャは、ブックモジュールというところに登録します。標準モジュールやシートモジュールとは違うところに登録します。
VBEを起動してブックモジュールを選択する方法については、冒頭の参考サイト「Excel VBA 入門講座 ワークブックのイベントプロシージャ」に書かれています。
ここでは、これまでやってきたのと同様に、rubyによってブックモジュールを登録する方法を採ります。VBEを立ち上げてコードを入力する必要はありません。
イベントプロシージャとしては最も簡単と思われるもの = 該当のワークブックがオープンされた時に、「Book1.xlsを開きます.」といったメッセージを出すスクリプトを取り上げます。
イベントプロシージャを設定したワークブックをオープンする度に、そのメッセージが出ます。
プロシージャ部分だけ掲げると次のとおり。
Private Sub Workbook_Open()
MsgBox ThisWorkbook.Name & "を開きます."
End Sub
ThisWorkbook というのは、ここではイベントプロシージャを組み込んだワークブックオブジェクトを指し示します。
以降においてスクリプトを掲げますが、イベントプロシージャは、全自動一括処理で扱ってもあまり意味がないので、win32oleのスクリプト、exlapのスクリプトは掲げません。その代わりといっては何ですが、簡単なイベントプロシージャの例をいくつか取り上げようと思います。
「概要」で述べた、オープン時にメッセージを出すスクリプトを掲げます。
Book1.xlsをオープンする度に、「Book1.xlsを開きます.」というメッセージを出します。
マクロにショートカットキーを割り当てることはしません。
1# encoding: Windows-31J 2require "./exlap" 3 4macro_str = <<'EOS' 5Private Sub Workbook_Open() 6 MsgBox ThisWorkbook.Name & "を開きます." 7End Sub 8EOS 9 10File.delete("Book1.xls") if test(?e, "Book1.xls") # 既存のBook1.xlsを削除 11Exlap.new("Book1.xls") do |wb| 12 cmod = wb.VBProject.VBComponents.Item("ThisWorkbook").CodeModule 13 cmod.AddFromString(macro_str) 14 wb.save 15end
上のrubyスクリプトに出てくる Item("ThisWorkbook")
というのが、ブックモジュールを処理の対象としていることを示します。
Workbook_BeforeClose は、ワークブックがクローズされる直前に呼び出されるプロシージャです。
ほんとうにクローズするか、それともクローズを取りやめるのかを、プロシージャの中で選ぶことができます。
具体的には下のようなコードになります。
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If ThisWorkbook.Saved = False Then ' 変更されているのに未保存
MsgBox ThisWorkbook.Name & ":未保存なのでクローズ取りやめ."
Cancel = True
End If
End Sub
Cancelという変数は、クローズをキャンセルするかどうかを制御するための変数です。
元々は False が代入されています。つまり、キャンセルせずにクローズする、という動作につながります。
これを True にすると、クローズがキャンセルされます。ワークブックが閉じられずに元の状態に戻ります。
このコードの場合、ワークブックが未保存だとクローズできません。ちょっと不便です。
そこで、下のスクリプトでは、「未保存だけど、本当にクローズしてもいいかどうか」を確認し、それへの Yes, No に応じてクローズするかどうか決めるようにしています。
少しごちゃごちゃした感じになりますが、枠組みは簡素です。
1# encoding: Windows-31J 2require "./exlap" 3 4macro_str = <<'EOS' 5Private Sub Workbook_BeforeClose(Cancel As Boolean) 6 Dim Res As VbMsgBoxResult 7 If ThisWorkbook.Saved = False Then ' 変更されているのに未保存 8 Res = MsgBox(ThisWorkbook.Name & "が未保存です。" & _ 9 "本当にクローズしますか?", vbYesNo + vbQuestion) 10 If Res = vbNo Then 11 Cancel = True 12 Else 13 ThisWorkbook.Saved = True ' 保存したことにする 14 End If 15 End If 16End Sub 17EOS 18 19File.delete("Book1.xls") if test(?e, "Book1.xls") # 既存のBook1.xlsを削除 20Exlap.new("Book1.xls") do |wb| 21 cmod = wb.VBProject.VBComponents.Item("ThisWorkbook").CodeModule 22 cmod.AddFromString(macro_str) 23 wb.save 24end
上のスクリプトを実行すると Book1.xls ができます。
それをオープンしてから何か書き込みをし、Excelを終了させようと試みると、
「Book1.xlsが未保存です。本当にクローズしますか?」というメッセージが出て、「はい」 「いいえ」のボタンも出ます。
「はい」を選ぶとクローズ、「いいえ」だと元に戻ります。
Book1.xlsをオープンしてから何も書き込みをせずにExcelを終了させようとしたときは、メッセージが出ずにすんなり終了します。
Workbook_BeforeSave は、ExcelをGUI操作している場合でいうと「上書き保存」あるいは「名前をつけて保存」を選択したときに、保存処理の前に呼び出されます。
VBAのマクロ記述でいうと、Save, SaveAs が実行される前に呼び出されます。
ワークブックをクローズするとき、または、Excelを終了させるときも、ブックに変更が加えられているとブックを保存するかどうかのダイアログが出ます。
しかし、この場合は Workbook_BeforeSave が呼び出されないようです。
Workbook_BeforeSaveには二つのパラメータがあり、たとえば次のように記述します。
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, _
Cancel As Boolean)
Cancel = True
End Sub
SaveAsUI, Cancel の両法とも、その値は True, False のどちらかです。
SaveAsUI は、「上書き保存」(Save)の前に呼び出されたときは False、「名前をつけて保存」(SaveAs)のときは True が代入されています。
Cancel には False が代入されています。
これを True に変更すると、保存が行われないまま元の状態に戻ります。
第1ワークシートのA1セルが空欄の場合にメッセージを出して保存はしない、というイベントプロシージャは下のようになります。
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, _
Cancel As Boolean)
If Worksheets(1).Range("A1").Value = "" Then
MsgBox "第1ワークシートのA1せるが空欄なので保存しません。"
Cancel = True
End If
End Sub
前置きが長くなりました。以下、イベントプロシージャを登録するスクリプトです。
1# encoding: Windows-31J 2require "./exlap" 3 4macro_str = <<'EOS' 5Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, _ 6 Cancel As Boolean) 7 If Worksheets(1).Range("A1").Value = "" Then 8 MsgBox "第1ワークシートのA1せるが空欄なので保存しません。" 9 Cancel = True 10 End If 11End Sub 12EOS 13 14File.delete("Book1.xls") if test(?e, "Book1.xls") # 既存のBook1.xlsを削除 15Exlap.new("Book1.xls") do |wb| 16 cmod = wb.VBProject.VBComponents.Item("ThisWorkbook").CodeModule 17 cmod.AddFromString(macro_str) 18 wb.Application.EnableEvents = false 19 wb.save 20 wb.Application.EnableEvents = true 21end
スクリプトの最後の方で、Application.EnableEvents の値を false にしたり true に戻したりしています。
これは、Book1.xlsを保存するときに Workbook_BeforeSave が呼び出されるのを抑制するための処置です。
スクリプトやマクロの中でワークブックの保存を行う場合、Workbook_BeforeSave が呼び出されると困るときは、Application.EnableEvents を False にして下さい。イベントプロシージャの呼び出しが抑制されます。
当シリーズ「ExcelVBAの散策とruby」は、これで終了です。