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. For example, if you want to know when any document is closed, 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 the Application object.
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":
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 only use the ApplicationEvents4_Event interface and the DocumentEvents2_Event interface 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 disambiguate 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." However, this explanation only covers the existence of the SinkHelper class and why there is both an ApplicationEvents/DocumentEvents interface and an ApplicationEvents_Event/DocumentEvents_Event interface. The reason 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 the code in some of the listings in this chapter, you might wonder how you will ever remember the syntax of complicated lines of code such as this one:
application.DocumentBeforeClose += new Word.ApplicationEvents4_DocumentBeforeCloseEventHandler( app_DocumentBeforeClose);
Fortunately, Visual Studio helps by generating most of this line of code as well as the corresponding event handler automatically. If you were typing this line of code, after you type +=, Visual Studio displays a pop-up tooltip, as shown in Figure 7-1. If you press the Tab key twice, Visual Studio generates the rest of the line of code and the event handler method (app_DocumentBeforeClose) automatically.
Figure 7-1. Visual Studio generates event handler code for you if you press the Tab key.
If you are using Visual Studio Tools for Office (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 to show the Properties window. 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 Property 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 is started up before any documents are loaded. However, this event is marked as "restricted" in the COM object model and probably should not be used at all. 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 not to 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. The InternalStartup method shown in Listing 7-2 is used by VSTO to connect the Startup and Shutdown event handlers as well as other events you might add using VSTO.
Listing 7-2. A VSTO Customization That Handles the Startup and Shutdown Events
public partial class ThisDocument { private void ThisDocument_Startup(object sender, EventArgs e) { } private void ThisDocument_Shutdown(object sender, EventArgs e) { } private void InternalStartup() { this.Startup += new System.EventHandler(ThisDocument_Startup); this.Shutdown += new System.EventHandler(ThisDocument_Shutdown); } }
Word raises the Quit event when the application shuts down. Listing 7-3 shows an example of handling the Quit event. Quit is the name of both a method and an event on Word's Application object. Because of this collision, you will not see the Quit event in Visual Studio's pop-up menu of properties, events, and methods associated with the Application object. Furthermore, a warning displays at compile time when you try to handle this event. To get Visual Studio's pop-up menus to work and the warning to go away, you can cast the Application object to the ApplicationEvents4_Event interface, as shown in Listing 7-3.
Listing 7-3. A VSTO Customization That Handles the Quit Event
public partial class ThisDocument { Word.Application app; private void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; ((Word.ApplicationEvents4_Event)app).Quit += new Word.ApplicationEvents4_QuitEventHandler( App_Quit); } void App_Quit() { MessageBox.Show("Quit Event Raised"); } private void InternalStartup() { this.Startup += new System.EventHandler(ThisDocument_Startup); } }
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 first created by the user either as a blank document or from a template or existing document. These events are never raised again 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:
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 partial class ThisDocument { Word.Application app; private void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; ((Word.ApplicationEvents4_Event)app).NewDocument += new Word.ApplicationEvents4_NewDocumentEventHandler( App_NewDocument); app.DocumentOpen += new Word.ApplicationEvents4_DocumentOpenEventHandler( App_DocumentOpen); } void App_NewDocument(Word.Document document) { MessageBox.Show(String.Format( "NewDocument event on {0}", document.Name)); Word.Range range = document.Sections[1].Footers[ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range; range.Text = String.Format("Created on {0} by {1}.", System.DateTime.Now, app.UserName); } void App_DocumentOpen(Word.Document document) { MessageBox.Show(String.Format( "NewDocument event on {0}", document.Name)); Word.Range range = document.Sections[1].Headers[ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range; range.Text = String.Format("Last opened on {0} by {1}.", System.DateTime.Now, app.UserName); } private void InternalStartup() { this.Startup += new System.EventHandler(ThisDocument_Startup); } }
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 first 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 only write handlers for the Document object's New event 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.
Listing 7-5. A VSTO Customization That Handles the Document Object's New and Open Events
public partial class ThisDocument { private void ThisDocument_New() { MessageBox.Show("New event"); Word.Range range = this.Sections[1].Footers[ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range; range.Text = String.Format("Created on {0} by {1}.", System.DateTime.Now, this.Application.UserName); } private void ThisDocument_Open() { MessageBox.Show("Open event"); Word.Range range = this.Sections[1].Headers[ Word.WdHeaderFooterIndex.wdHeaderFooterPrimary].Range; range.Text = String.Format("Opened on {0} by {1}.", System.DateTime.Now, this.Application.UserName); } private void InternalStartup() { this.New += new Word.DocumentEvents2_NewEventHandler( this.ThisDocument_New); this.Open += new Word.DocumentEvents2_OpenEventHandler( this.ThisDocument_Open); } }
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 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.
Close is the name of both a method and an event on the Document object. Because of this collision, you will not see the Close event in Visual Studio's pop-up menu of properties, events, and methods associated with the Document object. Furthermore, a warning displays at compile time when you try to handle this event. To get Visual Studio's pop-up menus to work and the warning to go away, you can cast the Document object to the DocumentEvents2_Event interface, as shown in Listing 7-6. |
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 if the document contains any spelling errors. If it does, a dialog displays with 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 press the Cancel button when you are prompted to save and 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 partial class ThisDocument { Word.Application app; Word.Document doc; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; doc = app.Documents.Add(ref missing, ref missing, ref missing, ref missing); doc.Range(ref missing, ref missing).Text = "Lawts uf spellin errers!"; app.DocumentBeforeClose += new Word.ApplicationEvents4_DocumentBeforeCloseEventHandler( App_DocumentBeforeClose); ((Word.DocumentEvents2_Event)doc).Close += new Word.DocumentEvents2_CloseEventHandler( Doc_Close); } void Doc_Close() { MessageBox.Show("Thanks for fixing the spelling errors."); } void App_DocumentBeforeClose(Word.Document document, ref bool cancel) { int spellingErrors = document.SpellingErrors.Count; if (spellingErrors > 0) { MessageBox.Show(String.Format( "There are still {0} spelling errors in this document.", spellingErrors)); cancel = true; } } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
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 bool saveAsUI parameter and a bool cancel parameter. If you set saveAsUI to true, the Save As dialog 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 routinefor example, you might cancel the DocumentBeforeSave event but call the SaveAs method on Document to enforce a particular file format.
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, it cancels the save by setting the cancel parameter to true. It also sets the saveAsUI parameter to TRue to force a Save As dialog to be shown for every save. When the DocumentBeforeSave event is triggered for an AutoSave, the dialog shown in Figure 7-3 displays.
Listing 7-7. A VSTO Customization That Handles the Application Object's DocumentBeforeSave Event
public partial class ThisDocument { Word.Application app; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; app.DocumentBeforeSave += new Word. ApplicationEvents4_DocumentBeforeSaveEventHandler( App_DocumentBeforeSave); } void App_DocumentBeforeSave(Word.Document document, ref bool saveAsUI, ref bool cancel) { saveAsUI = true; if (document.SpellingErrors.Count > 0) { MessageBox.Show( "You shouldn't save a document with spelling errors."); cancel = true; } } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
Figure 7-3. The message displayed by Word when an automatic save is cancelled.
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 would tell you when the contents of the document changeunfortunately, Word does not have a general event that tells you this.
The active document changes when you create a new documentthe new document becomes the active document. The active document changes when you open an existing documentthe document you opened becomes the active document. The active document changes when you switch between open documents by clicking a document that is not currently active or selecting a document using the Window menu or the Windows taskbar.
It is also possible to have multiple windows viewing the same documentfor example, because the user chose New Window from the Window menu. 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 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.
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 partial class ThisDocument { Word.Application app; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; app.WindowActivate += new Word. ApplicationEvents4_WindowActivateEventHandler( App_WindowActivate); app.WindowDeactivate += new Word. ApplicationEvents4_WindowDeactivateEventHandler( App_WindowDeactivate); app.DocumentChange += new Word. ApplicationEvents4_DocumentChangeEventHandler( App_DocumentChange); } void App_WindowActivate(Word.Document document, Word.Window window) { MessageBox.Show(String.Format( "Window {0} was activated.", window.Caption)); } void App_WindowDeactivate(Word.Document document, Word.Window window) { MessageBox.Show(String.Format( "Window {0} was deactivated.", window.Caption)); } void App_DocumentChange() { MessageBox.Show(String.Format( "The active document is now {0}.", app.ActiveDocument.Name)); } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
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 bool 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 routinefor example, you might cancel Word's default print behavior and use the PrintOut method on Document to enforce a certain print format.
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 partial class ThisDocument { Word.Application app; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; app.DocumentBeforePrint += new Word. ApplicationEvents4_DocumentBeforePrintEventHandler( app_DocumentBeforePrint); } void app_DocumentBeforePrint(Word.Document document, ref bool cancel) { // Print 2 copies and collate. object copies = 2; object collate = true; document.PrintOut(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref copies, ref missing, ref missing, ref missing, ref collate, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing); // Cancel because we printed already // and don't want Word to print again. cancel = true; } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
Mouse Events
Word raises events when the user right-clicks or double-clicks in the document area of a window. If the user right-clicks or double-clicks in an area of the window such as the ruler or the scrollbar, no events are raised.
Listing 7-10 shows a VSTO customization that handles the WindowBefore- DoubleClick 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 a dark red. We also set cancel to true to prevent the shape Properties dialog 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 WindowBefore- DoubleClick and WindowBeforeRightClick Events
public partial class ThisDocument { Word.Application app; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; app.WindowBeforeDoubleClick += new Word. ApplicationEvents4_WindowBeforeDoubleClickEventHandler( App_WindowBeforeDoubleClick); app.WindowBeforeRightClick += new Word. ApplicationEvents4_WindowBeforeRightClickEventHandler( App_WindowBeforeRightClick); } void App_WindowBeforeRightClick(Word.Selection selection, ref bool cancel) { if (selection.Type == Word.WdSelectionType.wdSelectionNormal) { selection.Range.Case = Word.WdCharacterCase.wdTitleWord; cancel = true; } } void App_WindowBeforeDoubleClick(Word.Selection selection, ref bool cancel) { if (selection.Type == Word.WdSelectionType.wdSelectionNormal) { selection.Range.Case = Word.WdCharacterCase.wdUpperCase; } else if (selection.Type == Word.WdSelectionType.wdSelectionShape) { selection.ShapeRange.Fill.ForeColor.RGB = 3000; cancel = true; } } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
Selection Events
Word raises several events when the selection changes in the active document.
Listing 7-11 shows a VSTO customization that uses the Range.Start and Range.End properties to display the start and end location 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 Studiowhen 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 partial class ThisDocument { Word.Application app; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; app.WindowSelectionChange += new Word. ApplicationEvents4_WindowSelectionChangeEventHandler( App_WindowSelectionChange); } void App_WindowSelectionChange(Word.Selection selection) { Word.WdSelectionType selType = selection.Type; MessageBox.Show(String.Format( "Selection type is {0}.", selType.ToString())); if (selType == Word.WdSelectionType.wdSelectionIP || selType == Word.WdSelectionType.wdSelectionNormal) { MessageBox.Show(String.Format( "Start is {0} and End is {1}.", selection.Range.Start, selection.Range.End)); } } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
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 than 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 opened.
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.
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.
EPostage 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 only be 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 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 Mail Merge from the Letters and Mailings menu of the Tools menu. 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. The Mail Merge task pane shown in Figure 7-5 then displays. The Mail Merge task pane is a wizard that can move back and forth through six steps. Whenever you move from step to step, the MailMergeWizardStateChange event is raised. When you 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 hereit prompts you as to whether you 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, the user is 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 to pick a data source.
Figure 7-7. Selecting a data source.
After we select the data source and choose Open, the Application object's MailMergeDataSourceLoad event is raised. This event lets us know that a data source has been chosen and we can now examine the data source through the OM. After the MergeDataSourceLoad event has been raised, the Mail Merge Recipients dialog appears, as shown in Figure 7-8. This dialog 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.
The Mail Merge Recipients dialog has a button called Validate. When clicked, this button raises the Application object's DataSourceValidate event. However, it only raises this event for the special e-postage add-in described earlier.
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 int 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 bool cancel 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 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 re-raise the MailMergeBeforeMerge event. The next time the user does a mail merge, the user's last selection in the dialog 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 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. It then 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 to Edit individual letters in Figure 7-9. If the user chose Print, null 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 partial class ThisDocument { Word.Application app; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; // Have to set ShowSendToCustom so that there is a custom command // that can be clicked to raise the MailMergeWizardSendToCustom event this.MailMerge.ShowSendToCustom = "My Custom Command"; app.MailMergeAfterMerge += new Word. ApplicationEvents4_MailMergeAfterMergeEventHandler( App_MailMergeAfterMerge); app.MailMergeAfterRecordMerge += new Word. ApplicationEvents4_MailMergeAfterRecordMergeEventHandler( App_MailMergeAfterRecordMerge); app.MailMergeBeforeMerge += new Word. ApplicationEvents4_MailMergeBeforeMergeEventHandler( App_MailMergeBeforeMerge); app.MailMergeBeforeRecordMerge += new Word. ApplicationEvents4_MailMergeBeforeRecordMergeEventHandler( App_MailMergeBeforeRecordMerge); app.MailMergeDataSourceLoad += new Word. ApplicationEvents4_MailMergeDataSourceLoadEventHandler( App_MailMergeDataSourceLoad); app.MailMergeDataSourceValidate += new Word. ApplicationEvents4_MailMergeDataSourceValidateEventHandler( App_MailMergeDataSourceValidate); app.MailMergeWizardSendToCustom += new Word. ApplicationEvents4_MailMergeWizardSendToCustomEventHandler( App_MailMergeWizardSendToCustom); app.MailMergeWizardStateChange += new Word. ApplicationEvents4_MailMergeWizardStateChangeEventHandler( App_MailMergeWizardStateChange); } void App_MailMergeAfterMerge(Word.Document document, Word.Document documentResult) { MessageBox.Show(String.Format( "MailMergeAfterMerge: Source = {0}, Result = {1}", document.Name, documentResult.Name)); } void App_MailMergeAfterRecordMerge(Word.Document document) { MessageBox.Show(String.Format( "MailMergeAfterRecordMerge for {0}", document.Name)); } void App_MailMergeBeforeMerge(Word.Document document, int startRecord, int endRecord, ref bool cancel) { MessageBox.Show(String.Format( "MailMergeBeforeMerge for {0}", document.Name)); // Word passes -16 as the EndRecord if the user // chose to merge all records. if (endRecord == -16) { endRecord = document.MailMerge.DataSource.RecordCount; } MessageBox.Show(String.Format( "Merging records from record {0} to record {1}." , startRecord, endRecord)); } void App_MailMergeBeforeRecordMerge(Word.Document document, ref bool cancel) { MessageBox.Show(String.Format( "MailMergeBeforeRecordMerge for {0}.", document.Name)); } void App_MailMergeDataSourceLoad(Word.Document document) { MessageBox.Show(String.Format( "MailMergeDataSourceLoad for {0}.", document.Name)); MessageBox.Show(String.Format( "The data source is {0}.", document.MailMerge.DataSource.Name)); } // This event won't fire except for an e-postage add-in void App_MailMergeDataSourceValidate(Word.Document document, ref bool handled) { MessageBox.Show(String.Format( "MailMergeDataSourceValidate for {0}.", document.Name)); } void App_MailMergeWizardSendToCustom(Word.Document document) { MessageBox.Show(String.Format( "MailMergeWizardSendToCustom for {0}.", document.Name)); } void App_MailMergeWizardStateChange(Word.Document document, ref int fromState, ref int toState, ref bool handled) { MessageBox.Show(String.Format( "MailMergeWizardStateChange for {0}.", document.Name)); } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
CommandBar Events
A common way to run your code is by adding a custom toolbar button or menu item to Word and handling 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," discussed 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 (for example, a CommandBarButton) 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, 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 Command Bar
public partial class ThisDocument { Word.Application app; Office.CommandBarButton btn; Office.CommandBarComboBox box; void ThisDocument_Startup(object sender, EventArgs e) { app = this.Application; // Store the new command bar in this document. app.CustomizationContext = this; Office.CommandBars bars = this.CommandBars; Office.CommandBar bar = bars.Add("My Custom Bar", missing, missing, true); bar.Visible = true; btn = bar.Controls.Add( Office.MsoControlType.msoControlButton, missing, missing, missing, true) as Office.CommandBarButton; btn.Click += new Office. _CommandBarButtonEvents_ClickEventHandler( Btn_Click); btn.Caption = "Display Message"; btn.Tag = "WordDocument1.btn"; btn.Style = Office.MsoButtonStyle.msoButtonCaption; box = bar.Controls.Add( Office.MsoControlType.msoControlComboBox, missing, missing, missing, true) as Office.CommandBarComboBox; box.Tag = "WordDocument1.box"; box.AddItem("Choice 1", 1); box.AddItem("Choice 2", 2); box.AddItem("Choice 3", 3); box.Change += new Office. _CommandBarComboBoxEvents_ChangeEventHandler( Box_Change); } static void Btn_Click(Office.CommandBarButton ctrl, ref bool cancelDefault) { MessageBox.Show("You clicked the button."); } static void Box_Change(Office.CommandBarComboBox ctrl) { MessageBox.Show(String.Format( "You selected {0}.", ctrl.Text)); } private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } }
Part One. An Introduction to VSTO
An Introduction to Office Programming
Introduction to Office Solutions
Part Two. Office Programming in .NET
Programming Excel
Working with Excel Events
Working with Excel Objects
Programming Word
Working with Word Events
Working with Word Objects
Programming Outlook
Working with Outlook Events
Working with Outlook Objects
Introduction to InfoPath
Part Three. Office Programming in VSTO
The VSTO Programming Model
Using Windows Forms in VSTO
Working with Actions Pane
Working with Smart Tags in VSTO
VSTO Data Programming
Server Data Scenarios
.NET Code Security
Deployment
Part Four. Advanced Office Programming
Working with XML in Excel
Working with XML in Word
Developing COM Add-Ins for Word and Excel
Creating Outlook Add-Ins with VSTO