The IDE exposes an object model that allows you to automate many of the tasks that would normally be done manually. The same object model is used by macros, add-ins, and wizards. (Wizards are discussed in the next chapter.)
At the core of the object model is the DTE object. (DTE stands for Development Tools Extensibility. Technically the object's coclass is DTE, and it implements an interface named _DTE, with an underscore. However, this COM-level detail will be hidden from you if you are working with VB.NET.) This object is the gateway into all of the functionality of the IDE.
|
The way in which you obtain a reference to the DTE object will depend on what type of code you are writing. Macros just use a global variable provided by the macro environment called DTE. Add-ins are passed a reference to this object when VS.NET initializes them. (Wizards, which are discussed in the next chapter, also have access to the DTE object in their script files through a global object called dte.) The best way to get a feel for what functionality is available from the DTE object model is to look at the properties available from the DTE object. Table 8-1 lists these properties and shows which sections of this chapter provide further information about the areas of functionality to which the various properties belong.
Property |
Description |
---|---|
ActiveDocument |
The Document object for the document with the input focus. (See Section 8.1.4.) |
ActiveSolutionProjects |
A collection of Project objects, representing the projects currently selected in the Solution Explorer. (See Section 8.1.1.) |
ActiveWindow |
A Window object representing the window with the input focus. (See Section 8.1.2.) |
AddIns |
A collection of AddIn objects representing the add-ins listed under the VS.NET Add-in Manager. (See Section 8.3.) |
CommandBars |
A collection of CommandBar objects representing all of the toolbars and menu bars in the VS.NET UI, including all those currently hidden. (See Section 8.1.2.) |
CommandLineArguments |
A string containing everything on the command line after the program name itself. (Usually empty unless VS.NET was run as part of an automated build script.) |
Commands |
A collection of Command objects, representing actions that can be performed in VS.NET. (See Section 8.1.3.) |
ContextAttributes |
A collection of ContextAttribute objects that allows extra items to be added to the Dynamic Help window. |
CSharpProjects |
A collection of Project objects containing all of the C# projects in the solution. (See Section 8.1.1.) |
Debugger |
A Debugger object representing the VS.NET debugger. (See Section 8.1.5.) |
DisplayMode |
A member of the vsDisplay enumeration indicating whether the UI is in Multiple Document Interface (MDI) mode (vsDisplayMDI) or tabbed mode (vsDisplayTabs). |
Documents |
A collection of Document objects, representing all of the documents currently open in the UI. (See Section 8.1.4.) |
DTE |
The DTE object. This may seem pointlessthis property refers back to itself. However, all of the objects in the DTE object model have a property called DTE allowing you to get a reference back to the DTE object. For the sake of consistency, even the DTE object has this property. |
Edition |
A string indicating which edition of VS.NET is installed (e.g., "Enterprise Architect" for the VS.NET Enterprise Architect edition). |
Events |
The Events object, which provides access to a family of objects that raise event notifications. (See Section 8.1.7.) |
Find |
The Find object, which can perform global search operations. |
FullName |
The full path of the devenv.exe (VS.NET) executable. |
Globals |
A Globals object, storing per-user configuration for add-ins or macros. (Note: there are three objects in the DTE hierarchy that provide a Globals property: the DTE object, Solution objects, and Project objects. They all work in the same way, the only difference being where the data is stored. Section 8.3.1.1 shows the use of the Solution object's Globals property.) |
ItemOperations |
An ItemOperations object that allows common operations to be performed on the object currently selected in the Solution Explorer, such as adding a new or existing item. |
LocaleID |
The locale ID in which VS.NET is running. |
Macros |
A Macros object, representing the macros recorder. (See Section 8.2.1.) |
MacrosIDE |
Returns the DTE object for the macros IDE. (Macros have their own IDE, as described in Section 8.2.2. This IDE has its own DTE object.) |
MainWindow |
A Window object representing the main VS.NET window. (See the Section 8.1.2 section.) |
Mode |
A value from the vsIDEMode enumeration indicating whether VS.NET is in design mode (vsIDEModeDesign) or debugging mode (vsIDEModeDebug). |
Name |
A string whose value is "Microsoft Development Environment' (unless this is the DTE object returned by the MacrosIDE property, in which case the string will be "Visual Studio Macros"). |
ObjectExtenders |
An ObjectExtenders property that manages the installed automation extenders. This provides a mechanism by which third-party vendors can add their own objects into the VS.NET automation model. |
Properties |
A parameterized property that returns Properties objects representing a page of global settings configured in the VS.NET Options dialog. (See Section 8.1.1.2.) |
RegistryRoot |
A string of the registry path VS.NET is using to retrieve its settings. |
SelectedItems |
A collection of currently selected items. (For treelike views such as the Solution Explorer, this will be an array of UIHierarchyItem objectssee Section 8.1.2.) |
Solution |
The Solution object for the currently loaded solution. (See the Section 8.1.1 section.) |
SourceControl |
An object allowing simple source control operations to be performed. (See Section 8.1.6.) |
StatusBar |
A StatusBar object representing the status bar at the bottom of the main VS.NET window. Typically used by long-running macros or add-ins in order to present progress notifications. |
SuppressUI |
Flag indicating whether user interface elements should be suppressedfalse when running VS.NET normally, but true when running a command-line build. |
UndoContext |
Allows sets of operations to be grouped so that they can be undone in a single step. (This is useful for macros that perform lots of individual stepsby default, everything done to documents through the automation model will be undoable one step at a time. This allows higher-level blocks of work to be undone in one step.) |
UserControl |
Flag returning true if the IDE is being used interactively, false if it is under automation control. (UserControl refers to the fact that VS.NET is under the control of the userit has nothing to do with Windows Forms user controls.) |
VBProjects |
A collection of Project objects representing all of the VB.NET projects in the current solution. (See Section 8.1.1.) |
Version |
A string containing VS.NET's version number ("7.10" for VS.NET 2003, "7.00" for VS.NET 2002). |
WindowConfigurations |
A collection of WindowConfiguration objects for each set of window layouts. VS.NET stores several different layouts for windows according to the modethe set of tool windows and toolbars you require tends to be different according to whether you are debugging, editing code, or designing forms, so VS.NET stores each layout separately. |
Windows |
A collection of Window objects representing all of the open document or tool windows. (See Section 8.1.2.) |
The DTE model provides access to many different aspects of VS.NETsome of the objects deal with solutions and projects, some deal with the VS.NET user interface, some deal with source control, and some deal with settings. The most important groups of objects are described in the following sections.
|
As Figure 8-1 illustrates, the DTE model provides an object hierarchy that mirrors the hierarchy of a solution and its projects in the IDE. The DTE object represents the IDE (VS.NET itself), and it has a Solution property, which is an object that represents the currently loaded solution. The Solution object contains a collection of Project objects, one for each project in the solution. Each Project contains a collection of ProjectItem objects that represent the files in the project. Each object in the hierarchy exposes methods and properties that allow you to carry out actions that you would normally perform interactively in the IDE. For example, the Solution object has a Remove method that allows you to remove a project from the solution. This method is the programmatic equivalent to right-clicking on the project in the Solution Explorer and selecting Remove.
Example 8-1 shows how to iterate through all of the items in each project in a solution using C#. (This snippet presumes that there is a field or variable in scope called DTE that contains a reference to the DTE object. Macros have such a property available globally. Add-ins are passed the DTE object during initialization.)
Solution s = DTE.Solution; foreach(Project p in s.Projects) { foreach(ProjectItem pi in p.ProjectItems) { MessageBox.Show("Item {0} in {1} Project of the {2} Solution", pi.Name, p.Name,s.FullName); } }
Although all project types have a great deal in common, there are certain features found only in .NET projects. For example, a .NET project has a list of references to other .NET components, but a Database project would have no use for such settings. To accommodate project-specific functionality, the Project object has a property called Object through which extra features are exposed, when appropriate.
VS.NET uses this facility with .NET projects to provide an object of type VSProject. You can retrieve a VSProject object like this:
Imports EnvDTE Imports VSLangProj ... Dim project As Project project = DTE.Solution.Projects.Item(1) Dim vsProject As VSProject vsProject = project.Object
Note the Import statementsmost of the VS.NET object model is defined in the EnvDTE namespace, but here we also need to import the VSLangProj namespace, as this is where VSProject is defined.
|
The VSProject object provides a References property, which is a collection of Reference objects, one for each reference the project has. It also has a WebReferencesFolder property for web service references. It provides a WorkOffline property, which allows you to work on web projects in a disconnected environment. It also provides a couple of utility methods for managing web service references.
Many of the entities you deal with in VS.NET have properties associated with them. Solutions, configurations, projects, and files all present property sets either in the Properties panel (F4) or the Property Pages dialog (Shift-F4).
Properties present a challenge because the exact set of properties available can varyfor example, a Project object's properties will depend on the type of project. Although in certain special cases this is dealt with by introducing an extra object such as the VSProject object described earlier, the DTE object model has a more extensible way of dealing with properties. All objects that represent items with property sets have a property called Properties. This is a collection of Property objects and is indexed by the name of the property.
The set of properties available depends on the type of objectthe VS.NET documentation provides the full (and extensive) lists for each type. Example 8-2 shows how to use this feature to retrieve the DefaultNamespace property that is present on C#, J#, and VB.NET projects.
Public Function GetNamespace(proj As Project) As String Dim prop As [Property] prop = proj.Properties.Item("DefaultNamespace") Return prop.Value End Function
|
The DTE object itself also has a Properties property, but it works slightly differently. It contains systemwide settings, as configured in the Options dialog (Tools Options). But unlike the other Properties properties, this one is not a collection object. Instead, it is a parameterized property that takes two strings, a Category and a Page. These mostly correspond to the Options dialog's categories and pages. For example, you can access the settings in the Environment category's General page with DTE.Properties("Environment", "General"). However, there are a few documented anomalies. For example, although the Fonts and Colors page is in the Environment category, you must use DTE.Properties("FontsAndColors", "TextEditor") to access these settings.
The DTE object model has two main kinds of objects that represent user interface elements: Window objects and CommandBar objects. Window objects represent windows, such as document editor windows, the Toolbox, the Solution Explorer, the Breakpoint window, and so on. CommandBar objects represent menu bars and toolbars, such as the main menu.
For each visible window, whether it is the main VS.NET window, a document window, or a tool window, there is a corresponding Window object available in the DTE object hierarchy. You can obtain these objects in a number of ways.
The DTE object itself provides two properties that provide direct access to certain windows. Its MainWindow property refers to the main VS.NET window. The ActiveWindow property refers to whichever window currently has the input focus.
The DTE object also provides a Windows property. This is a collection of Window objects and allows access to every window in the VS.NET UI. The property is indexed by the window kind, which is a GUID that indicates the type of window. This GUID would normally be one of those listed in the DTE's Constants enumeration, which defines a series of vsWindowKindXxx values for the built-in window types. (The documentation page entitled "vsWindowKind constants" provides the full list of built-in windows and their corresponding vsWindowKind names.) Example 8-3 shows how to use this collection to obtain the Window object for the Solution Explorer.
Dim wnd As Window wnd = DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer)
|
Once you have a Window object, you can perform various operations on it. As you would expect, anything that can be done interactively can also be done through code. The AutoHides property determines whether the window disappears when it loses the focusthis corresponds to the pushpin icon on the window. The IsFloating property determines whether the window is currently docked. The Top, Left, Width, and Height properties allow the window's size and position to be set when it is undocked. The Visible property determines whether it is shown at all. The Activate method gives the window the focus.
If the window is an editor window, you can access the associated document through its Document property. Certain window types provide an extra programming interface, which is available from the Window object's Object property. All of the windows that show a tree view (e.g., the Solution Explorer or the class view) use this to provide an object of type UIHierarchy. UIHierarchy objects provide a GetItem method that allows access to any item in the tree. It also provides SelectUp and SelectDown methods for navigation and a DoDefaultAction method to allow a double-click to be simulated.
Example 8-4 shows the use of the UIHierarchy object. It obtains the Window object for the Solution Explorer and then retrieves the UIHierarchy object. It then calls GetItem on this to retrieve the item representing the MyProject project in the MySolution solution. It then calls Select on this, in order to make that the currently selected item.
Dim wnd As Window wnd = DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer) Dim uih As UIHierarchy uih = wnd.Object Dim uihItem as UIHierarchyItem uihItem = uih.GetItem("MySolution\MyProject") uihItem.Select(vsUISelectionType.vsUISelectionTypeSelect)
CommandBar objects represent menus or toolbars. There is no distinction between a menu bar and a toolbarbuttons can be dragged onto the menu bar, and menu items can be dragged onto button bars.
|
The DTE object has a CommandBars property. This is a collection that contains every command bar in the VS.NET UI. (It includes any that are currently invisible, as well as all the visible ones.) The collection is indexed by the name of the command bars. It also provides an Add method that allows you to create new command bars.
CommandBar objects provide various properties that let you control their appearance and contents. Example 8-5 shows how to locate the Standard command bar (one of the built-in VS.NET toolbars) from the DTE object's CommandBars collection. It then toggles the bar's position between being docked to the top of the screen and floating.
Imports Microsoft.Office.Core Public Module MyModule Public Sub AddToolbar( ) Dim cmdBar As CommandBar cmdBar = DTE.CommandBars.Item("Standard") If cmdBar.Position = MsoBarPosition.msoBarTop Then cmdBar.Position = MsoBarPosition.msoBarFloating Else cmdBar.Position = MsoBarPosition.msoBarTop End If End Sub End Module
The most interesting property of any CommandBar object is the Controls property. This is a collection of CommandBarControl objects, one for each item on the bar. There are several different types of control. You can find out which type any particular control is from its Type property, which will return an item from the msoControlType enumeration. Menus have a type of msoControlPopup, and the objects that represent menus can be cast to the CommandBarPopup type. Leafs in a menu and buttons on a toolbar both have the type msoControlButton. Objects in the bar's Controls collection that have this type can be cast to the CommandBarButton type. Example 8-6 shows how to navigate through a tree of pop ups in a command barin this case we are using the main menu in VS.NET, which is a command bar called "MenuBar". Example 8-6 locates the File menu and then the Source Control submenu, before executing the Open from Source Control... menu item.
Imports Microsoft.Office.Core Public Module MyModule Public Sub UseCommandbar( ) Dim cmdBar As CommandBar Dim ctl As CommandBarControl Dim cmdPopup As CommandBarPopup Dim cmdButton As CommandBarButton cmdBar = DTE.CommandBars.Item("MenuBar") ctl = cmdBar.Controls("File") If ctl.Type = MsoControlType.msoControlPopup Then cmdPopup = ctl ctl = cmdPopup.Controls("Source Control") If ctl.Type = MsoControlType.msoControlPopup Then cmdPopup = ctl ctl = cmdPopup.Controls("Open From Source Control...") If ctl.Type = MsoControlType.msoControlButton Then cmdButton = ctl cmdButton.Execute( ) End If End If End If End Sub End Module
In fact, this code is unnecessarily complexnavigating through toolbars is required only if you wish to modify them in some way. If you merely wish to execute the command they represent, you should just use the corresponding Command object. You also need to use a Command object if you want to add an item to a command bar that actually does somethinga command bar button must be associated with the command that it invokes.
Most user actions in VS.NET are associated with a command. There are commands for every action in the editor, such as entering text or moving the cursor. Each dialog has a command that opens it. Every action accessible through toolbars and menus is associated with a command.
|
Every command has a corresponding Command object, which can be obtained through the DTE object's Commands collection. Commands are identified by name, available from the Command object's Name property. This name can also be used to invoke a command with the DTE object's ExecuteCommand method. Example 8-7 shows the more succinct way of invoking the same command that Example 8-6 executes.
DTE.ExecuteCommand("File.OpenFromSourceControl")
If you want to add an item to a toolbar menu that invokes a particular command, you simply obtain the relevant command object and call its AddControl method, passing in a reference to the command bar to which you would like to add a control. Example 8-8 shows how to add a button for the OpenFromSourceControl command as the fourth item in the Standard toolbar.
Dim cmd As Command cmd = DTE.Commands.Item("File.OpenFromSourceControl") cmd.AddControl(DTE.CommandBars("Standard"), 4)
You can create your own custom command objects, although you will need to write an add-in to provide code that will run when the command is executed. This is done with the DTE object's Commands collection, which has an AddNamedCommand method. This allows you to create a command, specifying the name, the text that should be used for this command on command bars, optional tooltip text, and the bitmap that should be used to represent the command on any command bar. The VS.NET Add-in Wizard described later in this chapter can generate code to add a new command and attach it to the Tools menu for you.
Every document open for editing in VS.NET has a corresponding Document object, which allows the document's contents to be manipulated. If the document is a text file, the Document object's Object property will return a TextDocument object, which provides operations specific to text files.
The DTE object provides two properties through which you can obtain a Document object. The ActiveDocument property returns the document that has the focus (or, if a tool window currently has the focus, the document that most recently had the focus). The Documents property is a collection of all open documents.
Most manipulation of a document is done through the document's Selection property. For a text document, this will be a TextSelection object. This represents the current selection, or, if there is no selection, the cursor location. It provides methods equivalent to the keystrokes for navigating around documentsfor example, the LineUp, LineDown, CharLeft, CharRight, PageUp, PageDown, StartOfDocument, and EndOfDocument methods. Each of these takes a Boolean indicating whether the operation should extend the current selection or not. (This is equivalent to whether or not you hold down the Shift key when using the corresponding keystroke.) An Insert method inserts text at the current cursor location. Cut, Copy, and Paste methods correspond to the standard clipboard operations.
The DTE object provides a property called Debugger. This is an object that allows the debugger to be controlled. This provides a Breakpoints collection, allowing breakpoints to be created, destroyed, or modified. For multiprocess and multithreaded debugging, it allows the current process and thread to be retrieved or set using the CurrentProcess and CurrentThread properties. It provides methods that correspond to each of the debugger actions. (See Chapter 3 for more information on debugging.) Example 8-9 shows how to use the Debugger object to step into the current line of code.
Dim dbg As EnvDTE.Debugger dbg = DTE.Debugger dbg.StepInto( )
The DTE object provides a SourceControl property. This is an object that allows certain source control operations to be performed. Unfortunately, it is fairly primitive. All operations use filenamesyou cannot pass a ProjectItem object in, for example. And you cannot check items inyou can only perform four source control operations.
You can discover whether items are under source control at all with the IsItemUnderSCC method. You can call the IsItemCheckedOut method to discover whether an item is already checked out. You can exclude items from source control with ExcludeItem or ExcludeItems. And you can check items out with the CheckoutItem or CheckoutItems methods.
The DTE object model is able to notify us when certain events happen. These events are raised through the standard COM notification mechanism (connection points). Events are grouped into categories, and as Figure 8-2 shows, each category has a corresponding event source object. (The objects shown with bold names are event sources. The other objects indicate how to navigate through the DTE object hierarchy to find the event sources.) Most of these objects are accessed through the DTE object's Events property. For example, build events are raised by the DTE.Events.BuildEvents object.
VSProject objects supply extra events specific to .NET projects through their VSProjectEvents objects. (VSProject objects are available on .NET projects, and are accessed through the associated Project object's Object property. Project objects can be accessed through the DTE.Solution.Projects collection.) These projects also provide project-specific events for individual items through the VSProjectItemEvents objects.
Add-ins can use normal COM event handling to deal with events from these objects, but macros must use their own technique. This is discussed in the next section, Section 8.2; see Example 8-13 for an illustration of the technique.