Take Control of Microsoft Word Through Events
Bill CoanMicrosoft Word MVP
May 2003
Applies to: Microsoft® Word 2000 and later versions
Summary: Bill Coan, a Microsoft Word Most Valuable Professional (MVP), provides information about how to change the way Word works. Application event procedures are the most powerful and flexible way to do this but other methods are useful, too, so he starts with the easier methods and works his way up to application event procedures. (13 printed pages)
Contents
IntroductionReplace Built-in RoutinesTake Advantage of "Auto" MacrosCreate "Document Event" ProceduresDiscover the Power of "Application Event" ProceduresTest Your Event HandlerShorter Names for Your Event Handler and Application ObjectsApplication Events Supported By Word 2002 and LaterUseful Examples of Application Event ProceduresAbout the Author
Introduction
The single smartest thing the Microsoft® Word development team ever did was to acknowledge and accept its own limitations. (We should all be so humble.)
Rather than viewing itself as an all-knowing, all-seeing, all-controlling gang of software dictators, the development team recognized from the beginning that power users and independent developers would have their own ideas about how Word should behave. That's why, with each new release of Word, the team has provided new, more exciting ways for power users and independent developers to alter Word's behavior.
In this article, we'll start by reviewing briefly some of the oldest and easiest-to-use techniques for altering Word's behavior. Then we'll move on and examine some of the newest and most exciting techniques for taking control of our favorite word processing application.
Replace Built-in Routines
One way we can alter Word's behavior is by creating macros to run in place of Word's built-in routines. For example, Word has a built-in routine called FilePrint that runs when a user chooses the File menu and then selects Print. If we create a macro called FilePrint, Word runs our macro instead of the built-in routine. (To see a list of built-in routines that can be overridden with like-named macros, on the Tools menu, point to Macro, and then click Macros. On the Macros dialog box set the Macros in: list to Word commands.)
Take Advantage of "Auto" Macros
Another way we can alter Word's behavior is by creating "auto" macros that run automatically when a user creates a new document (or opens or closes an existing one) based on a particular template. In addition, we can create auto macros that run automatically when Word loads or unloads a global template. The following table lists the auto macros supported by Word and describes when Word runs each one.
Auto Macro
Runs when …
AutoNew
Runs, if present, when a user creates a new document from the template containing the macro. (Note Technically, this is a legacy capability that, while still supported and likely to be supported well into the future, has since been supplanted by the Document_New event discussed later in this article.)
AutoOpen
Runs, if present, when a user opens an existing document based on the template containing the macro. (Note This is a legacy capability that, while still supported and likely to be supported well into the future, has since been supplanted by the Document_Open event discussed later in this article.)
AutoClose
Runs, if present, when a user closes an existing document based on the template containing the macro. (Note This is a legacy capability that, while still supported and likely to be supported well into the future, has since been supplanted by the Document_Close event discussed later in this article.)
AutoExec
Runs, if present, when the template containing the macro is loaded globally. (Note Normal.dot is loaded globally when Word starts. Templates stored in Word's Startup folder or the Office Startup folder also are loaded globally when Word starts. In addition, a template can be loaded globally by clicking the Tools menu, clicking Templates and Add-Ins, and then clicking Add or selecting a template in the list of Global templates and add-ins.)
AutoExit
Runs, if present, when the template containing the macro has been globally loaded and is then unloaded. (All globally loaded templates are unloaded when Word exits. In addition, a globally loaded template can be unloaded by clicking the Tools menu, clicking Templates and Add-Ins, and then clearing the template checkbox in the list of Global templates and add-ins.)
Create "Document Event" Procedures
Starting with Word 97, the distinction between Word documents and Word templates was blurred a bit when documents were given the ability to store Visual Basic for Applications (VBA) code projects. The most important aspect of this change is that both documents and templates were given the ability to store "document event" procedures, for example, procedures that run automatically when an event takes place that affects a particular document. The following table lists the document event procedures supported by Word since Word 97 and describes when Word runs each one.
Document Event Procedure
Runs when …
Document_New
Runs, if present, when a user creates a new document from a template containing the procedure. (Does NOT run when a user creates a new document from an existing document. Therefore, it doesn't make sense to store a Document_New procedure in a document's VBA code project. Instead, the procedure should always be stored in a template's VBA code project.)
Document_Open
Runs, if present, when a user opens a document containing the procedure or opens a document attached to the template containing the procedure. (Note If a document contains a Document_Open procedure and the template attached to the document also contains a Document_Open procedure, Word will run both procedures, one after the other. The procedure stored in the template runs first, followed immediately by the procedure stored in the document.)
Document_Close
Runs, if present, when a user closes a document containing the procedure or closes a document attached to the template containing the procedure. (Note If a document contains a Document_Close procedure and the template attached to the document also contains a Document_Close procedure, Word will run both procedures, one after the other. The procedure stored in the template runs first, followed immediately by the procedure stored in the document.)
Note When a user creates a new document, or opens or closes an existing one, Word first checks to see whether the template attached to the document contains a procedure for that event. If it does, then Word runs the procedure. Next, Word checks to see whether the document itself contains a procedure for the event. If it does, then Word runs that procedure. (If the template and the document both contain a procedure for the event, Word runs both procedures, one after the other. If neither the template nor the document contains a procedure, Word carries out the user's action without running a procedure.)
Important One benefit of document event procedures, as compared to auto macros, is that document event procedures stored in templates can coexist with those stored in documents. That is, if a document and its attached template both contain a procedure for a particular document event, Word will run both procedures, starting with the one stored in the template. This isn't true of auto macros. If a document contains an AutoOpen macro and its attached template also contains an AutoOpen macro, Word will run only the one stored in the document.
How to Store Document Event Procedures in a VBA Project
No special preparations are needed to store a document event procedure in a VBA code project. On the contrary, every VBA code project, whether it belongs to a template or to a document, by default contains a special module (called ThisDocument) specifically designed for storing document event procedures. Here's how to store a document event procedure in such a module:
Start Word and open the document or template in which you want the procedure to be stored. (If the procedure is to be stored in a template, you can create or open a document based on the template, if preferred, rather than opening the template itself.)
Press Alt+F11 to launch the Visual Basic Editor.
In the Project Explorer pane (upper left corner of the screen), locate the VBA project for your document or template.
Note For each document open in Word, you will find a project for that document and also a project for the document's attached template. Make sure you choose the project where you want the event procedure to be stored.
If you see a plus sign (+) next to the project, click it to expand the project tree. This will reveal that the project contains a branch for Microsoft Word Objects. If you see a plus sign next to this branch, click it to expand the branch. This will reveal the project's ThisDocument module.
Double-click the project's ThisDocument module to display the contents of the module in the Code pane (right side of the screen).
At the top of the Code pane, click the Object drop-down list and select Document. Then in the Procedure drop-down list, click Close, then click New, and finally click Open. This will create an empty procedure for the Document_Close, Document_New, and Document_Open events. Type the code in each procedure that you want to run when that event occurs. For example, you could type the following, substituting the appropriate event name, which displays a dialog box telling you what event has fired:
Copy CodeMsgbox "The
On the File menu, click Save and then click Close and return to Microsoft Word.
Discover the Power of "Application Event" Procedures
In addition to introducing document event procedures, Word 97 introduced application event procedures—another new and exciting way for power users and developers to alter Word's behavior.
Like document event procedures, application event procedures run automatically when certain events occur. To that extent, application event procedures are as easy to understand as document event procedures.
When you look more deeply into application event procedures, you discover that they are a "good news"/"bad news"/"more good news" proposition. The good news is that they provide far more power and flexibility than document event procedures. The bad news is that a certain amount of preparation is required before a VBA code project can take advantage of them. The final good news is this: the amount of preparation needed is very small relative to the gain in power and flexibility.
Note Back when Word 97 was introduced, Word supported only two application events, so you didn't have much to lose if you decided not to investigate them. With each new version of Word since then, the number of application events supported by Word has steadily increased. For example, Word 2002 SP-2 supports 23 different application events. It's fair to assume that Word 2003 will support an even greater number of application events. So if you haven't explored application event procedures before now, you couldn't pick a better time to begin.
Before examining application event procedures in detail, it's helpful to back up a moment and consider a question inherent in our earlier discussion of document event procedures: "Why aren't any special preparations needed when storing a document event procedure in a VBA code project?"
One reason for this is that Word already knows where document event procedures can be found (if present). As noted earlier, by design, such procedures can be found in the ThisDocument module of the VBA code project for the document or its attached template.
Another reason no special preparations are needed for document event procedures is that every time a user creates a new document, opens an existing document, or closes a document, Word automatically checks to see whether a corresponding event procedure is present. No one has to tell Word to do this.
Apparently the Word development team felt that users wouldn't notice or else wouldn't be troubled by the very slight delay caused by always checking for the presence of document event procedures. This makes sense, since the delay is negligible compared to the disk- or network-access delays when a user creates, opens, or closes a document.
Neither of the above conditions applies to application event procedures. To start with, no default location has been established for such procedures, so Word doesn't know where to check for them until you tell it where to check.
Moreover, application events occur very frequently and at times when users are accustomed to very rapid responses. Therefore, it doesn't make sense for Word to check for event procedures every time those events occur, unless there's reason to believe that such procedures are present. To solve this problem, Word checks for application event procedures only when you explicitly tell it to.
So how do you tell Word where you've stored your application event procedures and that you want it to check for them whenever an application event occurs? The answer is very simple in concept and almost as simple in practice. In concept, all you have to do is the following:
Create an "event handler" class module with event procedures stored inside it.
Note A class module is said to describe a category or a "class" of objects. Your class module will describe a class of objects known as event handlers. Later you will create an object that belongs to this class. (Technically, you could create two or three or even more such objects but one is probably all you'll need.) The only thing a class module does is describe what each member of a class will be like. It's almost as if you're creating a mold for event handlers so that, later on, you can create as many event handlers as you need.
Create an event handler object based on the description in your event handler class module.
Note An object is sometimes referred to as a member or an "instance" of a class. Multiple instances of a class can be created, if desired, and each one will conform to the description stored in the class module for that class.
If this all seems rather mysterious, don't worry. More than likely, it's only because some of the terminology may be new to you. In the paragraphs that follow, we'll put these concepts into practice and along the way we'll stop and review what's going on.
Important Some of the names used below are ridiculously long but I hope they will help you visualize how application events are handled. Later I will suggest some shorter names but for now please bear with me and accept the names suggested.
How to Store Application Event Procedures in a VBA Project
Application event procedures can be stored in a VBA code project for a document, a regular template, or a global template. Of these locations, a global template is the most commonly used location because application events are themselves global. That is, they affect all documents. Therefore, in the steps that follow, we'll assume that you want to store your application event procedures in an add-in template called EventHandlerAddin.dot. Here's what to do:
Start Word and create a new template and immediately save it as EventHandlerAddin.dot.
Press Alt+F11 to launch the Visual Basic Editor.
In the Project Explorer pane (upper left corner of the screen), locate the VBA project for EventHandlerAddin.dot.
Note For each document open in Word, you will find a project for that document and also a project for the document's attached template. Make sure you choose the project for EventHandlerAddind.dot.
Right-click the project, click the Insert menu, and then click Class Module. In the Properties pane (lower left corner of screen) click in the Name field and set the class module's name to clsEventHandler.
In the Code pane (right side of screen) type the following:
Copy Code...
'Declare that only an instance of Word can be assigned to this variable
'and also that the assigned instance should be notified to
'check inside the event handler for application event procedures.
Public WithEvents _
AppThatLooksInsideThisEventHandler _
As Word.Application
...
At the top of the Code pane, click the Object drop-down list, and select AppThatLooksInsideThisEventHandler. In the Procedure drop-down list, click DocumentBeforeClose, then click DocumentBeforePrint,and finally click DocumentBeforeSave. This will create an empty procedure for each of these events. We will explore other events later.
Type the code that you want to run when that event occurs into each procedure. In this case, let's enter a simple message box that will display the name of the associated application event when the procedure runs. For example, the first statements shown below applies to the DocumentBeforeClose event procedure, the second set apply to the DocumentBeforePrint, and the third to the DocumentBeforeSave event procedure:
Copy CodePrivate Sub AppThatLooksInsideThisEventHandler_DocumentBeforeClose(ByVal Doc As Document, Cancel As Boolean)
MsgBox "Event: DocumentBeforeClose"
End Sub
Private Sub AppThatLooksInsideThisEventHandler_DocumentBeforePrint(ByVal Doc As Document, Cancel As Boolean)
MsgBox "Event: DocumentBeforePrint"
End Sub
Private Sub AppThatLooksInsideThisEventHandler_DocumentBeforeSave(ByVal Doc As Document, SaveAsUI As Boolean, Cancel As Boolean)
MsgBox "Event: DocumentBeforeSave"
End Sub
On the File menu, choose Save.
Let's pause and review what we've accomplished so far.
We've created a class module called clsEventHandler that describes a class of event handler objects but doesn't bring such an object into existence (also known as instantiating the object). An event handler object will be brought into existence later. All that exists in the class module is a description of what each event handler will be like.
When created, an event handler object will include a Word application object with events and also will include procedures for the following application events: DocumentBeforeClose, DocumentBeforePrint, and DocumentBeforeSave.
Note In this context, "a Word application object" means a data structure in memory reflecting all the properties and methods of the Word application and "with events" means that the instance of Word involved will be notified that it needs to check for event procedures inside the event handler object whenever an application event occurs.
To emphasize: So far, all we've done is define an event handler object. No such object has been created yet. We're almost there, so let's proceed:
Right-click your code project, click the Insert menu, and then click Module. In the Properties pane (lower left corner of screen) click in the Name field and type modHandleEvents.
In the Code pane (right side of screen) type the following:
Copy Code...
' declare type of object to be stored in the objEventHandler variable
Dim objEventHandler As clsEventHandler
Sub CreateEventHandler()
'create a new event handler object and assign it to objEventHandler
Set objEventHandler = New clsEventHandler
'Notify the current instance of Word to check for
'application event procedures
'inside the event handler object
Set objEventHandler.AppThatLooksInsideThisEventHandler = Word.Application
End Sub
Sub DestroyEventHandler()
'release the memory being used by the event handler object
Set objEventHandler = Nothing
End Sub
Sub AutoExec()
'whenever this template is loaded globally,
'automatically create an event handler
CreateEventHandler
End Sub
Sub AutoExit()
'whenever this template is unloaded,
'automatically destroy the event handler
DestroyEventHandler
End Sub
On the File menu, click Save, then click Close and return to Microsoft Word.
Close Word.
Let's pause again and review what we've accomplished.
We've created a code module called modHandleEvents and in it, we've declared that we will be working with a variable called objEventHandler of type clsEventHandler. This declaration doesn't create an object, it just sets its type. That is, it tells the compiler what kind of object the variable refers to so that the compiler (and Microsoft IntelliSense® feature) can help us use the variable in accordance with the object description stored in clsEventHandler.
We've established a subroutine called CreateEventHandler. This subroutine creates a new instance of the class called clsEventHandler and assigns it to objEventHandler. In addition, this subroutine assigns the current instance of Word to the AppThatLooksInsideThisEventHandler property of objEventHandler.
We've also established a subroutine called DestroyEventHandler. This subroutine destroys our event handler by releasing the memory allocated to objEventHandler.
Finally, we've created an AutoExec macro that will run automatically when this template is loaded globally and an AutoExit macro that will run automatically when this template is unloaded. The AutoExec macro automatically creates an event handler. The AutoExit macro automatically destroys the event handler.
At last our event handler exists! Or at least, it will exist as soon as Word loads our template globally and runs the CreateEventHandler subroutine.
Another thing that will happen when Word runs the CreateEventHandler subroutine is that the current instance of Word will be assigned to our event handler's AppThatLooksInsideThisEventHandler property. As a result, Word will be notified to check for event procedures inside the event handler whenever an application event occurs.
In the case of certain events (DocumentBeforeClose, DocumentBeforePrint, and DocumentBeforeSave), Word will find an associated event procedure and run it. In the case of other events, Word will look for but not find an associated event procedure and therefore will complete its normal actions without running a procedure.
Test Your Event Handler
Testing your event handler will take just a few seconds. To see it in action, proceed as follows:
Start Word.
On the Tools menu, click Templates and Add-ins, click Add, click EventHandlerAddin.dot, and then click OK. This will cause Word to load the add-in template globally which will cause Word to run the add-in's AutoExec macro, which in turn will create an event handler object in memory. (It's a long chain of events, isn't it, but now that you understand that chain, it needn't seem strange or intimidating.)
Try saving, printing, and closing some documents. Each time you do this, a message will pop up, indicating that an application event has occurred and that Word has responded by running one of your application event procedures.
Click the Tools menu, click Templates and Add-ins, clear the EventHandlerAddin.dot checkbox, and then click OK. This will cause Word to unload the add-in template which will cause Word to run the add-in's AutoExit macro, which in turn will destroy the event handler object. (It's another long chain of events, but a familiar one by now.)
Try saving, printing, and closing some documents. When you do this, no messages will pop up because Word is no longer checking for application event procedures and in any case your application event procedures are no longer present in memory.
Shorter Names for Your Event Handler and Application Objects
In this article, I've suggested that you create a class module called clsEventHandler and that within this class module you declare a Word application object variable called AppThatLooksInsideThisEventHandler.
I suggested these names to help you visualize what goes on when you create an event handler object and assign an instance of Word to it. I am the first to admit, however, that the clarity of a name is in the eye of the beholder.
For my own projects, I'm perfectly happy using a class module called clsEventHandler. On some occasions, I change the name to clsAppEventHandler, to help me remember that my event handler is designed to handle application events only. Sometimes I leave off the cls prefix, calling the class module simply AppEventHandler.
As for the Word application object variable inside the class module, my preferred name for it is EventSource. This works out very well with VBA's IntelliSense feature, because later, in the regular code module (modHandleEvents) where I create an event handler object, I can simply type a period and the IntelliSense drop-down list help me choose an EventSource property for the event handler. This results in the following code:
Copy CodeDim objEventHandler As clsEventHandler
Sub CreateEventHandler()
Set objEventHandler = New clsEventHandler
Set objEventHandler.EventSource = Word.Application
End Sub
As you continue experimenting with application event procedures, you'll likely want to experiment until you come up with names that suit you and help you visualize what's going on.
Application Events Supported By Word 2002 and Later
As noted above, each new version of Word has supported a greater number of application events than the versions that preceded it. The following list is current for Microsoft Office 2002 Service Pack 2. To learn which application events are supported by your version of Word, look up application events in VBA Help.
Application Event Procedure
Runs when …
DocumentBeforeClose
Immediately before any open document closes.
DocumentBeforePrint
Before any open document is printed.
DocumentBeforeSave
Before any open document is saved.
DocumentChange
A new document is created, when an existing document is opened, or when another document is made the active document.
DocumentOpen
A document is opened.
EPostageInsert
A user inserts electronic postage into a document.
EPostagePropertyDialog
A user clicks the E-postage Properties (Labels and Envelopes dialog box) button or Print Electronic Postage toolbar button. This event allows a third-party software application to intercept and show their properties dialog box.
MailMergeAfterMerge
After all records in a mail merge have merged successfully.
MailMergeAfterRecordMerge
After each record in the data source successfully merges in a mail merge.
MailMergeBeforeMerge
A merge is executed before any records merge.
MailMergeBeforeRecordMerge
As a merge is executed for the individual records in a merge.
MailMergeDataSourceLoad
The data source is loaded for a mail merge.
MailMergeDataSourceValidate
A user performs address verification by clicking Validate in the Mail Merge Recipients dialog box.
MailMergeWizardSendToCustom
The custom button is clicked on step six of the Mail Merge Wizard.
MailMergeWizardStateChange
A user changes from a specified step to a specified step in the Mail Merge Wizard.
NewDocument
A new document is created.
Quit
The user quits Word.
WindowActivate
Any document window is activated.
WindowBeforeDoubleClick
The editing area of a document window is double-clicked, before the default double-click action.
WindowBeforeRightClick
The editing area of a document window is right-clicked, before the default right-click action.
WindowDeactivate
Any document window is deactivated.
WindowSelectionChange
The selection changes in the active document window.
WindowSize
The application window is resized or moved.
Useful Examples of Application Event Procedures
The code presented in this article isn't really of much use to anyone except for learning how to set up application event procedures in Word. Some interesting and useful examples of application event procedures created by me and my fellow Word MVPs can be found at the following URLs:
How can I prevent users from editing the header of a document in Word 2000 or higher?
Writing application event procedures
How to create global event procedures similar to AutoOpen, AutoNew and AutoClose, without using Normal.dot
Running a macro automatically when Word starts or quits
Intercepting events like Save and Print
No comments:
Post a Comment
Your Comment Please!