VSTO Extensions to the Word and Excel Object Models





VSTO Extensions to the Word and Excel Object Models

This chapter finishes up with a detailed list of every new property, event, and method aggregated onto the Word and Excel objects by the VSTO aggregates, with the exception of the new data binding features (which Chapter 17 covers). For Outlook, only the Application object is aggregated, and no new events, methods, or properties are added to that object.

As mentioned previously, every aggregated object now has a Tag property that you can use for any purpose you choose and an InnerObject property that you can use to access the aggregated object. In addition, each host control now has a Delete method that removes it (if it can be added dynamically at runtime) from its document or worksheet. Because every aggregating object has these properties and methods now, they are not mentioned again in the following topics.

The Word Document Class

VSTO Word projects have exactly one host item class. Every customized document class inherits from the aggregating class Microsoft.Office.Tools.Word.Document and implements the properties, methods, and events defined by the Microsoft.Office.Interop.Word.Document interface.

Document objects in VSTO source the following new events shown in Figure, all of which are raised by the Document object when the Application object raises the identically named event.

New Events on VSTO's Aggregated Document Object

Event Name

Delegate

Notes

ActivateEvent

WindowEventHandler

From Application, renamed from WindowActivate

BeforeClose

CancelEventHandler

From Application

BeforeDoubleClick

ClickEventHandler

From Application

BeforePrint

CancelEventHandler

From Application

BeforeRightClick

ClickEventHandler

From Application

BeforeSave

SaveEventHandler

From Application

CloseEvent

DocumentEvents2_CloseEventHandler

From Document, renamed

Deactivate

WindowEventHandler

From Application

EPostageInsert

EventHandler

From Application

EPostagePropertyDialog

EventHandler

From Application

MailMergeAfterMerge

MailMergeAfterMerge EventHandler

From Application

MailMergeAfterRecordMerge

EventHandler

From Application

MailMergeBeforeMerge

EventHandler

From Application

MailMergeBeforeRecordMerge

CancelEventHandler

From Application

MailMergeDataSourceLoad

EventHandler

From Application

MailMergeDataSourceValidate

HandledEventHandler

From Application

MailMergeWindowSendTo-Custom

EventHandler

From Application

MailMergeWizardStateChange

MailMergeWizardState ChangeEventHandler

From Application

New

DocumentEvents2_NewEventHandler

From Document, delayed

Open

DocumentEvents2_OpenEventHandler

From Document, delayed

SelectionChange

SelectionEventHandler

From Application

Shutdown

EventHandler

Startup

EventHandler

SyncEvent

DocumentEvents2_SyncEventHandler

From Application, renamed

WindowSize

WindowEventHandler

From Application

XMLAfterInsert

DocumentEvents2_XMLAfterInsertHandler

From Document

XMLBeforeDelete

DocumentEvents2_XMLBeforeDelete-Handler

From Document


Notice that the Sync and Close events have been renamed to avoid a naming conflict; C# does not allow a class to have an event and a method with the same name.

The Document class now has OnStartup and OnShutdown methods that force the Document object to source the Startup and Shutdown events.

The New and Open events are delayed so that they are not raised until the aggregate class is fully initialized. These events would normally be raised before any user-authored code could run. If user code does not run until after the event has been raised, however, how would you add an event handling delegate to listen to the event? Therefore, the events are delayed until after the customization's event binding code can run.

The event delegate types could use some additional explanation. All the event delegate types that begin with DocumentEvents2_ are from the Word PIA. The System.EventHandler, System.ComponentModel.CancelEventHandler and System.ComponentModel.HandledEventHandler delegates are straightforward. The remaining delegate types are all defined in the Microsoft.Office.Tools.Word namespace and have signatures as follows:

delegate void ClickEventHandler(object sender, ClickEventArgs e);
delegate void MailMergeAfterMergeEventHandler(object sender,
  MailMergeAfterMergeEventArgs e);
delegate void MailMergeWizardStateChangeEventHandler(object sender,
  MailMergeWizardStateChangeEventArgs e);
delegate void SaveEventHandler(object sender, SaveEventArgs e);
delegate void SelectionEventHandler(object sender, SelectionEventArgs e)
delegate void WindowEventHandler(object sender, WindowEventArgs e); 

The arguments classes of each are as follows:

  • The ClickEventArgs class inherits from System.ComponentModel.CancelEventArgs and therefore has a Cancel property. It also exposes the selection that was clicked:

    class ClickEventArgs : CancelEventArgs {
       ClickEventArgs (Interop.Word.Selection selection, bool cancel)
       Interop.Word.Selection Selection { get; }
    }
    

  • The MailMergeAfterMergeEventArgs class exposes the new document created:

    class MailMergeAfterMergeEventArgs : EventArgs {
      MailMergeAfterMergeEventArgs(Interop.Word.Document newDocument)
      Interop.Word.Document NewDocument { get; }
    }
    

  • The MailMergeWizardStateChangeEventArgs class exposes the previous, current, and handled states:

    class MailMergeWizardStateChangeEventArgs : EventArgs {
      MailMergeWizardStateChangeEventArgs (int fromState,
        int toState, bool handled)
      int FromState { get; }
      int ToState { get; }
      bool Handled { get; }
    }
    

  • The SaveEventArgs class allows the handler to instruct the event source whether the Save As dialog should display. This is also a cancelable event:

    class SaveEventArgs : CancelEventArgs {
      SaveEventArgs (bool showSaveAsUI, bool cancel)
      bool ShowSaveAsDialog { get; set; }
    }
    

  • The SelectionEventArgs class provides the selection that was changed:

    class SelectionEventArgs : EventArgs {
      SelectionEventArgs (Interop.Word.Selection selection)
      Interop.Word.Selection Selection{ get; }
    }
    

  • The WindowEventArgs class provides the window that was activated, deactivated, or resized:

    class WindowEventArgs : EventArgs {
     WindowEventArgs(Interop.Word.Window window)
     Interop.Word.Window Window { get; }
    }
    

In addition to the new events, the Document object also contains two new collections. First, as discussed earlier in this chapter, the Document object aggregate contains a collection of controls. Second, the Document object now contains a VSTOSmartTags collection (discussed further in Chapter 16, "Working with Smart Tags in VSTO").

C# does not support parameterized properties, but two methods in the Document interface use parameterized properties. To make it easier to call these methods from C#, both properties now return instances of helper classes that allow you to use parameterized indexers. They are as follows:

_ActiveWritingStyleType ActiveWritingStyle { get; }
_CompatibilityType Compatibility { get; }

The helper classes are scoped to within the customized host item's base class itself, not to the Microsoft.Office.Tools.Word namespace.


The helper classes are as follows:

class _ActiveWritingStyleType : System.MarshalByRefObject {
 public string this[object languageID] { get; set; }
}
class _CompatibilityType : System.MarshalByRefObject { 
 public string this[Interop.Word.WdCompatibility Type] { get; set; }
}

This means that you can access these properties by passing the parameter to the index to fetch or set the property:

style = this.ActiveWritingStyle[id];

The derived class can be further customized to add new events, methods, and properties. As you edit the document in the Word designer, any bookmarks or other host controls (such as buttons, check boxes, and so on) that you drop onto the design surface will be added as members of the document class. Similarly, any XML mappings added to the document will be added to the document class as either an XMLNode member (if the mapping is to a single node) or an XMLNodes member (if the mapping is to a repeatable node).

The document class has one additional new method, RemoveCustomization, which takes no arguments and has no return value. Calling this method on the aggregated document object removes the customization information from the document, so that after it is saved and reloaded, the customization code will no longer run.

Finally, the document class has a new property, ThisApplication, which refers to the Application object. This property exists to help migrate VSTO 2003 code that referred to a ThisApplication object. The document class also has an ActionsPane property, which is covered in detail in Chapter 15, "Working with Actions Pane."

The Word Bookmark Host Control

Bookmark objects in the Word object model do not source any events. The aggregated host control Bookmark in VSTO sources the following new events shown in Figure:

New Events on VSTO's Aggregated Bookmark Object

Event Name

Delegate

BeforeDoubleClick

ClickEventHandler

BeforeRightClick

ClickEventHandler

Deselected

SelectionEventHandler

Selected

SelectionEventHandler

SelectionChange

SelectionEventHandler


The delegate types and their corresponding argument classes are documented in the document class topic above.

As a convenience for both view programming and data binding, bookmark host controls also aggregate more than 150 methods and properties of the Range object that they represent. For example, these two lines of code are functionally identical:

columns = this.bookmark1.range.columns;
columns = this.bookmark1.columns;

The methods and properties of the Range object aggregated onto the Bookmark object are for the most part straightforward proxies that just call the method or property accessor on the aggregated range, so almost all of the methods will be functionally identical whether you call them from the Range or the Bookmark.

Three exceptions apply. First, setting the Text property on the Range object directly can sometimes result in the bookmark itself being deleted by Word. If you set the Text property by calling the new property added to the Bookmark aggregate, it ensures that the bookmark is not deleted.

Second and third, the Information and XML properties from the PIA interface are parameterized properties. Because C# does not support calling parameterized properties, the bookmark host control uses helper classes that enable you to use parameterized indexers from C#. The properties are now defined as follows:

_InformationType Information { get; }
_XMLType XML { get; }

The helper classes are scoped inside the Bookmark class itself:

class _InformationType : System.MarshalByRefObject {
  object this[Interop.Word.WdInformation Type] { get; }
}

class _XMLType : System.MarshalByRefObject {
  public string this[bool DataOnly] { get; }
}

You can then use the properties like this:

info = this.myBookmark.Information[WdInformation.wdCapsLock];

The Word XMLNode and XMLNodes Host Control Classes

When you map a schema into a Word document, element declarations that have a maxOccurs attribute in the schema equal to 1 are represented in the host item class as XMLNode objects. All others are represented as XMLNodes objects, because there could be more than one of them.

Figure shows the new events in VSTO that the XMLNode and XMLNodes objects source.

New Events on VSTO's Aggregated XMLNode and XMLNodes Objects

Event Name

Delegate

AfterInsert

NodeInsertAndDeleteEventHandler

BeforeDelete

NodeInsertAndDeleteEventHandler

ContextEnter

ContextChangeEventHandler

ContextLeave

ContextChangeEventHandler

Deselect

ContextChangeEventHandler

Select

ContextChangeEventHandler

ValidationError

EventHandler


As you can see, we have two new delegate classes, and therefore two new event argument classes. These events are normally sourced by the application object.

The delegates and event argument classes are all in the Microsoft.Office.Tools.Word namespace. The delegate classes are as follows:

delegate void ContextChangeEventHandler(object sender,
  ContextChangeEventArgs e);
delegate void NodeInsertAndDeleteEventHandler(object sender,
  NodeInsertAndDeleteEventArgs e);

  • When a node is inserted or deleted, it is often interesting to know whether the change is a result of the user inserting or deleting the element directly, or whether this is part of an undo or redo operation. This flag is therefore exposed on the event arguments class:

    class NodeInsertAndDeleteEventArgs : EventArgs {
      NodeInsertAndDeleteEventArgs (bool inUndoRedo)
      bool InUndoRedo { get; }
    }
    

  • When a node is selected or deselected, the appropriate event is raised. A "context change" is a special kind of selection change in which the insertion point of the document moves from one XML node to another. Therefore, the event arguments for the ContextEnter and ContextLeave events specify the node that was until recently the home of the insertion point, and the new home.

class ContextChangeEventArgs : NodeSelectionEventArgs {
  ContextChangeEventArgs( Interop.Word.XMLNode oldXMLNode,
    Interop.Word.XMLNode newXMLNode, Interop.Word.Selection selection,
    int reason)
  Interop.Word.XMLNode OldXMLNode { get; }
  Interop.Word.XMLNode NewXMLNode { get; }
}

The XMLNode interface in the PIA has two parameterized properties, which are not supported in C#. Therefore, these properties have been redefined to return helper classes that implement parameterized indexers instead. The two methods are as follows:

_ValidationErrorTextType ValidationErrorText { get; }
_XMLType XML { get; }

Their helper classes are scoped to the XMLNode class itself. They are defined as follows:

class _ValidationErrorTextType : System.MarshalByRefObject { 
  string this[bool Advanced] { get; }
}

class _XMLType : System.MarshalByRefObject {
  string this[bool DataOnly] { get; }
}

XMLNode objects also implement several convenient new methods for manipulating the XML bound to the document:

void LoadXml(string xml)
void LoadXml(System.Xml.XmlDocument document)
void LoadXml(System.Xml.XmlElement element)
void Load(string filename)

All of these take the contents of the XML in the argument and insert it into the given node and its children. However, the onus is on the caller to ensure both that the XML inserted into the node corresponds to the schematized type of the node, and that any child nodes exist and are mapped into the document appropriately. These methods will neither create nor delete child nodes.

As a further convenience for both view and data programming, the XMLNode object also provides a property that aggregates the Text property of the node's range:

string NodeText { get; set; }

Chapters 15, "Working with ActionsPane," 17, "VSTO Data Programming," and 22, "Working with XML in Word," cover data binding scenarios and actions pane scenarios for XMLNode and XMLNodes objects in detail. That sums up the VSTO extensions to the Word object model. The extensions to the Excel object models are similar but somewhat more extensive because of the larger number of host controls.

The Excel Workbook Host Item Class

The aggregating workbook class raises the same 29 events as the aggregated workbook class, with the same delegate types. Aside from renaming the Activate event to ActivateEvent, so as to avoid a collision with the method of the same name, there are no changes to the events raised by the Workbook object.

The Workbook object does have two new events raised when the customization starts up and shuts down:

event System.EventHandler Startup;
event System.EventHandler Shutdown;

The aggregated Workbook object also has two new methods, OnStartup and OnShutdown, which cause the workbook to raise the Startup and Shutdown events.

As with the Word document class, the Excel workbook class gains a ThisApplication property, which refers back to the Excel Application object; an ActionsPane property, which Chapter 15 covers; and a VstoSmartTags collection, which Chapter 16 covers. The Workbook object also has one additional new method, RemoveCustomization, which takes no arguments and has no return value. Calling this method on the aggregated Workbook object removes the customization information from the spreadsheet, so that after it is saved and reloaded, the customization code will no longer run.

There is only one other minor change to the view programming model of the workbook class. Because C# cannot use parameterized properties, the Colors property now returns a helper class (scoped to the host item class itself) that allows you to use a parameterized index:

_ColorsType Colors { get; }

class _ColorsType : System.MarshalByRefObject {
  object this[object Index] { get; set; } 

The Excel Worksheet Host Item Class

Much like the workbook, the aggregating worksheet class does not have any major changes to its view programming model. The aggregating worksheet class raises the same eight events as the aggregated worksheet class, with the same delegate types. Aside from renaming the Activate event to ActivateEvent, so as to avoid a collision with the method of the same name, there are no changes to the events raised by the Worksheet object.

The Worksheet object does have two new events raised when the customization starts up and shuts down:

event System.EventHandler Startup;
event System.EventHandler Shutdown;

The Worksheet object has two new methods, OnStartup and OnShutdown, which cause the worksheet to raise the Startup and Shutdown events. The worksheet also provides the Controls collection mentioned earlier in this chapter.

Worksheets classes can be customized by subclassing; the derived classes generated by the design have properties representing charts, named ranges, XML-mapped ranges, list objects, and other controls on each sheet.

There is only one other minor change to the view programming model of the worksheet class. Because C# cannot use parameterized properties, the Range property now returns a helper class (scoped to the worksheet class itself) that allows you to use a parameterized index:

_RangeType Range { get; }

class _RangeType : System.MarshalByRefObject {
  Interop.Excel.Range this[object Cell1, object Cell2] { get; }
}

The Excel Chart Sheet Host Item Class and Chart Host Control

Chart sheet host items and chart host controls are practically identical; the only difference between them as far as VSTO is concerned is that chart sheets are host items classes with their own designer and code-behind file. Charts, by contrast, are treated as controls embedded in a worksheet.

Both rename the Activate and Select events (to ActivateEvent and SelectEvent respectively) to avoid the name conflicts with the methods of the same name. The chart sheet host item class raises Startup and Shutdown events and has OnStartup and OnShutdown methods just as the worksheet class does.

Both the chart and the chart sheet have a parameterized HasAxis property that cannot be called from C#. The property therefore now returns an instance of a helper class that allows you to use a parameterized indexer instead:

_HasAxisType HasAxis { get; }

class _HasAxisType : System.MarshalByRefObject {
  object this[object Index1, object Index2] { get; set; }
}

The Excel NamedRange, XmlMappedRange, and ListObject Host Controls

All three of these are special kinds of Range objects. They raise the following new events shown in Figure.

New Events on VSTO's Aggregated NamedRange, XmlMappedRange, and ListObject Objects

Event Name

Delegate

BeforeDoubleClick

DocEvents_BeforeDoubleClickEventHandler

BeforeRightClick

DocEvents_BeforeRightClickEventHandler

Change

DocEvents_ChangeEventHandler

Deselected

DocEvents_SelectionChangeEventHandler

Selected

DocEvents_SelectionChangeEventHandler

SelectionChange

DocEvents_SelectionChangeEventHandler


All the event delegates are from the Microsoft.Office.Tools.Interop.Excel namespace in the Excel PIA.

The list object raises several more events in addition to those above, but because they all are primarily used to implement data binding functionality, Chapter 17 covers them.

There are many parameterized properties in both the NamedRange and XmlMappedRange interfaces that are not supported by C#. To make this functionality usable more easily from C#, these properties now return helper functions (scoped to the NamedRange or XmlMappedRange classes themselves) that expose parameterized indexers.

The NamedRange object only has one redefined property:

_EndType End { get; }

The _EndType helper class is defined as follows:

class _EndType : System.MarshalByRefObject { 
  Interop.Excel.Range this[Interop.Excel XlDirection Direction] { get; }
}

The NamedRange aggregate also implements a parameterized indexer:

object this[object RowIndex, object ColumnIndex]
  { get; set; }

The following properties are redefined on both NamedRange and XmlMapped-Range aggregates:

_AddressLocalType AddressLocal { get; }
_AddressType Address { get; }
_CharactersType Characters { get; }
_ItemType Item { get; }
_OffsetType Offset { get; }
_ResizeType Resize { get; }

The corresponding helper classes are defined as follows:

class _AddressLocalType : System.MarshalByRefObject {
  string this[bool RowAbsolute, bool ColumnAbsolute,
    Interop.Excel.XlReferenceStyle ReferenceStyle, bool External,
    object RelativeTo] { get; }
}
class _AddressType : System.MarshalByRefObject {
  string this[bool RowAbsolute, bool ColumnAbsolute,
    Interop.Excel.XlReferenceStyle ReferenceStyle, bool External,
    object RelativeTo] { get; }
}
class _CharactersType : System.MarshalByRefObject {
  Interop.Excel.Characters this[int Start, int Length] { get; }
}
class _ItemType : System.MarshalByRefObject {
  object this[int RowIndex] { get; set; }
  object this[int RowIndex, int ColumnIndex] { get; set; }
}
class _OffsetType : System.MarshalByRefObject {
  Interop.Excel.Range this[int RowOffset, int ColumnOffset] { get; }
}
class _ResizeType : System.MarshalByRefObject {
  Interop.Excel.Range this[int RowSize, int ColumnSize] { get; }
}

As a convenience for both view and data programming, NamedRange host controls also expose directly all the methods of the associated Name object:

  • string RefersTo { get; set; }

  • string RefersToLocal { get; set; }

  • string RefersToR1C1 { get; set; }

  • string RefersToR1C1Local { get; set; }

  • Interop.Excel.Range RefersToRange { get; }

If somehow the NamedRange object has been bound to a non-named range, these will throw NotSupportedException.

The NamedRange object also has a Name property that is somewhat confusing. The property getter returns the Name object associated with this named range. If you pass a Name object to the setter, it will set the Name property, just as you would expect. If you pass a string, however, it will attempt to set the Name property of the underlying Name object.

The NamedRange host control also slightly changes the exception semantics of the Name property in two ways. First, in the standard Excel object model, setting the Name property of the name object of a named range to the name of another named range deletes the range, oddly enough; doing the same to a VSTO NamedRange host control raises an ArgumentException and does not delete the offending range.

Second, in the standard Excel object model, setting the Name property to an invalid string fails silently. The VSTO NamedRange object throws an Argument-Exception if the supplied name is invalid.

The XMLMappedRange and ListObject host controls do not aggregate the methods of the Name object or change the error handling semantics of the name setter. The changes to the Name property semantics only apply to the NamedRange object.


XML mapped ranges and list objects are the Excel equivalent of the XMLNode and XMLNodes controls in Word. The XML mapped range represents a mapped singleton element, and the list object represents a set of rows. We cover data binding scenarios in Chapter 17, "VSTO Data Programming," and other XML scenarios in Excel in Chapter 21, "Working with XML in Excel." In this chapter, we just discuss their use as host controls.

The list object host control has one new property:

bool IsSelected { get; }

This property is most useful for determining whether there is an "insert row." Excel does not display an insert row if the list object's range is not selected.

The list object host control also slightly changes the error handling semantics of these properties:

Interop.Excel.Range DataBodyRange { get; } 
Interop.Excel.Range HeaderRowRange { get; }
Interop.Excel.Range InsertRowRange { get; }
Interop.Excel.Range TotalsRowRange { get; }

The only difference is that these properties now all return null rather than throwing an exception if you attempt to access the property on a list object that lacks a body, header, insert row, or totals row, respectively.

Chapter 17 discusses other new properties and methods added to the list object used for data binding.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows