Chapter 10. The Visual Studio Automation Object Model
IN THIS CHAPTER
|
Visual Studio is built to be "extensible." It ships with its own API to enable you, the developer, to control many of the pieces of the IDE.
The API is called the Visual Studio automation object model, and understanding its capabilities is the key to unlocking your ability to program and control the IDE itself by writing code in the form of macros and add-ins (discussed in Chapter 11, "Writing Macros, Add-Ins, and Wizards").
In this chapter, we discuss the layout and structure of the automation object model. We map the various objects in the object model to their IDE counterparts, delve into the various ways to interact with these objects through managed code, and, we hope, start to see a glimpse of the possibilities in terms of Visual Studio customization.
To drive home the object model concepts and place them in context, we have provided various code snippets and listings, nearly 100% of which are written in Visual Basic. The reason is that Visual Basic is the language of macros (other languages are not supported), and macros are by far the easiest and quickest way to reach out and touch elements of the IDE. As such, macros are a perfect vehicle for exploring and understanding the object model. In Chapter 11, we'll move beyond the object model and work to understand how to use, write, and run macros and add-ins (add-ins don't suffer from the Visual Basic limitation, so we'll switch gears and provide a majority of our add-in code using C#).
Don't worry too much about the mechanics of writing an add-in or macro at this point; concentrate instead on understanding the automation objects and how they are referenced and used. For the ambitious, know that the code listings here can be pasted directly into the Macros IDE Editor and run as is.
An Overview of the Automation Object Model
The automation object model is a structured class library with a top-level root object called DTE (or DTE2; more on this in a bit), which stands for Development Tools Environment. By referencing the assembly that implements the DTE/DTE2 object, you can instance this root object and use its members and child classes to access the IDE components.
Object Model Versions
The automation object model is actually implemented across two different, complementary primary interoperable assemblies: EnvDTE and EnvDTE80. EnvDTE is the original automation assembly distributed with previous versions of Visual Studio .NET. EnvDTE80 is a new library distributed with Visual Studio 2005. Visual Studio 2005 provides enhanced automation functionality. Because of this, Microsoft was faced with a common design decision: Replace or upgrade the current EnvDTE and risk introducing incompatibilities with current macros and add-ins, or ship a new assembly that could be leveraged in cases in which the new functionality was desired (existing code would still target the previous, unchanged library).
The latter path was chosen, and thus EnvDTE80 (80 represents version 8.0) represents the latest automation functions, while EnvDTE provides the base level of functionality and backward compatibility.
Within the EnvDTE80 assembly, you will find types that supersede their predecessors from the EnvDTE assembly. In these cases, the type name has been appended with a 2 to indicate the revised version. Thus, we have DTE and DTE2, Solution and Solution2, and so on.
Table 10.1 provides a side-by-side listing of some of the most important types implemented in EnvDTE and EnvDTE80. This type list is incomplete; it should be considered for reference only. This table is useful, however, for identifying some of the newly minted types in the new automation assembly; in the next section, we'll see how these types can be organized into broad Visual Studio automation categories and how they map onto physical IDE constructs.
Table 10.1. EnvDTE and EnvDTE80 Types
EnvDTE Type
EnvDTE80 Type
Description
AddIn
Represents a VS add-in.
Breakpoint
Breakpoint2
Returns the debugger object.
BuildDependencies
For the selected project, represents a collection of BuildDependency objects.
BuildDependency
For the selected project, represents the projects that it depends on for a successful build.
BuildEvents
Exposes a list of events relevant to a solution build.
Command
Represents a command action in the IDE.
Commands
Commands2
Returns a collection of all commands supported in the IDE.
CommandWindow
Represents the command window.
Configuration
Represents a project's configuration properties.
Debugger
Debugger2
Represents the Visual Studio debugger.
DebuggerEvents
Exposes events from the debugger.
Document
Represents an open document in the IDE.
Documents
Returns a collection of all open documents in the IDE.
DTE
DTE2
Represents the IDE; this is the top-level root object for the automation object model.
EditPoint
EditPoint2
Represents a text operation point within a document.
Events
Events2
Exposes all automation events.
Find
Find2
Represents the Find capability for text searches in the IDE.
HTMLWindow
Represents an HTML window.
OutputWindow
Represents the output window.
Program
(Process2)
Represents a program running within the IDE; useful for examining processes and threads within the program. EnvDTE80 functionality is provided by the Process2 object.
Project
Represents a project loaded in the IDE.
ProjectItem
Represents an item contained within a given project.
ProjectItems
Returns a collection of all items contained within a project.
Property
Represents a generic property for an object (this can be used across a variety of objects in the automation library).
SelectedItem
Represents projects or project items that are currently selected in the IDE.
Solution
Solution2
Represents the solution currently loaded in Visual Studio.
SourceControl
SourceControl2
Represents the source control system of record within Visual Studio.
TaskItem
Represents an item in the task list window.
TaskItems
TaskItems2
Returns a collection of all items in the task list window.
TaskList
Represents the task list window.
TexTDocument
Represents a text file open in the IDE.
TextPane
TextPane2
Represents a pane within an open text editor window.
TextWindow
Represents a text window.
ToolBox
Represents the Toolbox window.
ToolBoxItem
ToolBoxItem2
Represents an item within the Toolbox window.
ToolBoxTab
ToolBoxTab2
Represents a tab of items on the Toolbox window.
Window
Window2
Represents, generically, any window within the IDE.
Windows
Windows2
Returns a collection of all windows within the IDE.
Automation Categories
Because any automation effort with Visual Studio starts with the object model, you should understand first how it maps onto the IDE constructs and determine the exact capabilities that it exposes.
In general, you can think of the object model classes as being organized into categories that directly speak to these IDE concepts:
-
Solutions and projects
-
Windows and command bars (toolbars and menu bars)
-
Documents
-
Commands
-
Debugger
-
Events
Each of the objects in these categories touches a different piece of the IDE, and access to each object is always through the root-level DTE2 object.
The DTE/DTE2 Root Object
The DTE/DTE2 object represents the tip of the API tree. You can think of it as representing Visual Studio itself, with the objects under it mapping to the various constituent parts of the IDE.
As mentioned previously, DTE2 is the object used with Visual Studio 2005, with DTE providing compatibility with previous versions. In this chapter, unless we specifically need to differentiate between their capabilities, we will generically refer to the DTE and DTE2 objects as simply DTE.
The DTE properties are used to gain a reference to a specific IDE object (or collection of objects). Methods on the object are used to execute commands in the IDE, launch wizards, or close the IDE.
Table 10.2 shows the major properties and methods defined on the DTE2 object; they have been organized within the six object categories itemized in the preceding section.
Table 10.2. DTE2 Properties and Methods for IDE Access
Category
Property
Description
Commands
Commands
Returns a collection of Command objects; in general, a command is an action that can be carried out within the IDE such as opening or saving a file.
Debugger
Debugger
Returns the debugger object.
Documents
ActiveDocument
Returns a Document object representing the currently active document.
Documents
Documents
Returns a collection of Document objects representing all open documents.
Event Notification
Events
Returns the Events object for handling event notifications.
Solutions and Projects
ActiveSolutionProjects
Returns a collection of the Project objects representing the projects that are currently selected within the Solution Explorer.
Solutions and Projects
Solution
Returns the Solution object for the currently loaded solution.
Windows and Command Bars
ActiveWindow
Returns a Window object representing the window within the IDE that currently has focus.
Windows and Command Bars
CommandBars
Returns a collection of CommandBar objects representing all the toolbars and menu bars.
Windows and Command Bars
MainWindow
Returns a Window object representing the IDE window itself.
Windows and Command Bars
StatusBar
Returns a StatusBar object representing Visual Studio's status bar.
Windows and Command Bars
ToolWindows
Returns a ToolWindows instance, which in turns provides access to a few of the most prominent tool windows: the command window, error list, output window, Solution Explorer, task list, and Toolbox.
Windows and Command Bars
WindowConfigurations
Returns a collection of WindowConfiguration objects; these objects represent the various window layouts in use by Visual Studio.
Commands
ExecuteCommand
Executes an environment command.
--
LaunchWizard
Starts the identified wizard with the given parameters.
--
Quit
Closes Visual Studio.
Note
The mechanics of referencing and instancing a DTE object change slightly depending on whether you are writing an add-in or a macro, so we'll cover the specifics in the macro and add-in sections in Chapter 11.
In summary, the DTE object is a tool for directly interacting with certain IDE components and providing access to the deeper layers of the API with its property collections. If you move one level down in the API, you find the major objects that form the keystone for automation.
Solution and Project Objects
The Solution object represents the currently loaded solution. The individual projects within the solution are available via Project objects returned within the Solution.Projects collection. Items within a project are accessed in a similar fashion through the Project.ProjectItems collection.
As you can see from Figure 10.1, this hierarchy exactly mirrors the solution/project hierarchy that we first discussed in Chapter 4, "Solutions and Projects."
Figure 10.1. Mapping the solution/project hierarchy.
There are some mismatches heresolution folders, for instance, are treated as projectsbut for the most part, the object model tree closely resembles the solution project tree that you are used to.
The Solution object and Solution2 object members allow you to interact with the current solution to perform common tasks such as
-
Determining the number of projects in the solution (Count property)
-
Adding a project to the solution based on a project file (AddFromFile method)
-
Creating a new solution or closing the current one (Create and Close methods)
-
Saving the solution (SaveAs method)
-
Removing a project from the solution (Remove method)
You can also directly retrieve a reference to any of the projects within the currently loaded solution by iterating over the Solution.Projects collection. As an example of interacting with the Solution and Project objects, this Visual Basic code snippet removes the first project from the current solution:
Dim sol As Solution = DTE.Solution Dim proj As Project = sol.Projects.Item(1) If proj.Saved Then sol.Remove(proj) Else ... End If
Table 10.3 provides the combined list of the most commonly used properties and methods implemented by Solution2.
Table 10.3. Primary Solution/Solution2 Object Members
Property
Description
AddIns
Returns a collection of AddIn objects associated with the current solution.
Count
Returns a count of the projects within the solution.
DTE
Provides a reference back to the parent DTE object.
FullName
Provides the full path and name of the solution file.
IsOpen
Indicates whether a solution is open.
Projects
Returns a collection of Project objects representing all the projects within the solution.
Properties
Returns a collection of Property objects that expose all the solution's properties.
Saved
Indicates whether the solution has been saved since the last modification.
SolutionBuild
Returns a reference to a SolutionBuild object. This is the entry point to the build automation objects applicable for the current solution.
Method |
Description |
---|---|
AddFromFile |
Adds a project to the solution using an existing project file. |
AddFromTemplate |
Takes an existing project, clones it, and adds it to the solution. |
AddSolutionFolder |
Creates a new solution folder in the solution. |
Close |
Closes the solution. |
Create |
Creates an empty solution. |
FindProjectItem |
Initiates a search for a given item in one of the solution's projects. |
Item |
Returns a Project instance. |
Open |
Opens a solution (using a specific view). |
Remove |
Removes a project from the solution. |
SaveAs |
Saves the solution. |
Controlling Projects in a Solution
One of the things that the Solution object is good for is retrieving references to the various projects that belong to the solution. Each Project object has its own set of useful members for interacting with the projects and their items. By using these members, you can interact with the projects in various, expected ways, such as renaming a project, deleting a project, and saving a project.
See Table 10.4 for a summary of the most common Project members.
Table 10.4. Primary Project Object Members
Property
Description
AddIns
Returns a collection of AddIn objects associated with the current solution.
Count
Returns a count of the project within the solution.
DTE
Provides a reference back to the parent DTE object.
FullName
Provides the full path and name of the solution file.
IsOpen
Indicates whether a solution is open.
Projects
Returns a collection of Project objects representing all the projects within the solution.
Properties
Returns a collection of Property objects that expose all the solution's properties.
Saved
Indicates whether the solution has been saved since the last modification.
SolutionBuild
Returns a reference to a SolutionBuild object. This is the entry point to the build automation objects applicable for the current solution.
Method |
Description |
---|---|
AddFromFile |
Adds a project to the solution using an existing project file. |
AddFromTemplate |
Takes an existing project, clones it, and adds it to the solution. |
AddSolutionFolder |
Creates a new solution folder in the solution. |
Close |
Closes the solution. |
Create |
Creates an empty solution. |
FindProjectItem |
Initiates a search for a given item in one of the solution's projects. |
Item |
Returns a Project instance. |
Property |
Description |
---|---|
Open |
Opens a solution (using a specific view). |
Remove |
Removes a project from the solution. |
SaveAs |
Saves the solution. |
Accessing Code Within a Project
Beyond the basic project attributes and items, one of the cooler things that can be accessed via a Project instance is the actual code within the project's source files. Through the CodeModel property, you can access an entire line of proxy objects representing the code constructs within a project. For instance, the CodeClass interface allows you to examine and edit the code for a given class in a given project.
Note
Support for the different CodeModel entities varies from language to language. The MSDN documentation for each CodeModel type clearly indicates the source language support for that element.
After grabbing a CodeModel reference from a Project instance, you can access its CodeElements collection (which is, not surprisingly, a collection of CodeElement objects). A CodeElement is nothing more than a generic representation of a certain code structure within a project. The CodeElement object is generic, but it provides a property, Kind. This property is used to determine the exact native type of the code object contained within the CodeElement.
The CodeElement.Kind property is an enumeration (of type vsCMElement) that identifies the specific type of code construct lurking within the CodeElement object. Using the Kind property, you can first determine the true nature of the code element and then cast the CodeElement object to its strong type. Here is a snippet of C# code that does just that:
if (element.Kind == vsCMElement.vsCMElementClass) CodeClass myClass = (CodeClass)element;
For a better grasp of the code model hierarchy, consider the C# code presented in Listing 10.1; this is a "shell" solution that merely implements a namespace, a class within that namespace, and a function within the class.
Listing 10.1. A Simple Namespace and Class Implementation
using System; using System.Collections.Generic; using System.Text; namespace MyNamespace { class MyClass { public string SumInt(int x, int y) { return x + y; } } } |
If you map the code in Listing 10.1 to the code object model, you would end up with the structure you see in Figure 10.2.
Figure 10.2. Simple code model object hierarchy.
To get an idea of the complete depth of the code model tree that can be accessed through the CodeElements collection, consult Table 10.5; this table shows all the possible vsCMElement values, the type they are used to represent, and a brief description of the type.
Table 10.5. Mapping the vsCMElement Enumeration Values
=200> =150> =150>Enumeration Value
Type
Description
vsCMElementAssignmentStmt
An assignment statement
vsCMElementAttribute
An attribute
vsCMElementClass
CodeClass
A class
vsCMElementDeclareDecl
A declaration
vsCMElementDefineStmt
A define statement
vsCMElementDelegate
CodeDelegate
A delegate
vsCMElementEnum
CodeEnum
An enumeration
vsCMElementEvent
CodeEvent
An event
vsCMElementEventsDeclaration
An event declaration
vsCMElementFunction
CodeFunction
A function
vsCMElementFunctionInvokeStmt
A statement invoking a function
vsCMElementIDLCoClass
An IDL co-class
vsCMElementIDLImport
An IDL import statement
vsCMElementIDLImportLib
An IDL import library
vsCMElementIDLLibrary
An IDL library
vsCMElementImplementsStmt
An implements statement
vsCMElementImportStmt
CodeImport
An import statement
vsCMElementIncludeStmt
An include statement
vsCMElementInheritsStmt
An inherits statement
vsCMElementInterface
CodeInterface
An interface
vsCMElementLocalDeclStmt
A local declaration statement
vsCMElementMacro
A macro
vsCMElementMap
A map
vsCMElementMapEntry
A map entry
vsCMElementModule
A module
vsCMElementNamespace
CodeNamespace
A namespace
vsCMElementOptionStmt
An option statement
vsCMElementOther
CodeElement
A code element not otherwise identified in this enum
vsCMElementParameter
CodeParameter
A parameter
vsCMElementProperty
CodeProperty
A property
vsCMElementPropertySetStmt
A property set statement
vsCMElementStruct
CodeStruct
A structure
vsCMElementTypeDef
A type definition
vsCMElementUDTDecl
A user-defined type
vsCMElementUnion
A union
vsCMElementUsingStmt
CodeImport
A using statement
vsCMElementVariable
A variable
vsCMElementVBAttributeGroup
A Visual Basic attribute group
vsCMElementVBAttributeStmt
A Visual Basic attribute statement
vsCMElementVCBase
A Visual C++ base
Windows
The visible, content portion of Visual Studio is represented by Window objects. Window objects are instances of open windows within the IDE such as the Solution Explorer, the task list window, an open code editor window, and so on. Even the IDE itself is represented by a Window object.
Any given window is either a document window or a tool window. Document windows host documents that are editable by the Text Editor. Tool windows contain controls that display information relevant to the current context of the IDE; the Solution Explorer and task list windows are examples of tool windows, and a VB source code file open in an editor is an example of a document window.
Referencing Windows
If you need to retrieve an instance of a specific window, you have a few different options, each optimal for a given situation. For starters, the main IDE window is always available directly from the DTE object:
Dim IDE As Window IDE = DTE.MainWindow
Obviously, if you need to perform a specific action against the IDE window, this is your quickest route.
The DTE.ActiveWindow property also provides direct and quick access to a Window object, in this case the currently active window:
Dim CurrentWindow As Window CurrentWindow = DTE.ActiveWindow
The tool windows within the IDEthat is, the command window, the error list window, the output window, the Solution Explorer, the task list window, and the Toolboxalso have a direct way to retrieve their object model instances: You use the DTE.ToolWindows property. This property returns a ToolWindows object that exposes a separate property for each of the tool windows.
This Visual Basic code grabs a reference to the task list window and closes it:
Dim taskwin As Window taskwin = DTE.ToolWindows.TaskList taskwin.Close()
And finally, the fourth way to access an IDE window is through the DTE.Windows collection; this collection holds an entry for each IDE window. You can access a window from the collection by using either an integer representing the window's position within the collection, or by providing an object or string that represents the window you are trying to retrieve.
The following code grabs a handle to the Solution Explorer window:
Dim windows As Windows2 = DTE.Windows Dim window As Window = windows.Item(Constants.vsWindowKindSolutionExplorer)
Interacting with Windows
Table 10.6 itemizes the properties and methods available on each Window object.
Table 10.6. Window Object Members
=150> =350>Property
Description
AutoHides
A Boolean flag indicating whether the window can be hidden (applies only to tool windows).
Caption
The title/caption of the window.
Collection
The Windows collection that the current Window object belongs to.
CommandBars
A CommandBars collection of the command bars implemented by the window.
ContextAttributes
A collection of ContextAttribute objects; they are used to associate the current context of the window with the Dynamic Help window.
Document
If the Window object is hosting a document, this returns a reference to the document.
DTE
A reference to the root DTE object.
Height
The height of the window in pixels.
IsFloating
A Boolean flag indicating whether the window is floating or docked.
Left
The distance, in pixels, between the window's left edge and its container's left edge.
Linkable
A Boolean flag indicating whether the window can be docked with other windows.
LinkedWindowFrame
Returns a reference to the Window object that is acting as the frame for a docked window.
LinkedWindows
A collection of Window objects representing the windows that are linked together within the same frame.
Object
Returns an object proxy that represents the window and can be referenced by name.
ObjectKind
A GUID indicating the type of the object returned from Window.Object.
Project
A Project instance representing the project containing the Window object.
ProjectItem
A ProjectItem instance representing the project item containing the Window object.
Selection
Returns an object representing the currently selected item in the window (for document windows, this might be text; for tool windows, this might be an item in a list, and so on).
Top
The distance, in pixels, between the window's top edge and its parent's top edge.
Visible
A Boolean flag indicating whether the window is visible or hidden.
Width
The width of the window in pixels.
WindowState
Gets or sets the current state of the window (via a vsWindowState enum value: vsWindowStateMaximize, vsWindowStateMinimize, vsWindowStateNormal).
Method |
Description |
---|---|
Activate |
Gives the window focus. |
Close |
Closes the window; you can indicate, with a vsSaveChanges enum value, whether the window's hosted document should be saved or not saved, or whether the IDE should prompt the user to make that decision. |
SetSelectionContainer |
Passes an array of objects to the Properties window when the Window object has focus. This property is mainly used for custom tool windows where you need to control what is displayed in the Properties window. |
SetTabPicture |
Specifies an object to use as a tab image; this image is displayed whenever the window is part of a tab group within the IDE. |
Beyond the basics (such as using the Height and Width properties to query or affect a window's dimensions, or setting focus to the window with the SetFocus method), a few properties deserve special mention:
-
The Document property gives you a way to programmatically interact with the document that the window is hosting (if any).
-
The Project and ProjectItem properties serve to bridge the Window portion of the API with the Project/Solution portion; in a similar vein as the Document property, you can use these properties to interact with the project that is related to the window, or the project item (such as the VB code file, text file, resource file, and so on).
-
If you are dealing with a tool window, the SetTabPicture method provides a way to set the tab icon that is displayed when the tool window is part of a group of tabbed windows (for instance, the Toolbox window displays a wrench and hammer picture on its tab when part of a tabbed group).
-
Again, specifically for tool windows only, the SetSelectionContainer can be used to supply one or more objects for display within the Properties window. This capability is useful if you have a custom window where you need to control what is displayed in the Properties window when the window has focus (all the standard VS windows already do this for you).
Listing 10.2 contains a simple macro illustrating the use of the Window object; in this example, each window is queried to determine its type, and then a summary of each window is output in a simple message box.
Listing 10.2. VB Macro for Querying the Windows Collection
Imports EnvDTE Imports EnvDTE80 Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub InventoryWindows() ' Get collection of all open windows Dim windows As Windows2 = DTE.Windows ' Count the nbr of open windows Dim windowCount As Integer = windows.Count ' Local vars for looping and holding window and string ' results Dim idx As Integer Dim results As String Dim window As Window2 results = windowCount.ToString + " windows open..." + vbCrLf ' Iterate the collection of windows For idx = 1 To windowCount window = windows.Item(idx) Dim title As String = window.Caption ' If the window is hosting a document, a valid Document ' object will be returned through Window.Document If Not (window.Document Is Nothing) Then ' Write this out as a document window Dim docName As String = window.Document.Name results = results + "Window '" + title + "' is a document window" + vbCrLf Else ' If no document was present, this is a tool window ' (tool windows don't host documents) results = results + "Window '" + title + "' is a tool window" + vbCrLf End If Next ' Show the results MessageBox.Show(results, "Window Documents", MessageBoxButtons.OK, _ MessageBoxIcon.Information) End Sub End Module |
Note
If you want to embed your own custom control inside a tool window, you have to write an add-in and use the Windows.CreateToolWindow method. We cover this scenario in Chapter 11.
Text Windows and Window Panes
Text windows have their own specific object abstraction in addition to the generic Window object: The TextWindow object is used to represent text editor windows. To obtain a reference to a window's TextWindow object, you retrieve the Window object's value and assign it into a TextWindow type:
Dim textWindow As TextWindow textWindow = DTE.ActiveWindow.Object
The TextWindow object doesn't provide much functionality over and above the functionality found in the Window type; its real value is the access it provides to window panes.
Text editor windows in Visual Studio can be split into two panes; with a text editor open, simply select Split from the Window menu to create a new pane within the window. The TextWindow.ActivePane property returns a TextPane object representing the currently active pane in the window, and the TextWindow.Panes property provides access to all the panes within a text window:
' Get pane instance from collection Dim newPane As TextPane2 newPane = textWindow.Panes.Item(1) ' Get currently active pane Dim currPane As TextPane2 currPane = textWindow.ActivePane
One of the more useful things you can do with the TextPane object is to scroll the client area of the pane (for example, the visible portion of the document within the pane) so that a specific range of text is visible. This is done via the TextPane.TryToShow method.
Here is the definition for the method:
Function TryToShow(Point As TextPoint, Optional How As vsPaneShowHow, _ PointOrCount As Object)
The TextPoint parameter represents the specific location within the text document that you want visible in the text pane (we discuss TextPoint objects in depth in a later section of this chapter; see "Editing Text Documents"). The vsPaneShowHow value specifies how the pane should behave when scrolling to the indicated location:
-
vsPaneShowHow.vsPaneShowCentered will cause the pane to center the text/text selection in the middle of the pane (horizontally and vertically).
-
vsPaneShowHow.vsPaneShowTop will place the text point at the top of the viewable region in the pane.
-
vsPaneShowHow.vsPaneShowAsIs will show the text point as is with no changes in horizontal or vertical orientation within the viewable region in the pane.
The last parameter, the PointOrCount object, is used to specify the end of the text area that you want displayed. If you provide an integer here, this represents a count of characters past the original text point; if you provide another text point, then the selection is considered to be that text that resides between the two text points.
The TextPane object is also used to access the Incremental Search feature for a specific window pane. Listing 10.3 shows an example of this feature in action.
Listing 10.3. Controlling Incremental Search
Imports EnvDTE Imports EnvDTE80 Imports Microsoft.VisualStudio.CommandBars Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub IncrementalSearch() ' Grab references to the active window; ' we assume, for this example, that the window ' is a text window. Dim window As Window2 = DTE.ActiveWindow ' Grab a TextWindow instance that maps to our ' active window Dim txtWindow As TextWindow = window.Object ' Get the active pane from the text window Dim pane As TextPane2 = txtWindow.ActivePane 'Using the active pane, get an IncrementalSearch object ' for the pane Dim search As IncrementalSearch = pane.IncrementalSearch ' Try to find our IMessageMapper interface by looking ' for the string "IM" ' Configure the search: ' search forward in the document ' append the chars that we are searching for ' quit the search search.StartForward() search.AppendCharAndSearch(AscW("I")) search.AppendCharAndSearch(AscW("M")) ' To remove us from incremental search mode, ' we can call IncrementalSearch.Exit()... 'search.Exit() End Sub End Module |
The Tool Window Types
In addition to having a Window object abstraction, each default tool window in the IDEthe command window, output window, Toolbox window, and task list windowis also represented by a discrete type that exposes methods and properties unique to that tool window. Table 10.7 lists the default tool windows and their underlying type in the automation object model.
Table 10.7. Tool Windows and Their Types
=150> =350>Tool Window
Type
Command Window
CommandWindow
Output Window
OutputWindow
Task List Window
TaskList
Toolbox Window
ToolBox
To reference one of these objects, you first start with its Window representation and then cast its Window.Object value to the matching type. For instance, this VB snippet starts with a Window reference to the task list window and then uses that Window object to obtain a reference to the TaskList object:
Dim windows As Windows = DTE.Windows Dim twindow As Window = _ DTE.Windows.Item(EnvDTE.Constants.vsWindowKindTaskList)
Tasks and the Task List Window
The TaskList object enables you to access the items currently displayed in the task list window; each item in the window is represented by its own TaskItem object. The TaskItem object exposes methods and properties that allow you to manipulate the task items. For instance, you can mark an item as complete, get or set the line number associated with the task, and change the priority of the task.
You remove tasks from the list by using the TaskItem.Delete method and add them by using the TaskItems.Add method. The Add method allows you to specify the task category, subcategory, description, priority, icon, and so on:
Dim tlist As TaskList = CType(twindow.Object, TaskList) tlist.TaskItems.Add("Best Practices", "Coding Style", _ "Use of brace indenting is inconsistent", _ vsTaskPriority.vsTaskPriorityMedium, _ vsTaskIcon.vsTaskIconUser, True, _ "S:\ContosoCommonFramework\Contoso.Fx.Common\Class1.cs", _ 7, True, True)
Table 10.8 provides an inventory of the TaskItem members.
Table 10.8. TaskItem Members
=150> =350>Property
Description
Category
The category of the task.
Checked
A Boolean flag indicating whether the task is marked as completed (a check mark appears in the check box next to the task).
Collection
The TaskList collection that the current TaskItem object belongs to.
Description
The description of the task.
Displayed
A Boolean flag indicating whether the task is currently visible in the task list window.
DTE
A reference to the root DTE object.
FileName
The name of the file associated with the task (if any).
IsSettable
By passing in a vsTaskListColumn enum value to this property, you can determine whether that column is editable or not.
Line
The line number associated with the task.
Priority
A vsTaskPriority value indicating the task's priority level. Possible values include vsTaskPriorityHigh, vsTaskPriorityMedium, and vsTaskPriorityLow.
SubCategory
The subcategory of the task.
LinkedWindows
A collection of Window objects representing the windows that are linked together within the same frame.
Object
Returns an object proxy that represents the Window and can be referenced by name.
ObjectKind
A GUID indicating the type of the object returned from Window.Object.
Project
A Project instance representing the project containing the Window object.
ProjectItem
A ProjectItem instance representing the project item containing the Window object.
Selection
Returns an object representing the currently selected item in the Window (for document windows, this might be text; for tool windows, this might be an item in a list, and so on).
Top
The distance, in pixels, between the window's top edge and its parent's top edge.
Visible
A Boolean flag indicating whether the window is visible or hidden.
Width
The width of the window in pixels.
WindowState
Gets or sets the current state of the window (via a vsWindowState enum value: vsWindowStateMaximize, vsWindowStateMinimize, vsWindowStateNormal).
Method |
Description |
---|---|
Delete |
Removes the task from the task list window. |
Navigate |
Causes the IDE to navigate to the location (for example, file and line) associated to the task. |
Select |
Selects or moves the focus to the task within the task list window. |
Listing 10.4 contains a short VB macro demonstrating the use of the TaskList, TaskItems, and TaskItem objects to iterate the tasks and toggle their completed status.
Listing 10.4. Toggling Task Item Completion
Imports EnvDTE Imports EnvDTE80 Imports Microsoft.VisualStudio.CommandBars Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub ToggleAllTasks() ' Reference the windows collection Dim windows As Windows = DTE.Windows ' Pluck the task list window from the collection Dim twindow As Window = _ DTE.Windows.Item(EnvDTE.Constants.vsWindowKindTaskList) ' Convert the window object to a TaskList instance by ' casting its Object property Dim tlist As TaskList = CType(twindow.Object, TaskList) ' Iterate all of the task items in the task list For Each task As TaskItem In tlist.TaskItems ' Toggle the "completed" check mark on each item task.Checked = Not task.Checked Next End Sub End Module |
The ToolBox
Four objects are used to programmatically interface with the Toolbox:
-
ToolBoxAn object representing the Toolbox itself
-
ToolBoxTabsA collection representing the tab panes on the Toolbox
-
ToolBoxItemsA collection representing the items within a tab on the Toolbox
-
ToolBoxItemA discrete item displayed within a Toolbox tab
Figure 10.3 illustrates the Toolbox object hierarchy.
Figure 10.3. ToolBox object hierarchy.
These objects are used primarily to add, remove, or alter the items hosted by the Toolbox. For instance, you can easily add a custom tab to the Toolbox by using the ToolBoxTabs collection:
Dim tBox As ToolBox Dim myTab As ToolBoxTab tBox = DTE.Windows.Item(Constants.vsWindowKindToolbox).Object myTab = tBox.ToolBoxTabs.Add("My TBox Tab")
You can also add items to any tab with the ToolBoxItems.Add method, which accepts a name for the item to add, a "data" object representing the item, and a vsToolBoxItem-Format enum, which specifies the format of the item. The Add method uses the vsToolBoxItemFormat to determine how to interpret the "data" object value. For instance, if you wanted to add a .NET control to the tab created in the previous code snippet, you could accomplish that with just one line of code:
tlBoxTab.ToolBoxItems.Add("ContosoControl", _ "C:\Contoso\Controls\CalendarControl.dll", _ vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent)
Notice that the item, in this case, is represented by a path to the assembly that implements the control and that it has an item format of vsToolBoxItemFormatDotNET-Component.
Listing 10.5 contains a VB function that adds a tab to the Toolbox, adds a control and a text fragment to the tab, and then removes the tab.
Listing 10.5. Adding and Removing Items in the Toolbox Window
Imports EnvDTE Imports EnvDTE80 Imports Microsoft.VisualStudio.CommandBars Imports System.Diagnostics Imports System.Windows.Forms Public Sub AddAToolBoxTab() Dim toolBox As ToolBox Dim tabs As ToolBoxTabs Dim tab As ToolBoxTab Dim tabItems As ToolBoxItems Dim win As Window Try ' Get a reference to the toolbox win = DTE.Windows.Item(Constants.vsWindowKindToolbox) toolBox = win.Object ' Get a reference to the toolbox tabs collection tabs = toolBox.ToolBoxTabs ' Add a new tab to the ToolBox tab = tabs.Add("New ToolBox Tab") ' Make the added tab the active tab tab.Activate() tabItems = tab.ToolBoxItems With tabItems ' Add a piece of text to the toolbox. ' Clicking on the text will add it to ' the active document... .Add("Code Comment", _ "This is some text to add to the toolbox", _ vsToolBoxItemFormat.vsToolBoxItemFormatText) 'Now add a control to the toolbox. 'When adding a control, you need to specify 'the path to the assembly; you can add all 'classes from the assembly (shown below) 'or just one of the classes (see MSDN 'docs for that syntax) .Add("My Login Control", _ "C:\MyComponents\Contoso\LoginControl.dll", _ vsToolBoxItemFormat.vsToolBoxItemFormatDotNETComponent) 'For demonstration purposes, let's remove 'the items that we had just added, and then 'remove the newly created tab... 'Put up a messagebox to confirm the deletes MessageBox.Show("Click OK to delete the tab and added items.", _ "Delete Toolbox Tab Items", MessageBoxButtons.OK, _ MessageBoxIcon.Information) 'Delete the tab tab.Delete() End With Catch ex As Exception MsgBox("Error: " & ex.ToString()) End Try End Sub |
Executing Commands in the Command Window
The command window is a tool window used to execute IDE commands or aliases. IDE commands are essentially ways to tell the IDE to perform some action. Some commands map directly to menu items (such as File Open), whereas others don't have menu equivalents.
The CommandWindow object permits you to programmatically pipe commands into the command window and execute them. You can also output a text string (for informational purposes) to the window and clear its current content:
' Get a reference to the command window Dim cmdWindow As CommandWindow = _ DTE.Windows.Item(Constants.vsWindowKindCommandWindow).Object ' Display some text in the command window cmdWindow.OutputString("Hello, World!") ' Clear the command window cmdWindow.Clear()
Listing 10.6 shows how to programmatically execute commands in the CommandWindow object.
Listing 10.6. Executing Commands in the Command Window
Imports EnvDTE Imports EnvDTE80 Imports Microsoft.VisualStudio.CommandBars Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub ExecCommandWindow() Dim cmdWindow As CommandWindow = _ DTE.Windows.Item(Constants.vsWindowKindCommandWindow).Object ' Display some text in the command window cmdWindow.OutputString("Executing command from the automation OM...") ' Send some command strings to the command window and execute ' them... ' This command will start logging all input/output in the ' command window to the specified file cmdWindow.SendInput("Tools.LogCommandWindowOutput cmdwindow.log", True) ' Open a file in a code editor: ' 1. We use an alias, 'of', for the File.OpenFile command ' 2. This command takes quote-delimited parameters (in this case, ' the name of the editor to load the file in) Dim cmd As String = "of " cmd = cmd & """C:\Contoso\ContosoCommonFramework\Integration\Integration.cs""" cmd = cmd & "/e:""CSharp Editor""" cmdWindow.SendInput(cmd, True) cmdWindow.SendInput("Edit.Find MessageTrxId", True) ' Turn off logging cmdWindow.SendInput("Tools.LogCommandWindowOutput /off", True) End Sub End Module |
Output Window
The output window displays messages generated from a variety of different sources in the IDE. A prime example is the messages generated by the compiler when a project is being built. For a deeper look at the functionality provided by the output window, see Chapter 9, "Debugging with Visual Studio 2005."
The output window is controlled through three objects:
-
OutputWindow is the root object representing the output window.
-
OutputWindowPanes is a collection of OutputWindowPane objects.
-
OutputWindowPane represents one of the current panes within the output window.
Using these objects, you can add or remove panes from the output window, output text to any one of the panes, and respond to events transpiring in the window.
The following VB code fragment retrieves a reference to the output window and writes a test string in the Build pane:
Dim outWindow As OutputWindow = _ DTE.Windows.Item(Constants.vsWindowKindOutput).Object Dim pane As OutputWindowPane = _ outWindow.OutputWindowPanes.Item("Build") pane.OutputString("test")
Using the OutputWindowPane object, you can also add items simultaneously to a specific output pane and the task list window. The OutputWindowPane.OutputTaskItemString method writes text into the output window and simultaneously adds that text as a task to the task list window:
Dim output As String = "Exception handler not found" Dim task As String = "Add exception handler" pane.OutputTaskItemString(output, _ vsTaskPriority.vsTaskPriorityMedium, "", vsTaskIcon.vsTaskIconNone, _ "", 0, task, True)
Because most of the output window actions are conducted against a specific pane, most of the useful methods are concentrated in the OutputWindowPane object. For your reference, the OutputWindowPane members are itemized in Table 10.9.
Table 10.9. OutputWindowPane Members
=150> =350>Property
Description
Collection
The OutputWindowPanes collection that the current OutputWindowPane object belongs to
DTE
A reference to the root DTE object
Guid
The GUID for the output window pane
Name
The name of the output window pane
Textdocument
A Textdocument object representing the window pane's content
Method |
Description |
---|---|
Activate |
Moves the focus to the output window |
Clear |
Clears the contents of the window pane |
ForceItemsToTaskList |
Writes all task items not yet written to the task list window |
OutputString |
Writes a string to the output window pane |
OutputTaskItemString |
Writes a string to the output window pane and simultaneously adds a task to the task list window |
Listing 10.7 demonstrates controlling the output window by adding a new pane to the window, writing text into that pane, and then clearing its content.
Listing 10.7. Writing to the Output Window
Imports EnvDTE Imports EnvDTE80 Imports Microsoft.VisualStudio.CommandBars Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub WriteToOutputWindow() ' Grab a reference to the output window Dim outWindow As OutputWindow = _ DTE.Windows.Item(Constants.vsWindowKindOutput).Object ' Create a new pane in the output window Dim pane As OutputWindowPane = _ outWindow.OutputWindowPanes.Add("New Pane") pane.OutputString("Text in the 'New Pane'") pane.Clear() End Sub End Module |
Linked Windows
Tool windows can be positioned in a variety of ways within the IDE: You can float tool windows around within the overall IDE container; you can dock a tool window to one of the sides of the IDE; you can join windows together, pin and unpin them; and so on (see the section "Managing the Many Windows of the IDE" in Chapter 2, "A Quick Tour of the IDE," for an introduction to window layout).
A linked window refers to two or more tool windows that have been aggregated together. Figure 10.4 shows one common example of this: The Toolbox and Solution Explorer and the Data Sources window have all been joined together in a common frame. Each window that is part of the frame can be viewed by clicking on its tab.
Figure 10.4. Linked windows.
By joining together two or more tool windows, you actually create an additional window objectcalled a linked window or window framethat functions as the container for its hosted tool windows and is available as a part of the DTE.Windows collection.
By using the Window.LinkedWindows and Window.WindowFrame properties and the Windows2.CreateLinkedWindowFrame method, you can programmatically link and unlink any available tool windows. The Visual Basic code in Listing 10.8 demonstrates this process by doing the following:
-
You grab the window objects for the Toolbox window and the Solution Explorer window.
-
You programmatically join these two windows together, effectively creating the linked window that you see in Figure 10.4.
-
After joining the windows together, you get a reference to the newly created linked window and use its LinkedWindows property to unlink the windows that were previously just linked together.
Listing 10.8. Linking and Unlinking Tool Windows
Imports EnvDTE Imports EnvDTE80 Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub LinkUnLink() Dim windows As Windows2 = DTE.Windows ' Grab references to the solution explorer and the toolbox Dim solExplorer As Window2 = _ windows.Item(Constants.vsWindowKindSolutionExplorer) Dim toolbox As Window2 = windows.Item(Constants.vsWindowKindToolbox) ' Use the Windows2 collection to create a linked window/window ' frame to hold the toolbox and solution explorer windows Dim windowFrame As Window2 windowFrame = windows.CreateLinkedWindowFrame(solExplorer, _ toolbox, vsLinkedWindowType.vsLinkedWindowTypeTabbed) ' At this point, we have created a linked window with two tabbed ' "interior" windows: the solution explorer, and the toolbox... MessageBox.Show("Press OK to Unlink the windows", "LinkUnLink", _ MessageBoxButtons.OK, MessageBoxIcon.None) ' To unlink the windows: ' -- Use the window frame's ' -- Remove the window objects from this collection windowFrame.LinkedWindows.Remove(toolbox) windowFrame.LinkedWindows.Remove(solExplorer) End Sub End Module |
Command Bars
A command bar is a menu bar or toolbar; from an object model perspective, these are represented by CommandBar objects. Because menu bars and toolbars are hosted within a window, you reference specific CommandBar objects via the Window object, through the Window.CommandBars property. In turn, every CommandBar plays host to controls such as buttons, drop-downs, and so on. Figure 10.5 shows the Solution Explorer tool window with its command bar highlighted.
Figure 10.5. The Solution Explorer tool window and its command bar.
Note that there are six buttons hosted on the command bar.
Note
Unlike the Windows collection, which holds only an instance of each open window, the CommandBars collection holds instances for every single registered command bar, regardless of whether the command bar is currently being shown in the window.
The VB code in Listing 10.9 queries the CommandBar object for the Solution Explorer window and prints out the CommandBar objects that it finds.
Listing 10.9. Querying the CommandBar Object
Imports EnvDTE Imports EnvDTE80 Imports Microsoft.VisualStudio.CommandBars Imports System.Diagnostics Imports System.Windows.Forms Public Module MacroExamples Public Sub QueryCommandBar() Dim windows As Windows2 = DTE.Windows ' Grab reference to the solution explorer Dim solExplorer As Window2 = _ windows.Item(Constants.vsWindowKindSolutionExplorer) ' Retrieve the solution explorer's command bar object Dim cmdBar As CommandBar = CType(solExplorer.CommandBars(1), CommandBar) ' Start building our output string Dim output As String = "Command bar contains: " + vbCrLf ' Get a reference to the controls hosted in the ' command bar Dim controls As CommandBarControls = cmdBar.Controls ' Count integer Dim i As Integer = 1 ' Iterate the controls in the command bar For Each control As CommandBarControl In controls If control.Enabled Then output = output + i.ToString() + " " + _ control.Type.ToString() + _ ": " + control.Caption + vbCrLf i = i + 1 End If Next MessageBox.Show(output, "Solution Explorer Command Bar", _ MessageBoxButtons.OK) End Sub End Module |
Correlate the results in Figure 10.6 with Figure 10.4: Six buttons are visible on the tool window, and the code has found six items in the CommandBarControls collection (which is returned through the CommandBar.Controls property). Removing the following check against the Enabled property would result in many more controls produced in the message box:
If control.Enabled Then
Figure 10.6. Controls found in the command bar.
Notice in Listing 10.9 that you have to explicitly cast the object returned from the Window.CommandBars property: this is, interestingly, not a strongly typed property, and it returns an Object instead of an actual CommandBars instance.
Tip
Use the CommandBar.Type property to determine whether a command bar is a toolbar or a menu bar. A value of MsoBarType.msoBarTypeNormal indicates that the command bar is a toolbar, whereas a value of MsoBarType.msoBarTypeMenu indicates that the command bar is a menu bar.
The CommandBar object properties and methods are documented in Table 10.10.
Table 10.10. CommandBar Members
=150> =350>Property
Description
AdaptiveMenu
For menu bars, this Boolean flag indicates whether the command bar has adaptive menus enabled. (Adaptive menus, sometimes referred to as personalized menus, are menus that alter their drop-down content based on projected or actual usage by the user; the intent is to display only those commands that are useful on the menu and hide the other nonessential commands.)
Application
An object representing the parent application to the command bar.
BuiltIn
Boolean flag used to distinguish between built-in and custom command bars.
Context
A string indicating where the CommandBar is saved (the format and expected content of this string are dictated by the hosting application).
Controls
A CommandBarControls collection containing CommandBarControl objects; each of these objects represents a control displayed by the command bar.
Creator
An integer value that identifies the application hosting the CommandBar.
Enabled
A Boolean flag indicating whether the command bar is enabled.
Height
The height of the command bar in pixels.
Index
The index of the command bar in the command bar collection.
Left
The distance, in pixels, between the left side of the command bar and the left edge of its parent container.
Name
The name of the command bar.
NameLocal
The localized name of the command bar.
Parent
An object that is the parent of the command bar.
Position
An MsoBarPosition enum value used to get or set the position of the command bar (for example, MsoBarPosition.msoBarTop).
Protection
An MsoBarProtection enum value that identifies the protection employed against used modification (for example, MsoBarProtection.msoBarNoMove).
RowIndex
An integer representing the docking row of the command bar.
Top
The distance, in pixels, between the top of the command bar and the top edge of its parent container.
Type
The type of the command bar (as an MsobarType enum value; for example, MsoBarType.msoBarTypeNormal).
Visible
A Boolean flag indicating whether the command bar is currently visible.
Width
The width of the command bar in pixels.
Method |
Description |
---|---|
Delete |
Removes the command bar from its parent collection. |
FindControl |
Enables you to retrieve a reference to a control hosted by the command bar that fits various parameters such as its type, ID, tag, and visibility. |
Reset |
Resets one of the built-in command bars to its default configuration. |
ShowPopup |
Displays a pop-up representing a command bar. |
Note
Previous versions of Visual Studio actually relied on a Microsoft Office assembly for the CommandBar object definition (Microsoft.Office.Core). Visual Studio 2005 provides its own implementation of the CommandBar object that is defined in the Microsoft.VisualStudio.CommandBars namespace, although you will find some types that carry their nomenclature over from the MS Office assembly, such as the various MsoXXX enums.
Property |
Description |
---|---|
Command ObjectsEvery action that is possible to execute through the menus and toolbars in Visual Studio is generically referred to as a command. For example, pasting text into a window is a command, as is building a project, toggling a breakpoint, and closing a window. For each command supported in the IDE, there is a corresponding Command object; the DTE.Commands collection holds all the valid Command object instances. Each command is keyed by a name that categorizes, describes, and uniquely identifies the command. The "paste" command, for instance, is available via the string key "Edit.Paste". If you wanted to retrieve the Command object mapping to the paste command, you would pull from the Commands collection using that string key: Dim commands As Commands2 = DTE.Commands Dim cmd As Command = commands.Item("Edit.Paste") You can query a command's name via its Name property: ' name would = "Edit.Paste" Dim name As String = cmd.Name Table 10.14 contains the members declared on the Command interface.
Table 10.14. Command Members |
|
Bindings |
The keystrokes that can be used to invoke the command |
Collection |
The Commands collection that the Command object belongs to |
DTE |
A reference to the root-level DTE object |
GUID |
A GUID that identifies the command's group |
ID |
An integer that identifies the command within its group |
IsAvailable |
A Boolean flag that indicates whether the command is currently enabled |
LocalizedName |
The localized name of the command |
Name |
The name of the command |
Method |
Description |
---|---|
AddControl |
Creates a control for the command that can be hosted in a command bar |
Delete |
Removes a named command that was previously added with the Commands.AddNamedCommand method |
The list of all available commands is extremely long (nearly 3,000 total), and it is therefore impossible to cover every one of them here, or even a large portion of them. To get an idea, however, of the specific commands available, you can visit the dialog box used to customize the Visual Studio toolbars. If you select the Customize option from the View, Toolbars menu, and then click on the Commands tab, you can investigate all the various commands by category (see Figure 10.9). Another alternative would be to programmatically iterate the DTE.Commands collection and view them that way. In fact, in the following chapter, we use this as one scenario for showcasing add-in development.
Figure 10.9. Using the Customize dialog box to view commands.
So, although we can't cover all the commands, you can learn how to perform common tasks with the Command objects such as executing a command, checking on a command's current status, and even adding your own commands to the command library.
Executing a Command
Commands can be executed in two different ways. The DTE object has an ExecuteCommand method that you can use to trigger a command based on its name:
DTE.ExecuteCommand("Window.CloseDocumentWindow")
The Commands collection is also a vehicle for launching commands through its Raise method. Instead of using the command's name, the Raise method uses its GUID and ID to identify the command:
Dim commands As Commands2 = DTE.Commands Dim cmd As Command = commands.Item("Window.CloseDocumentWindow") Dim customIn, customOut As Object commands.Raise(cmd.Guid, cmd.ID, customin, customout)
Some commands accept arguments. The Shell command is one example. It is used to launch an external application into the shell environment and thus takes the application filename as one of its parameters. You can launch this command by using the ExecuteCommand method like this:
Dim commands As Commands2 = DTE.Commands Dim cmd As Command = commands.Item("Tools.Shell") Dim arg1 = "MyApp.exe " DTE.ExecuteCommand(cmd.Name, arg1)
The Raise method also works with arguments: The last two parameters provided to the Raise method are used to specify an array of arguments to be used by the command and an array of output values returned from the command.
Mapping Key Bindings
Most commands can be invoked by a keyboard shortcut in addition to a menu entry or button on a command bar. You can set these keyboard shortcuts on a per-command basis by using the Command.Bindings property. This property returns or accepts a SafeArray (essentially an array of objects) that contains each shortcut as an element of the array.
Key bindings are represented as strings with the following format:
"[scopename]::[modifier+][key]".
Scopename is used to refer to the scope where the shortcut is valid, such as Text Editor or Global. The modifier token is used to specify the key modifier such as "ctrl+", "alt+", or "shift+" (modifiers are not required). And the key is the keyboard key that will be pressed (in conjunction with the modifier if present) to invoke the command.
To add a binding to an existing command, you first need to retrieve the current array of binding values, add your binding string to the array, and then assign the whole array back into the Bindings property like this:
Dim commands As Commands2 = DTE.Commands Dim cmd As Command = _ commands.Item("File.SaveSelectedItems") Dim bindings() As Object bindings = cmd.Bindings ' Increase the array size by 1 to hold the new binding ReDim Preserve bindings(bindings.GetUpperBound(0) + 1) ' Assign the new binding into the array bindings(bindings.GetUpperBound(0)) = "Global::Shift+F2" ' Assign the array back to the command object cmd.Bindings = bindings
Note
You can create your own named commands that can be launched from a command bar in the IDE (or from the command window for that matter). The Command object itself is added to the Commands collection by calling Commands.AddNamedCommand. The code that will run when the command is executed will have to be implemented by an add-in. We'll cover this scenario in Chapter 11.
Debugger Objects
The automation object model provides a Debugger object that allows you to control the Visual Studio debugger. A Debugger instance can be obtained through the DTE.Debugger property:
Dim debugger As Debugger debugger = DTE.Debugger
With a valid Debugger object, you can
-
Set breakpoints
-
Start and stop the debugger for a given process
-
Control the various execution stepping actions supported by the debugger such as Step Into, Step Over, and Step Out
-
Issue the Run to Cursor command to the debugger
-
Query the debugger for its current mode (for example, break mode, design mode, or run mode)
The following code starts the debugger if it isn't already started:
Dim debugger As Debugger2 debugger = DTE.Debugger If debugger.CurrentMode <> dbgDebugMode.dbgRunMode Then debugger.Go() End If
Automation Events
If your macro or add-in needs to be notified when a certain event occurs, various event objects are supported in all the automation object categories previously discussed. There are events for windows, events for editors, events for projects, and so on. For every event supported by the IDE, a corresponding class in the automation model allows you to hook the event and take action if the event is raised. The event objects tree is rooted in the DTE.Events property, as depicted in Figure 10.10.
Figure 10.10. Event types.
Because events are handled differently depending on whether you are working with code in an add-in or code in a macro, we will wait until the next chapter to cover the details of handling events. The basic premise, however, is fairly simple: You obtain a reference to the event object that you are interested in and then write an event handler that responds to one of that object's published events.
This code, for instance, is how you might handle the "build complete" event from inside a Visual Basic add-in:
Dim WithEvents bldevents As BuildEvents bldevents = DTE.Events.BuildEvents
After instantiating a BuildEvents object, you now have to write the actual event handler:
Private Sub bldevents_OnBuildDone(ByVal Scope As EnvDTE.vsBuildScope, _ ByVal Action As EnvDTE.vsBuildAction) Handles bldevents.OnBuildDone ' Code to handle the event goes here End Sub
SummaryThe Visual Studio automation object model is a deep and wide API that exposes many of the IDE components to managed code running as a macro or an add-in. In this chapter, we documented how this API is organized and described its capabilities in terms of controlling the Visual Studio debugger, editors, windows, tool windows, solutions, and projects. We also discussed the eventing model exposed by the API and looked at the API's capabilities with regards to accessing the underlying code structure for a project, issuing commands inside the IDE, and editing text documents programmatically. Using the methods and properties expressed on the automation objects, you can automate common tasks in the IDE and extend Visual Studio in ways that address your specific development tool needs. In the next chapter, we will directly build on the concepts discussed here and specifically walk you through the process of building add-ins and writing macros that talk to the automation objects. |