Visual Studio Tools for Office: Using Visual Basic 2005 with Excel, Word, Outlook, and InfoPath

Understanding the events in the Word object model is critical, because this is often the primary way that your code is run. This chapter covers all the events in the Word object model, when they are raised, and the type of code you might associate with these events.

Some of the events in the Word object model are repeated on the Application and Document objects. This repetition allows you to decide whether you want to handle the event for all documents or for a particular document. If you want to know when any document is closed, for example, you would handle the Application object's DocumentBeforeClose event. If you want to know when a particular document is closed, you would handle the Close event on a particular Document object. When an event is repeated on the Application and Document object, it is raised first on the Document object and then on the Application object.

Advanced Topic: Why Are There Multiple Application and Document Event Interfaces?

When you work with the Word Object model, you will quickly notice multiple public interfaces, classes, and delegates that contain the text "ApplicationEvents" and "DocumentEvents":

  • ApplicationEvents Interface

  • ApplicationEvents_Event Interface

  • ApplicationEvents_SinkHelper class

  • ApplicationEvents2 Interface

  • ApplicationsEvents2_Event Interface

  • ApplicationEvents2_* Delegates

  • ApplicationEvents2_SinkHelper class

  • ApplicationEvents3 Interface

  • ApplicationsEvents3_Event Interface

  • ApplicationEvents3_* Delegates

  • ApplicationEvents3_SinkHelper class

  • ApplicationEvents4 Interface

  • ApplicationsEvents4_Event Interface

  • ApplicationEvents4_* Delegates

  • ApplicationEvents4_SinkHelper class

  • DocumentEvents Interface

  • DocumentEvents_Event Interface

  • DocumentEvents_* Delegates

  • DocumentEvents_SinkHelper class

  • DocumentEvents2 Interface

  • DocumentEvents2_Event Interface

  • DocumentEvents2_* Delegates

  • DocumentEvents2_SinkHelper class

The only items from this list that you should ever use in your code are the ones in bold: ApplicationEvents4_Event interface, the ApplicationEvents4_* delegates, the DocumentEvents2_Event interface, and the DocumentEvents2_* delegates. You should use the ApplicationEvents4_Event interface and the DocumentEvents2_Event interface only when you have to cast an object declared as Application or Document to the corresponding event interface because a method name and event name collide. An example of this is the Document object that has both a Close method and a Close event. To distinguish between the two, you will have to cast the Document object to the DocumentEvents2_Event interface when you want to handle the Close event.

The reason for the other items in this list is partially explained in Chapter 1, "An Introduction to Office Programming." This explanation, however, covers only the existence of the SinkHelper class and why there are both an ApplicationEvents/DocumentEvents interface and an ApplicationEvents_Event/DocumentEvents_Event interface. The reason why there are multiple numbered event interfaces goes back to the original COM implementation of the Word object model.

The Word Application and Document COM objects are defined by the IDL definition shown in Listing 7.1. Note that the Application object has four event interfaces and Document has two. ApplicationEvents4 is the default event interface for Word's Application object, and DocumentEvents2 is the default event interface for Word's Document object. ApplicationEvents, ApplicationEvents2, ApplicationEvents3, and DocumentEvents are supported for legacy purposes. Word had to keep these older interfaces in place for backward-compatibility reasons because older versions of Word used these interfaces.

Listing 7.1. The IDL Definition of Word's Application and Document Objects

[ uuid(000209FF-0000-0000-C000-000000000046), ] coclass Application { [default] interface _Application; [source] dispinterface ApplicationEvents; [source] dispinterface ApplicationEvents2; [source] dispinterface ApplicationEvents3; [default, source] dispinterface ApplicationEvents4; }; [ uuid(00020906-0000-0000-C000-000000000046), ] coclass Document { [default] interface _Document; [source] dispinterface DocumentEvents; [default, source] dispinterface DocumentEvents2; };

Visual Studio Generation of Event Handlers

As you consider some of the code in this chapter, you might wonder how you will ever remember the syntax of complicated lines of code such as this one:

Private Sub App_DocumentBeforeClose( _ ByVal document As Word.Document, _ ByRef cancel As Boolean) _ Handles app.DocumentBeforeClose

Fortunately, Visual Studio 2005 helps by generating this code for you. When you have declared the app member variable as having events by using the WithEvents keyword, Visual Studio displays the app variable in the left drop-down list of the code editor. Select app from the left drop-down list; then select the event that is raised by app that you want to handle from the right drop-down listin this case, DocumentBeforeClose (see Figure 7.1). When you select the event you want to handle, Visual Studio generates the event handler method automatically.

Figure 7.1. Visual Studio generates event handler code for you if you use the left and right drop-down lists in the code editor.

If you are using VSTO, you can also use the Properties window to add event handlers to your document class. Double-click the project item for your document class. Make sure the Properties window is visible; if it is not, choose Properties Window from the View menu. Make sure that the document class (typically called ThisDocument) is selected in the combo box at the top of the Properties window. Then click the lightning-bolt icon to show events associated with the document. Type the name of the method you want to use as an event handler in the edit box to the right of the event you want to handle.

Figure 7.2. shows the Properties window and an event handler we have added by typing the text ThisDocument_New in the edit box next to the New event. This will cause the New event to be handled by a method called ThisDocument_New in the document class. If the method does not already exist, Visual Studio will add the method for you.

Figure 7.2. Adding a document event handler using the Properties window in VSTO.

Startup and Shutdown Events

Several events are raised when the application is started and shut down. The Word Application object has a Startup event that is raised when the application starts and before any documents are loaded. This event is marked as "restricted" in the COM object model, however, and probably should not be used. The only kind of customization that can handle this event is an add-in. The event is raised before documents are loaded and before an automation executable can establish an event handler. Even add-ins do not need to use this event because they already implement OnConnection, which serves the same purpose. Our recommendation is that you not use the Application object's Startup event.

For VSTO customizations, we recommend that you use the Startup and Shutdown events raised by VSTO on a document project item. Startup is raised when the document is opened or created from a template. Shutdown is raised when the document is closed. In the project item created for you by VSTO, these events are already connected for you, as shown in Listing 7.2.

Listing 7.2. A VSTO Customization That Handles the Startup and Shutdown Events

Public Class ThisDocument Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup End Sub Private Sub ThisDocument_Shutdown(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Shutdown End Sub End Class

Word raises the Quit event when the application shuts down. Listing 7.3 shows an example of handling the Quit event.

Note

Quit is the name of both a method and an event on Word's Application object. Because of this collision, you will have to use the CType operator to cast the Application object to the ApplicationEvents4_Event interface when adding an event handler dynamically using the AddHandler statement. If you are adding an event handler declaratively using With Events and Handles as in Listing 7.3, you do not have to worry about this issue.

Listing 7.3. A VSTO Customization That Handles the Quit Event

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub App_Quit() Handles app.Quit MsgBox("Quit Event Raised") End Sub End Class

New and Open Document Events

Word raises a NewDocument event on the Application object and a New event on a Document object when a new document is created by the user either as a blank document or from a template or existing document. These events are never raised on subsequent opens of the document. Word also raises a DocumentOpen event on the Application object and an Open event on a Document object when an existing document is opened:

  • Application.NewDocument is raised whenever a document is created. Word passes the Document that was created as a parameter to this event.

  • Document.New is raised on a template or a new blank document. So, for example, when a document is created from a template, you can handle the New event to set up the document for the first time. For subsequent opens of the document, you can handle the Open event or the Startup event raised by VSTO.

  • Application.DocumentOpen is raised whenever an existing document is opened. Word passes the Document that was opened as a parameter to this event.

  • Document.Open is raised on an existing document when it is opened.

Listing 7.4 shows a VSTO customization that handles the Application object's NewDocument event and puts into the footer of every new document created in Word the date the document was created and the name of the user who created the document. It also handles the Application object's DocumentOpen event to put into the header of an existing document that is opened the date the document was opened and the name of the user who opened the document.

Listing 7.4. A VSTO Customization That Handles the Application Object's NewDocument and DocumentOpen Events

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub App_NewDocument(ByVal document As Word.Document) _ Handles app.NewDocument MsgBox(String.Format("NewDocument event on {0}", _ document.Name)) Dim range1 As Word.Range = document.Sections(1).Footers( _ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary).Range range1.Text = String.Format("Created on {0} by {1}.", _ System.DateTime.Now, app.UserName) End Sub Private Sub App_DocumentOpen(ByVal document As Word.Document) _ Handles app.DocumentOpen MsgBox(String.Format("DocumentOpen event on {0}", _ document.Name)) Dim range2 As Word.Range = document.Sections(1).Headers( _ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary).Range range2.Text = String.Format("Last opened on {0} by {1}.",_ System.DateTime.Now, app.UserName) End Sub End Class

Listing 7.5 shows VSTO code behind a template that handles the Document object's New event to display the time in the footer when the document is created from a template. It also handles the Document object's Open event to put into the header the date and user who last opened the document each time the document is opened.

To understand this listing, it is important to understand how Word templates work in VSTO. You should write handlers for the Document object's New event only in a template project. When a user creates a new document from that template, the code associated with the template will be associated with the newly created document, and the New event will be raised on the newly created document.

Also note that because the New event conflicts with the New keyword in Visual Basic, the Handles clause puts the New event in square brackets so the compiler knows that New is being used as an event name rather than a keyword.

Listing 7.5. A VSTO Customization That Handles the Document Object's New and Open Events

Public Class ThisDocument Private Sub ThisDocument_New() Handles Me.[New] MsgBox("New event") Dim range1 As Word.Range = Me.Sections(1).Footers( _ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary).Range range1.Text = String.Format("Created on {0} by {1}.", _ System.DateTime.Now, Me.Application.UserName) End Sub Private Sub ThisDocument_Open() Handles Me.Open MsgBox("Open event") Dim range2 As Word.Range = Me.Sections(1).Headers( _ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary).Range range2.Text = String.Format("Opened on {0} by {1}.", _ System.DateTime.Now, Me.Application.UserName) End Sub End Class

Document Close Events

Word raises events when a document is closed. The DocumentBeforeClose event is raised on the Application object before the document closes, which allows the handler to cancel the closing of the document. The Close event raised on the Document object does not allow canceling the closing of the document.

Unfortunately, the Close event is raised even in cases where the document is not really going to close. The event is raised before a dialog box is shown to the user prompting the user to save the document. Users are asked whether they want to save with a Yes, No, and Cancel button. If the user selects Cancel, the document remains open even though a Close event was raised. It is also possible for another add-in to handle the DocumentBeforeClose event and cancel the close of the document. For this reason, it is better to use VSTO's Shutdown event on the document, which is not raised until after the user and any handlers of the DocumentBeforeClose event have been given a chance to cancel the closing of the document.

The following events are raised when documents are about to be closed:

  • Application.DocumentBeforeClose is raised before a document is closed. Word passes the Document that is about to close as a parameter to this event. It also passes by reference a Boolean cancel parameter. The cancel parameter can be set to true by your event handler to prevent Word from closing the document.

  • Document.Close is raised when a document is about to be closed. As discussed earlier, however, the user can still cancel the closing of the document, so you cannot trust this event to tell you whether the document is going to close. Use VSTO's Shutdown event instead.

Note

Close is the name of both a method and an event on Word's Document object. Because of this collision, you will have to use the CType operator to cast the Document object to the DocumentEvents2_Event interface when adding an event handler dynamically using the AddHandler statement. If you are adding an event handler declaratively using WithEvents and Handles, you do not have to worry about this issue.

Listing 7.6 shows a VSTO customization that handles the Application object's DocumentBeforeClose event and the Document object's Close event. In the handler of the DocumentBeforeClose event, the code checks to see whether the document contains any spelling errors. If it does, a dialog box displays the number of spelling errors, and the user is told to correct them before closing the document. The cancel parameter is set to TRue to prevent the document from closing. Another thing to try when running this code is to click the Cancel button when you are prompted to save and then observe that the Document object's Close event fires in this case.

Listing 7.6. A VSTO Customization That Handles the Application Object's DocumentBeforeClose Event and the Document Object's Close Event

Public Class ThisDocument Private WithEvents app As Word.Application Private WithEvents doc As Word.Document Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application doc = app.Documents.Add() doc.Range.Text = "Lawts uf spellin errers!" End Sub Private Sub Doc_Close() Handles doc.Close MsgBox("Thanks for fixing the spelling errors.") End Sub Private Sub App_DocumentBeforeClose(_ ByVal document As Word.Document, _ ByRef cancel As Boolean) Handles app.DocumentBeforeClose Dim spellingErrors As Integer = document.SpellingErrors.Count If spellingErrors > 0 Then MsgBox(String.Format( _ "There are still {0} spelling errors.", _ spellingErrors)) cancel = True End If End Sub End Class

Document Save Events

Word raises the DocumentBeforeSave event on the Application object before any document is saved. Word passes the Document that is about to be saved as a parameter to this event. It also passes by reference a Boolean saveAsUI parameter and a Boolean cancel parameter. If you set saveAsUI to true, the Save As dialog box displays for the document. If you set the cancel parameter to true, the save will be canceled. Often, this event is handled to implement a custom save routine You might cancel the DocumentBeforeSave event but call the SaveAs method on Document to enforce a particular file format, for example.

Note that the DocumentBeforeSave event is also raised when Word does an AutoSave on a document. You should be careful that you test your code to make sure that it works properly when AutoSave is triggered.

Listing 7.7 shows a VSTO customization that handles the DocumentBeforeSave event. If the document contains any spelling errors, the event handler cancels the save by setting the cancel parameter to true. It also sets the saveAsUI parameter to true to force a Save As dialog box to be shown for every save. When the DocumentBeforeSave event is triggered for an AutoSave, the dialog box shown in Figure 7.3 displays.

Figure 7.3. The message displayed by Word when an automatic save is canceled.

Listing 7.7. A VSTO Customization That Handles the Application Object's DocumentBeforeSave Event

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub App_DocumentBeforeSave( _ ByVal document As Word.Document, _ ByRef saveAsUI As Boolean, ByRef cancel As Boolean) _ Handles app.DocumentBeforeSave saveAsUI = True If document.SpellingErrors.Count > 0 Then MsgBox( _ "You shouldn't save a document with spelling errors.") cancel = True End If End Sub End Class

Document Activation Events

Word raises several events on the Application object when the active document changes. One such event is the DocumentChange event. The name DocumentChange makes you think that maybe this event will tell you when the contents of the document change; unfortunately, Word does not have a general event that tells you this.

The active document changes when you create a new document; the new document becomes the active document. The active document changes when you open an existing document; the document you opened becomes the active document. The active document changes when you switch between open documents by clicking a document that is not active or by selecting a document using the Window menu or the Windows taskbar.

It is also possible to have multiple windows viewing the same documentbecause the user chose New Window from the Window menu, for example. Word raises an event called WindowActivate that tells you when a particular window becomes the active window and an event called WindowDeactivate when a particular window is deactivated. Unlike in Excel, switching to another application causes Word's WindowDeactivate event to be raised, and switching back to Word causes the WindowActivate event to be raised.

The following events are raised when windows are activated and deactivated:

  • Application.DocumentChange is raised when the active document changes (not when the contents of the document change). Word passes no parameters to this event. To determine the new active document, you must use the Application object's ActiveDocument property.

  • Application.WindowActivate is raised when a Word window is activated. This can occur when the user switches between windows within Word or when the user switches to another application and then switches back to Word. Word passes the Document associated with the window that was activated as a parameter to this event. Word also passes the Window that was activated as a parameter to this event.

  • Application.WindowDeactivate is raised when a Word window is deactivated. This can occur when the user switches between windows within Word or when the user switches to another application. Word passes the Document associated with the window that was deactivated as a parameter to this event. Word also passes the Window that was deactivated as a parameter to this event.

Listing 7.8 shows a VSTO customization that handles the DocumentChange, WindowActivate, and WindowDeactivate events and displays a message box when these events are raised.

Listing 7.8. A VSTO Customization That Handles the Application Object's WindowActivate, WindowDeactivate, and DocumentChange Events

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub App_WindowActivate( _ ByVal document As Word.Document, _ ByVal window As Word.Window) Handles app.WindowActivate MsgBox(String.Format("Window {0} was activated.", _ window.Caption)) End Sub Private Sub App_WindowDeactivate( _ ByVal document As Word.Document, _ ByVal window As Word.Window) Handles app.WindowDeactivate MsgBox(String.Format("Window {0} was deactivated.", _ window.Caption)) End Sub Private Sub App_DocumentChange() Handles app.DocumentChange MsgBox(String.Format("The active document is now {0}.",_ app.ActiveDocument.Name)) End Sub End Class

Document Print Events

Word raises a DocumentBeforePrint event on the Application object before a document is printed. Word passes the Document that is about to be printed as a parameter to this event. It also passes by reference a Boolean cancel parameter. If you set the cancel parameter to true, the default printing of the document will be canceled. Often, this event is handled to implement a custom print routine. You might cancel Word's default print behavior and use the PrintOut method on Document to enforce a certain print format, for example.

Listing 7.9 shows a VSTO customization that handles the DocumentBeforePrint event to enforce some custom print settings. It forces two copies to be printed and collation to be turned on when the user prints the document.

Listing 7.9. A VSTO Customization That Handles the Application Object's DocumentBeforePrint Event

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub app_DocumentBeforePrint( _ ByVal document As Word.Document, _ ByRef cancel As Boolean) Handles app.DocumentBeforePrint ' Print 2 copies and collate. document.PrintOut(Copies:=2, Collate:=True) ' Cancel because we printed already ' and don't want Word to print again. cancel = True End Sub End Class

Mouse Events

Word raises events when the user right-clicks or double-clicks the document area of a window. If the user right-clicks or double-clicks in area of the window such as the ruler or the scroll bar, no events are raised.

The following events are raised when double-clicks or right-clicks occur:

  • Application.WindowBeforeDoubleClick is raised when the document area of a window is double-clicked. Word passes the selection that was double-clicked. This can be a range of text or other objects in the document such as a shape. Word also passes by reference a Boolean cancel parameter. The cancel parameter can be set to TRue by your event handler to prevent Word from performing the default action associated with a double-click.

  • Application.WindowBeforeRightClick is raised when the document area of a window is right-clicked. Word passes the selection that was right-clicked. This can be a range of text or other objects in the document such as a shape. Word also passes by reference a Boolean cancel parameter. The cancel parameter can be set to true by your event handler to prevent Word from performing the default action associated with a right-click.

Listing 7.10 shows a VSTO customization that handles the WindowBeforeDoubleClick and WindowBeforeRightClick events. When the document is double-clicked, this application sets the selected range of text to be all caps. The range of text that is selected depends on where the user double-clicked. If the user double-clicks a word, the selection changes to be the word. If the user triple-clicks, the selection changes to be a paragraph. If the user double-clicks the page margin, the selection changes to be the line next to where the user double-clicked.

When a range of text is right-clicked, this customization sets the range of text to be title case. Finally, if you double-click a shape in the document, the color is set to dark red. We also set cancel to true to prevent the shape Properties dialog box from being shown when a shape is double-clicked and to prevent the right-click menu from appearing when a range of text is right-clicked.

Listing 7.10. A VSTO Customization That Handles the Application Object's WindowBeforeDoubleClick and WindowBeforeRightClick Events

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub App_WindowBeforeRightClick( _ ByVal selection As Word.Selection, ByRef cancel As Boolean) _ Handles app.WindowBeforeRightClick If selection.Type = Word.WdSelectionType.wdSelectionNormal _ Then selection.Range.Case = Word.WdCharacterCase.wdTitleWord cancel = True End If End Sub Private Sub App_WindowBeforeDoubleClick( _ ByVal selection As Word.Selection, _ ByRef cancel As Boolean) _ Handles app.WindowBeforeDoubleClick If selection.Type = Word.WdSelectionType.wdSelectionNormal _ Then selection.Range.Case = Word.WdCharacterCase.wdUpperCase ElseIf selection.Type = _ Word.WdSelectionType.wdSelectionShape Then selection.ShapeRange.Fill.ForeColor.RGB = 3000 cancel = True End If End Sub End Class

Selection Events

Word raises several events when the selection changes in the active document:

  • Application.WindowSelectionChange is raised when the selection in a document changes. This event is also raised when the location of the insertion point changes within the document because of clicking with the mouse or moving via navigation keys (such as Page Up and Page Down). This event is not raised when the insertion point is moved as a result of typing new text in the document. Word passes a Selection object representing the new selection as a parameter to this event. If only the insertion point has moved, and no range of text is selected, the Selection object will be passed as a one-character-long Range object containing the character after the current location of the insertion point, and the Type property of the Selection object will return WdSelectionType.wdSelectionIP.

  • Application.XMLSelectionChange is raised when the selected XML element changes in a document with XML mappings. Chapter 22, "Working with XML in Word," discusses using XML mappings in Word. Word passes the new Selection object as a parameter to this event. It also passes the old XMLNode object that was selected previously and the XMLNode object that is now selected. It also passes a reason for the selection change of type WdXMLSelectionChange, which can be wdXMLSelectionChangeReasonDelete, wdXMLSelectionChangeReasonInsert, or wdXMLSelectionChangeReasonMove.

Listing 7.11 shows a VSTO customization that uses the Range.Start and Range.End properties to display the start and end locations of the selection. The code first checks whether the selection type is wdSelectionIP or wdSelectionNormal. It also prints the selection type using a helpful feature of Visual Studio; when you use the ToString method associated with an enumerated type, it displays the string name of the enumeration instead of just displaying a number.

Listing 7.11. A VSTO Customization That Handles the Application Object's WindowSelectionChange Event

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application End Sub Private Sub App_WindowSelectionChange( _ ByVal selection As Word.Selection) Handles app.WindowSelectionChange Dim selType As Word.WdSelectionType = selection.Type MsgBox(String.Format("Selection type is {0}.", _ selType.ToString())) If selType = Word.WdSelectionType.wdSelectionIP Or _ selType = Word.WdSelectionType.wdSelectionNormal Then MsgBox(String.Format("Start is {0} and End is {1}.", _ selection.Range.Start, selection.Range.End)) End If End Sub End Class

Window Sizing Events

Word raises a WindowSize event on the Application object when a window associated with a document is resized. Once again, the behavior of this event is different from the window-sizing event in Excel. The WindowSize event in Word is raised even when the document window is maximized to fill the Word application window and the Word application window is resized. The event is not raised for the Word application window when it is resized and no documents are open.

Word passes the Document object associated with the window that was resized as a parameter to this event. Word also passes the Window object for the window that was resized.

XML Events

Word raises several events when XML elements have been mapped into the document using the XML Structure feature of Word. You have already learned about the Application object's XMLSelectionChange that is raised when the selection changes from one XML element to another. Chapter 22, "Working with XML in Word," considers Word's XML features in more detail.

The following events are raised as part of the XML Structure feature of Word:

  • Application.XMLValidationError is raised when the XML in the document is not valid when compared with the schema associated with the document. Word passes the XMLNode object corresponding to the invalid element as a parameter to this event.

  • Document.XMLAfterInsert is raised after the user adds a new XML element to the document. If multiple XML elements are added at the same time, the event will be raised for each element that was added. Word passes the XMLNode object for the newly added element as a parameter to this event. It also passes a Boolean inUndoRedo parameter that indicates whether the XML element was added because undo or redo was invoked.

  • Document.XMLBeforeDelete is raised when the user deletes an XML element from the document. If multiple XML elements are removed at the same time, the event will be raised for each element that was removed. Word passes a Range object representing the range of text that was deleted. If an element was deleted without any text being deleted, the Range will be passed as Nothing. Word also passes the XMLNode object that was deleted and a Boolean inUndoRedo parameter that indicates whether the XML element was deleted because undo or redo was invoked.

Sync Events

Word raises the Document object's Sync event when a local document is synchronized with a copy on the server using Word's document workspace feature. Word passes a parameter of type MsoSyncEventType that gives additional information on the status of the document synchronization.

E-Postage Events

Word supports a feature called electronic postage, which enables you to create an envelope or label with printed postage that is printed on an envelope or package along with the address. Figure 7.4 shows the Envelopes and Labels dialog box, which has an Add Electronic Postage check box and an E-Postage Properties button that are used to configure electronic postage. Word provides three events to allow third parties to create an e-postage add-in: EPostageInsert, EPostageInsertEx, and EPostagePropertyDialog. An e-postage add-in is distinguished from other Word add-ins by a special registry key. There can be only one active e-postage add-in installed in Word. This book does not consider these events further because it is unlikely that you will ever need to create your own electronic postage add-in. You can read more about e-postage add-ins by downloading the e-postage SDK at http://support.microsoft.com/?kbid=304095.

Figure 7.4. The Envelopes and Labels dialog box with electronic postage options.

Mail Merge Events

Word raises eight events associated with the mail merge feature. To understand these events, you must first understand how mail merge works and when and why each of these events is raised.

The user starts a mail merge by choosing Tools > Letters and Mailings > Mail Merge. This causes the Application object's MailMergeWizardStateChange event to be raised, notifying us that we are moving from Step 0 to Step 1 of the Mail Merge Wizard. Then the Mail Merge task pane shown in Figure 7.5 displays. The Mail Merge task pane is a wizard that can move back and forth through six steps. Whenever we move from step to step, the MailMergeWizardStateChange event is raised. When we close the document, the MailMergeWizardStateChange event is raised, moving from Step 6 back to Step 0.

Figure 7.5. Step 1 of the Mail Merge Wizard.

Step 2 is not shown here; it prompts us as to whether we want to start from the current document or from a template or existing document on disk. In Step 2, we will choose to use the current document. When we get to Step 3 of the Mail Merge Wizard, we are prompted to select a data source for the mail merge. Figure 7.6 shows Step 3.

Figure 7.6. Step 3 of the Mail Merge Wizard.

We choose Use an Existing List and click the Browse link to locate an Access database we have previously created called Authors.mdb. Figure 7.7 shows the dialog box for picking a data source.

Figure 7.7. Selecting a data source.

After we select the data source and click Open, the Application object's MailMergeDataSourceLoad event is raised. This event lets us know that a data source has been chosen, and now we can examine the data source through the object model. After the MergeDataSourceLoad event has been raised, the Mail Merge Recipients dialog box appears, as shown in Figure 7.8. This dialog box shows each record in the data source and lets you further control which records you want to use for the mail merge.

Figure 7.8. The Mail Merge Recipients dialog box.

The Mail Merge Recipients dialog box has a button called Validate. When clicked, this button raises the Application object's DataSourceValidate event. It raises this event only for the special e-postage add-in described earlier in this chapter, however.

Step 4 of the Mail Merge Wizard lets you insert address blocks, greeting blocks, and other fields into the body of your document. Step 5 lets you preview the final look of your document when Word loads the data from your data source into the blocks you have defined.

Step 6 displays two actions you can take to complete the mail merge. The first is to print the generated letters. The second is to create a new document and insert each letter into the new document. You can also specify a third action by writing a line of code such as the following before Step 6 of the wizard is shown:

document.MailMerge.ShowSendToCustom = "My Custom Action..."

The MailMerge object's ShowSendToCustom property takes a String value and allows you to add a third custom action defined by your code to do at the end of a mail merge. When the user clicks this custom action, the Application object's MailMergeWizardSendToCustom event is raised. Figure 7.9 shows Step 6 of the Mail Merge Wizard with a custom action called My Custom Action.

Figure 7.9. Step 6 of the Mail Merge Wizard.

When the user chooses Print or Edit Individual Letters, the Application object's MailMergeBeforeMerge event is raised. Word passes the start record and the end record that will be merged as Integer parameters. The default is to merge all the records. When all the records are going to be merged, Word passes 1 for the start record and 16 for the end record. Word also passes by reference a Booleancancel parameter. If you set the cancel parameter to true, the mail merge will be canceled.

After the MailMergeBeforeMerge event is raised, Word shows a dialog box letting the user change the records to merge, as shown in Figure 7.10. Unfortunately, if the user changes the records to be merged, Word does not raise the MailMergeBeforeMerge event again. The next time the user does a mail merge, the user's last selection in the dialog box will be reflected in the parameters passed to MailMergeBeforeMerge.

Figure 7.10. Selecting the records to merge.

When the user clicks the OK button in the dialog box shown in Figure 7.10, the mail merge begins in earnest. Before Word merges a record from the data source to create a letter, it first raises the Application object's MailMergeBeforeRecordMerge event; then it creates the letter from the record and raises the Application object's MailMergeAfterRecordMerge event when the letter for the record has been generated. This sequence of MailMergeBeforeRecordMerge followed by MailMergeAfterRecordMerge repeats for each record that is going to be merged. When all the records have been merged, Word raises the Application object's MailMergeAfterMerge event and passes the newly created Document object as a parameter if the user chose Edit Individual Letters in Figure 7.9. If the user chose Print, Nothing will be passed for the newly created document.

Listing 7.12 shows a VSTO customization that handles all the mail merge events.

Listing 7.12. A VSTO Customization That Handles Mail Merge Events

Public Class ThisDocument Private WithEvents app As Word.Application Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application ' Set ShowSendToCustom so that a custom command ' can raise the MailMergeWizardSendToCustom event Me.MailMerge.ShowSendToCustom = "My Custom Command" End Sub Private Sub App_MailMergeAfterMerge( _ ByVal document As Word.Document, _ ByVal documentResult As Word.Document) _ Handles app.MailMergeAfterMerge MsgBox(String.Format("MailMergeAfterMerge: Source = {0}, _ Result = {1}", document.Name, documentResult.Name)) End Sub Private Sub App_MailMergeAfterRecordMerge( _ ByVal document As Word.Document) _ Handles app.MailMergeAfterRecordMerge MsgBox(String.Format("MailMergeAfterRecordMerge for {0}",_ document.Name)) End Sub Private Sub App_MailMergeBeforeMerge( _ ByVal document As Word.Document, _ ByVal startRecord As Integer, _ ByVal endRecord As Integer, ByRef cancel As Boolean) _ Handles app.MailMergeBeforeMerge MsgBox(String.Format("MailMergeBeforeMerge for {0}", _ document.Name)) ' Word passes -16 as the EndRecord if the user ' chose to merge all records. If endRecord = -16 Then endRecord = document.MailMerge.DataSource.RecordCount End If MsgBox(String.Format("Merging record {0} to record {1}.",_ startRecord, endRecord)) End Sub Private Sub App_MailMergeBeforeRecordMerge( _ ByVal document As Word.Document, ByRef cancel As Boolean) _ Handles app.MailMergeBeforeRecordMerge MsgBox(String.Format( _ "MailMergeBeforeRecordMerge for {0}.", _ document.Name)) End Sub Private Sub App_MailMergeDataSourceLoad( _ ByVal document As Word.Document) Handles app.MailMergeDataSourceLoad MsgBox(String.Format("MailMergeDataSourceLoad for {0}.",_ document.Name)) MsgBox(String.Format("The data source is {0}.", _ document.MailMerge.DataSource.Name)) End Sub ' This event won't fire except for an e-postage add-in Private Sub App_MailMergeDataSourceValidate( _ ByVal document As Word.Document, ByRef handled As Boolean) _ Handles app.MailMergeDataSourceValidate MsgBox(String.Format( _ "MailMergeDataSourceValidate for {0}.", _ document.Name)) End Sub Private Sub App_MailMergeWizardSendToCustom( _ ByVal document As Word.Document) _ Handles app.MailMergeWizardSendToCustom MsgBox(String.Format( _ "MailMergeWizardSendToCustom for {0}.", _ document.Name)) End Sub Private Sub App_MailMergeWizardStateChange( _ ByVal document As Word.Document, _ ByRef fromState As Integer, _ ByRef toState As Integer, ByRef handled As Boolean) _ Handles app.MailMergeWizardStateChange MsgBox(String.Format( _ "MailMergeWizardStateChange for {0}.", _ document.Name)) End Sub End Class

CommandBar Events

A common way to run your code is to add a custom toolbar button or menu item to Word and handle the click event raised by that button or menu item. Word uses the same object model as Excel to add toolbar buttons and menu items. Chapter 4, "Working with Excel Events," discusses this model in more detail.

One difference between Excel and Word is that Word can save an added toolbar or menu item in a template or a document. The default location that a new toolbar or menu item is saved to is the Normal template (normal.dot). You can specify that the new toolbar or menu item be associated with another template or with a document by using the Application object's CustomizationContext property. The CustomizationContext property takes an Object that is either a Template object or a Document object. Subsequent calls to add toolbars or buttons (a CommandBarButton, for example) will be saved in the template or document you set using the CustomizationContext property.

Listing 7.13 shows a listing similar to the Excel example in Listing 4.9 in Chapter 4, with two significant differences. First, we use the CustomizationContext property to make it so the toolbar we add will be associated with a particular document. Second, we pass true as the last parameter to the various Add methods so that the CommandBar, CommandBarButton, and CommandBarComboBox are added permanently rather than temporarily.

Listing 7.13. A VSTO Customization That Adds a Custom CommandBar

Public Class ThisDocument Private WithEvents app As Word.Application Private WithEvents btn As Office.CommandBarButton Private WithEvents box As Office.CommandBarComboBox Private Sub ThisDocument_Startup(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Startup app = Me.Application ' Store the new command bar in this document. app.CustomizationContext = app.ActiveDocument Dim bars As Office.CommandBars = Me.CommandBars Dim bar As Office.CommandBar = bars.Add("My Custom Bar",_ Temporary:=True) bar.Visible = True btn = bar.Controls.Add( _ Office.MsoControlType.msoControlButton, _ Temporary:=True) btn.Caption = "Display Message" btn.Tag = "WordDocument1.btn" btn.Style = Office.MsoButtonStyle.msoButtonCaption box = bar.Controls.Add( _ Office.MsoControlType.msoControlComboBox, _ Temporary:=True) box.Tag = "WordDocument1.box" box.AddItem("Choice 1", 1) box.AddItem("Choice 2", 2) box.AddItem("Choice 3", 3) End Sub Private Sub Btn_Click(ByVal ctrl As Office.CommandBarButton, _ ByRef cancelDefault As Boolean) Handles btn.Click MsgBox("You clicked the button.") End Sub Private Sub Box_Change( _ ByVal ctrl As Office.CommandBarComboBox) _ Handles box.Change MsgBox(String.Format("You selected {0}.", ctrl.Text)) End Sub End Class

Категории