VSTO 2.0 Managed Control Support: Primary and Extender
VSTO 2.0 has a feature that allows you to embed any WinForms control you want into an Excel spreadsheet or Word document. This lets you use lots of powerful WinForms controls in your Office solution. When you add a managed control to your document (by dragging and dropping from the Visual Studio controls toolbox to the Excel/Word document surface) we create a control that appears in the list of objects you can code against.
I've blogged about host items and host controls here and here. These blog entries talk about the host controls that are associated with objects provided natively by Excel and Word like “bookmark“ or “named range“ or “list“. When you add a managed control to the document, you also get something that looks like a host control in the drop down list to code against (a “Button1“ control for example). But under the covers things are a bit more complex.
The complexity comes because a control embedded in a document doesn't really control its own position in the document--the positioning of the control in the document is controlled by the host application (Excel or Word). The coordinate system used to position the control is controlled by the host. For example, the document might have a coordinate system that is in terms of twips, inches, centimeters, or some other arbitrary system. You can imagine that Excel could (although it doesn't really) require you to position the control by giving it a cell name like “A1“ rather than a number.
Because a control placed in a document has no notion of where it is positioned in the document—this positioning information is maintained by the host. The host can provide this information for a control via an object known as an extender. Typically an extender will have properties like Width, Height, Left, Top, etc.
VSTO2 merges together what we call the primary control—say System.Windows.Forms.Button—with the host provided extender positioning interface—in the Excel case this interface is “Microsoft.Office.Interop.Excel._OLEObject”. So when you look closely at a VSTO 2.0 button on a document, it isn't quite like the standard WinForm control you know. The definition for a VSTO 2.0 Windows Forms button looks like this:
public class Button : System.Windows.Forms.Button
Three key things to notice.
1. It is in the Microsoft.Office.Tools.Excel.Controls namespace.
2. It derives from System.Windows.Forms.Button
3. In addition to being a “Button“, it implements the Microsoft.Office.Interop.Excel._OLEObject interface but doesn't say it implements that interface in the type definition for reasons best described in another blog post.
The VSTO 2.0 WinForm button object merges together the properties and methods of the primary WinForms button with the extender interface provided by Excel. The extender interface’s properties and methods take priority in case of collision—so a Windows.Forms.Button Left property will be hidden when merged with a Microsoft.Office.Interop.Excel._OLEObject which also has a Left property. Calls to the Left property on the merged control will set the position of the control via the extender interface, which is the behavior the developer expects.
The tricky part is if you ever cast this guy back to a System.Windows.Forms.Button, you have lost the extender interface and un-hidden the positioning properties provided by the primary button object that were previously hidden by the extender interface. Now if you set Left on the Button, you will not be setting the position of the control in the Excel document (you won't be talking to the extender), but you will be setting the position of the control relative to the container within Excel that the managed control is living on (since you are talking to the primary button object). Badness (or at least confusion) results.