Word-VBAでApplicationイベントを利用すると、どんないいことがある?
「普通の」方法ではdocumentレベルの、たった3つのイベント(New,Open,Close)しか使えませんが、Applicationレベルのイベント(記事の下部の一覧表を参照)を使うと、複数のドキュメントのイベントを一括で扱えるだけでなく、差込印刷やウィンドウがアクティブや非アクティブになったとき、またダブルクリックや右クリック、印刷のイベントも補足できます。
ワクワクしませんか?
Application イベント プロシージャはどこに置くこともできます。どこにでもと書きましたが、一般的にはWordアプリケーションを制御するときに必ず開いているWord,ExcelまたはAccessになるでしょう。
Exce? Access?と思われるかもいるかもしれませんが、CreateObjectもしくはGetObject関数を使って、Wordのインスタンスを補足すれば、そんなこともできます。
Microsoftのドキュメントには、アドインテンプレートに記述し、そのアドインテンプレートをWordに読み込む方法を取っていますが、今回はもっと直接的に、Wordのファイルに記述する方法をでいきます。
WindowsXP Word2002 SP3で動作確認しました。
最初の準備
1.Wordファイルを開きvisual basic editorを起動する。
2.クラスモジュールを追加し、名前を「clsEventHandler」に変更する。
WithEventsキーワードを使う
3.クラスモジュール「clsEventHandler」に次のコードを記載する。
Option Explicit 'Word のインスタンスのみがこの変数に割り当てられることと、 '割り当てられたインスタンスに、Application イベント プロシージャについて 'イベント ハンドラ内をチェックするように伝える必要があることを宣言します。 Public WithEvents AppThatLooksInsideThisEventHandler As Word.Application
イベントハンドラオブジェクトを宣言する
4.標準モジュールに任意のモジュールを追加し、モジュール内に以下のように記載します。
Option Explicit ' objEventHandler 変数に格納されるオブジェクトのタイプを宣言します。 Public objEventHandler As clsEventHandler
ここでは、ThisDocument モジュール内のDocument_OpenイベントでobjEventHandler変数を使いたいので、Public宣言していますが、Microsoftの資料のように、同一のモジュール内に
Public Sub AutoExec() Set objEventHandler = Word.Application End sub
と記載するのであれば、Public宣言でなくDim宣言でOKです。
使い回しできるように、ちょっと工夫
5.既に作ったクラスモジュール「clsEventHandler」内に、次のように追加して記述します。
ここで定義したInitメソッドは、イベントの探知を開始するタイミングで使います。
Public Sub Init(ByVal In_DocObj As Word.Application) 'AppThatLooksInsideThisEventHandlerを初期化する。 Set AppThatLooksInsideThisEventHandler = In_DocObj End Sub
イベントハンドラオブジェクトの生成と廃棄
6.「ThisDocument」モジュール内で、コードペイン上部のオブジェクトドロップダウンリストから「Document」を選ぶ。また、プロシージャドロップダウンリストからDocument_Openを選択し、空のプロシージャを作る。
そして、その空のプロシージャ内に次のように記載する。
'objEventHandlerにclsEventHandlerを割り当てる。 Set objEventHandler = New clsEventHandler 'クラスモジュールに宣言したInitメソッドを呼び出して、初期化する。 'objEventHandlerにWord.Applicationをセットする。 Call objEventHandler.Init(Word.Application)
また、クラスモジュール「clsEventHandler」内に次のように記載して、Wordが終了するときにイベントハンドラを破棄します。
Private Sub AppThatLooksInsideThisEventHandler_Quit() Set objEventHandler = Nothing End Sub
これで、準備は完了です。あとは、それぞれのイベントで何をさせるか記述しましょう。
イベントが発火するときにさせたいことを書く
7.クラスモジュール「clsEventHandler」内で、コードペイン上部のオブジェクトドロップダウンリストから「AppThatLooksInsideThisEventHandler」を選び、また、プロシージャドロップダウンリストから任意のイベントを選択すると、空のプロシージャができるので、そこにそのイベントで駆動させたいコードを記述する。
サンプル1
たとえば、
Private Sub AppThatLooksInsideThisEventHandler_WindowBeforeDoubleClick(ByVal Sel As Selection, Cancel As Boolean) Dim lngType As Long Dim strType As String lngType = Sel.Type Select Case lngType Case wdNoSelection strType = "選択なし" Case wdSelectionBlock strType = "ブロックの選択" Case wdSelectionColumn strType = "列の選択" Case wdSelectionFrame strType = "レイアウト枠の選択" Case wdSelectionInlineShape strType = "インライン図形の選択" Case wdSelectionIP strType = "インラインの段落の選択" Case wdSelectionNormal strType = "通常の選択またはユーザー定義の選択" Case wdSelectionRow strType = "行の選択" Case wdSelectionShape strType = "図形の選択" End Select MsgBox "Event: WindowBeforeDoubleClick Selection: " , strType End Sub
とすると、ウィンドウをダブルクリックするたびにクリックされた選択範囲の種類を表示させることになります。
サンプル2
また、DocumentBeforeSaveイベントを利用する例として、
Private Sub AppThatLooksInsideThisEventHandler_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean) Dim intResponse As Integer Dim strMessage As String strMessage = "このファイル「" & Doc.Name & "」を保存しますか?" intResponse = MsgBox(strMessage, vbYesNo) If intResponse = vbNo Then Cancel = True End Sub
に記述すると、Ctrl+sで上書き保存をするときでも、メッセージボックスが表示され、本当に保存するかどうか確認できます。
引数にDocumentオブジェクトをとるので、ファイル名をはじめとするDocumentオブジェクトの属性によって処理を分岐できます。
SaveAsUI は読み取り専用のようです
ただ、引数の SaveAsUI の取扱には注意が必要です。
ヘルプには、
SaveAsUI True の場合 [名前を付けて保存] ダイアログ ボックスが表示されます。
と書いてあります。なんとなく読んでいると、SaveAsUIにfalseを返せば、[名前を付けて保存] ダイアログ ボックスが表示されないように思えてしまいます。
しかし、引数Cancelのヘルプには
Cancel イベントが発生すると、False が渡されます。イベント プロシージャでこの引数に True を設定すると、プロシージャが終了したときに文書は保存されません。
と、値を設定できることが明示されているにもかかわらず、SaveAsUIにはそのような記載がないことから、SaveAsUIは読み取り専用なようです。
じっさい、SaveAsUIの値によって[名前を付けて保存] ダイアログ ボックスの表示は制御できません。それどころか、[名前を付けて保存] ダイアログ ボックスが表示されるケースで、SaveAsUI=falseとすると、Wordが強制終了します。
ファイルを保存して、再度ファイルを開いてみる
8.最後に、ファイルを上書き保存して、再度ファイルを開くと、文書が開いたときに発生するOpenイベントのあと、Applicationイベントが制御できるようになります。
イベントハンドラオブジェクトの生成と廃棄はご自由に
9.今回は、ドキュメントを開いたタイミングでobjEventHandlerを初期化して、イベントを補足するようにしましたが、任意のタイミングでイベントの補足を開始する場合は、標準モジュールに
Sub setup() Set objEventHandler = New clsEventHandler Call objEventHandler.Init(Word.Application) End Sub
として、このサブプロシージャを走らせることになります。
また、イベントの補足をやめさせたいときには、
Sub Kaiho() Set objEventHandler = Nothing End Sub
を起動します。
思うところを述べてみる
どうやって使ったら楽しいかな
アプリケーションレベルのイベントはいろいろな使い方ができますが、私はファイルを保存するとき、特定のプロパティ値を強制的に指定した値に変更するために、DocumentBeforeSaveイベントを使っています。あまりWordで定型作業をすることがないので、出番は少ないです。
Wordで定型的な作業をする場合には、「イベントを使用して Microsoft Word を制御する」のように、アドインテンプレートにコードを記載して、アドインを読み込むようにしたほうが楽でしょうね。
普段はもっぱらAccess使いなので、Applicationレベルのイベントには関心がなかったのですが、WordのみならずExcelにもアプリケーションイベントがあります。同様の方法でExcelのApplicationイベントも補足できます。
興味深い資料 その1 イベントハンドラを自作しなくてはならない訳
この記事は、msdnの「イベントを使用して Microsoft Word を制御する(http://msdn.microsoft.com/ja-jp/library/aa140279(office.10).aspx)」を参考にしたのですが、WithEventsステートメントについて興味深い解説がありましたので、紹介します。
それは、「”Application イベント” プロシージャの機能を理解する」の中の次の部分です。
Application イベント プロシージャを詳細に説明する前に、ここでこれまでに説明した Document イベント プロシージャに伴う疑問について考えてみるとわかりやすくなります。”VBA コード プロジェクトに Document イベント プロシージャを保存するときに特別な準備が必要ないのはなぜでしょうか。”
その理由の 1 つは、Word が、その Document イベント プロシージャがどこにあるのかを既に認識しているからです (存在する場合)。前に説明したように、設計によって、そのようなプロシージャは文書またはそれに添付されたテンプレートの VBA コード プロジェクトの ThisDocument モジュール内にあります。
Document イベント プロシージャに特別な準備が必要ないもう 1 つの理由は、ユーザーが新しい文書を作成するか、既存の文書を開くまたは文書を閉じるたびに、Word によって対応するイベント プロシージャが存在するかどうかが自動的に調べられるからです。Word にこれを実行するように指示する必要はありません。
Document イベント プロシージャの存在を常にチェックするために発生するわずかな遅延にユーザーが気付いたり、誰かが迷惑を被ることはないと Word の開発チームが考えていたことは明らかです。ユーザーが文書を作成したり、開いたり閉じたりする場合の、ディスクやネットワークにアクセスするときの遅延に比べれば、わずかな時間であるため、これは道理にかなっています。
これらの条件は、いずれも Application イベント プロシージャには当てはまりません。まず第一に、Application イベント プロシージャのための既定の場所が確立されていないので、その場所を指定しない限り、Word はチェックする場所を認識できません。
さらに、Application イベントは頻繁に発生し、またユーザーが速い応答に慣れているときに発生する場合もあります。したがって、それらのイベントが発生するたびにイベント プロシージャについて Word がチェックするのは、そのようなプロシージャが存在するとわかっていない限り、意味がありません。この問題を解決するために、Word は明示的に指示された場合にのみ、Application イベント プロシージャをチェックします。
では、Application イベント プロシージャの保存場所と、Application イベントが発生するたびにそれらをチェックするように Word に認識させるにはどうしたらよいでしょうか。答えは概念的にはとても簡単で、実行においてもほとんど同様に簡単です。概念的には、実行する必要があるのは以下の事項だけです。
1.イベント プロシージャを内部に格納する “イベント ハンドラ” クラス モジュールを作成します。
注: クラス モジュールは、オブジェクトのカテゴリ、つまり “クラス” を記述すると言われています。作成するクラス モジュールでは、イベント ハンドラと呼ばれるオブジェクトのクラスを記述します。後で、このクラスに属するオブジェクトを作成します (技術的には、そのようなオブジェクトを複数作成できますが、おそらく 1 つで十分です)。クラス モジュールが行うことは、クラスの各メンバがどのようなものであるかを記述することだけです。これは、後で必要な数だけイベント ハンドラを作成できるようにイベント ハンドラの型を作成するようなものです。
2.イベント ハンドラ クラス モジュールの記述に基づいて、イベント ハンドラ オブジェクトを作成します。
注: オブジェクトは、クラスのメンバまたは “インスタンス” と呼ばれることがあります。必要に応じて、1 つのクラスの複数のインスタンスを作成できます。各インスタンスは、そのクラスのクラス モジュールに格納された記述に従います。
なるほど。よく分かりました。簡潔な説明ですねー。
興味深い資料 その2 WithEventsキーワードとActiveXとOLEと
参考資料をもう一つ。
The Word MVP Site というサイトにある、「Writing application event procedures」。
内容は「イベントを使用して Microsoft Word を制御する」とほぼ同じなのですが、気になったのが次の一文。
The WithEvents keyword can only be used in Class Modules and can only refer to ActiveX – i.e. OLE-compliant – objects.
(WithEventsキーワードは、クラスモジュールでのみ使える。また、ActiveX、すなわちOLEで操作されるオブジェクトのみ参照できる。)
こういう説明は、初めて見ました。
WithEventsステートメントを使ってイベントを補足するときは、OLEの機能を使っているようです。
参考資料
Application イベント プロシージャ |
実行されるタイミング |
---|---|
DocumentBeforeClose | 開いていた文書を閉じる直前。 |
DocumentBeforePrint | 開いている文書を印刷する前。 |
DocumentBeforeSave | 開いている文書を保存する前。 |
DocumentChange | 新規文書を作成したとき、既存の文書を開いたとき、または別の文書をアクティブな文書にしたとき。 |
DocumentOpen | ドキュメントが開いたとき。 |
EPostageInsert | ユーザーが文書に電子切手を挿入したとき。 |
EPostagePropertyDialog | ユーザーが [宛名ラベル作成] ダイアログ ボックスの [電子切手のプロパティ] ボタンまたは電子切手を印刷するためのツールバー ボタンをクリックしたとき。このイベントでは、サードパーティ製ソフトウェア アプリケーションがインターセプトし、それらのプロパティのダイアログ ボックスを表示することができます。 |
MailMergeAfterMerge | 差し込み印刷のすべてのレコードのマージが正常に行われた後。 |
MailMergeAfterRecordMerge | 差し込み印刷でデータ ファイルの各レコードのマージが正常に行われた後。 |
MailMergeBeforeMerge | レコードのマージの前にマージが実行されたとき。 |
MailMergeBeforeRecordMerge | マージ内の個別のレコードに対してマージが実行されたとき。 |
MailMergeDataSourceLoad | 差し込み印刷のためにデータ ファイルが読み込まれたとき。 |
MailMergeDataSourceValidate | ユーザーが [差し込み印刷の宛て先] ダイアログ ボックスで [確認] をクリックすることによってユーザーが住所の確認を実行するとき。 |
MailMergeWizardSendToCustom | 差し込み印刷ウィザードの手順 6 でカスタム ボタンをクリックしたとき。 |
MailMergeWizardStateChange | 差し込み印刷ウィザードで指定された手順から指定された手順にユーザーが変更したとき。 |
NewDocument | 新規文書が作成されたとき。 |
Quit | ユーザーが Word を終了したとき。 |
WindowActivate | 文書ウィンドウがアクティブになったとき。 |
WindowBeforeDoubleClick | 既定のダブルクリック アクションの前に、文書ウィンドウの編集領域をダブルクリックしたとき。 |
WindowBeforeRightClick | 既定の右クリック アクションの前に、文書ウィンドウの編集領域を右クリックしたとき。 |
WindowDeactivate | 文書ウィンドウが非アクティブになったとき。 |
WindowSelectionChange | アクティブな文書ウィンドウで選択が変更されたとき。 |
WindowSize | アプリケーション ウィンドウがサイズ変更されたか移動されたとき。 |
最近のコメント