Events in the Word Object Model

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":

  • 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 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:

  • 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 first 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 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.

  • 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 bool 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. However, as discussed earlier, 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.

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.

  • 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 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.

  • 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 bool cancel parameter. The cancel parameter can be set to TRue by your event handler to prevent Word from doing 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 bool cancel parameter. The cancel parameter can be set to TRue by your event handler to prevent Word from doing the default action associated with a right-click.

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.

  • 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 into 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 wdXMLSelection-ChangeReasonMove.

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.

  • Application.XMLValidationError is raised when the XML in the document is not valid when compared to 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 an inUndoRedo bool 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 deleting any text, the Range will be passed as null. Word also passes the XMLNode object that was deleted and a bool 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.

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



Visual Studio Tools for Office(c) Using C# with Excel, Word, Outlook, and InfoPath
Visual Studio Tools for Office(c) Using C# with Excel, Word, Outlook, and InfoPath
ISBN: 321334884
EAN: N/A
Year: N/A
Pages: 214

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net