Chapter 9: Programming the Visual Studio User Interface


Microsoft® Visual Studio® is made up of many different windows that show data to the user, including the Task List, Solution Explorer, and the Microsoft Windows® Forms designer. You can manipulate these windows not only by using the mouse and keyboard but also through the object model by using a macro or an add-in. In this chapter, we'll discuss the many objects you can program in the user interface of Visual Studio.

Window Basics

The user interface for each window in Visual Studio is different from that of other windows, but they all share a few basic methods and properties. Let's look at the common parts of the object model.

The Windows Collection

Visual Studio contains a number of tool and document windows that you can access through the automation model. Each of these windows is represented in the object model by a Window object and can be found in the Windows collection, which is accessible through the DTE.Windows property.

You can retrieve a Window object from the Windows collection in a number of ways. One way is to use the enumerator to walk the list of all available windows, as shown here:

     Sub EnumWindows()         Dim window As EnvDTE.Window         For Each window In DTE.Windows             MsgBox(window.Caption)         Next     End Sub 

Or you can use the numerical indexing method:

     Sub EnumWindows2()         Dim window As EnvDTE.Window         Dim i As Integer         For i = 1 To DTE.Windows.Count             MsgBox(DTE.Windows.Item(1).Caption)         Next     End Sub 

However, using these formats for finding a window isn't optimal because you usually want to find one specific window, and looking at all the windows to find it is a waste of CPU cycles. The numerical indexing method isn't always best because the position of a window from one instance of Visual Studio to the next might change, so you can't rely on using an index to return a specific Window object. In fact, you have no guarantee that calling the Item method twice in a row by using a numerical index will return the same EnvDTE.Window object because new windows might be created in between calls to this method. In addition, the numerical indexing method might not find all the available windows. For example, creating a tool window can be an expensive operation. To increase performance, Visual Studio won't create a tool window until one is specifically asked for, and, because the numerical indexing method looks only for windows that have been created, a particular tool window might not be found.

A simple experiment shows how iterating through the list of all tool windows slows down your code if all tool windows haven't been created. By default, the Server Explorer tool window is docked and hidden on the left side of the Visual Studio main window. If you move the mouse pointer over the icon for this window, the Server Explorer window appears. If this window hasn't yet been shown for that instance of Visual Studio, you'll see a delay of a couple seconds while the window is created before being shown for the first time. If you run the EnumWindows2 macro and some of the Window objects need to be created, creating those windows will consume a lot of processor time, causing the macro to run very slowly.

Another way to find a window is to index the Windows collection by using the name of the window. The following macro demonstrates this approach; it uses the name of the Task List tool window to find the Window object for the Task List.

     Sub FindTaskListWindow()         Dim objWindow As EnvDTE.Window         objWindow = DTE.Windows.Item("Task List")     End Sub 

This is also not the best way of finding a particular Window object, as this example clearly shows. During a search for a window, the string passed to the Windows.Item method is compared with the title of each window until a window with a matching title is found. If you right-click on the Task List and choose Show Tasks | Comment, the title of this window becomes "Task List – X Comment tasks shown (filtered)," where X is a number. Because the string Task List passed to the Item method doesn't exactly match the title of the Task List window, the code Windows.Item("Task List") won't find the Window object. This isn't to say that you can't use the title indexing method in some situations. Some windows, such as the Properties window or Object Browser window, have names that don't change (unless the user is using a different language), and you can find such windows by using the window title as the index. Another reason why passing the title of a window isn't the best choice for the Item method is because, just as in the case of a numerical index, if the tool window hasn't been created, the Window object won't be found.

The best way to find a Window object is to use an index that is unique and independent of both the position within the Windows collection and the title of the window. Each tool window has a constant globally unique identifier (GUID) assigned to it; you can pass this GUID to the Item method to find the window you need. Because a GUID might be hard to remember, most of the tool windows that Visual Studio can create have constants defined that are easier to remember and recognize. These constants all start with the prefix vsWindowKind and are static (shared if you're using the Visual Basic language) members of either the EnvDTE.Constants class or the EnvDTE80.WindowKinds class. The following macro finds the Task List tool window:

     Sub FindTaskListWindow2()         Dim objWindow As EnvDTE.Window         objWindow = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindTaskList)     End Sub 

Because the GUID is unique to a specific tool window and doesn't change over time, you don't need to worry about either the caption of a window or its position within the EnvDTE. Windows collection changing. One other benefit of using the GUID is that even if the window you're searching for hasn't yet been created, Visual Studio is advanced enough to create the tool window when it's requested.

You might occasionally run across a window that doesn't have a constant GUID defined for it. The Source Control Explorer window is an example. When you need to find such a window, you can use the GUID in the form of a string in place of one of the predefined constants, as shown in the following example, which retrieves the Window object for the Source Control Explorer window:

     Sub FindTheSourceControlExplorerWindow()         Dim window As EnvDTE.Window         window = DTE.Windows.Item("{}")     End Sub 

You can find the GUID that can be passed to the Item method by using the ObjectKind property. The following macro takes this approach to display the GUID for the Favorites window:

     Sub FindTheSourceControlExplorerWindow2()         Dim window As EnvDTE.Window         'You should show the Source Control Explorer window         ' before calling this code!         window = DTE.Windows.Item("Source Control Explorer")         MsgBox(window.ObjectKind)     End Sub 

When you run this macro, the GUID for the Source Control Explorer window is displayed in a message box. You can then define a constant set to this GUID, and use this constant in any code that needs to find this window. This is how we found the GUID for the FindTheSourceControlExplorerWindow macro.

Using the Object Property

Many windows in Visual Studio have an object model that you can use to manipulate the data contained in that window. You can find these window-specific objects by using the Object property of the Window object. For example, calling the Object property of the Window object for the Task List window returns the TaskList object, which allows you to enumerate, add, remove, and change properties of task items in the Task List window. The following macro retrieves the TaskList object:

     Sub GetTaskListObject()         Dim window As EnvDTE.Window         Dim taskList As EnvDTE.TaskList         window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindTaskList)         taskList = CType(window.Object, EnvDTE.TaskList)     End Sub 

A number of types are available as the programmable object for the different windows, not just the TaskList object, as shown in the macro. Table 9-1 lists the GUID constant you pass to the Item method to find a Window object, as well as the programmable object for that window.

Table 9-1: Windows and Their Programmable Objects

Window

GUID Constant

Object Type

Command Window

vsWindowKindCommandWindow

EnvDTE.CommandWindow

Macro Explorer

vsWindowKindMacroExplorer

EnvDTE.UIHierarchy

Output window

vsWindowKindOutput

EnvDTE.OutputWindow

Server Explorer

vsWindowKindServerExplorer

EnvDTE.UIHierarchy

Solution Explorer

vsWindowKindSolutionExplorer

EnvDTE.UIHierarchy

Error List

vsWindowKindErrorList

EnvDTE80.ErrorList

Task List

vsWindowKindTaskList

EnvDTE.TaskList

Toolbox

vsWindowKindToolbox

EnvDTE.ToolBox

Web browser window

vsWindowKindWebBrowser

SHDocVw.WebBrowser

Text editor

<None>

EnvDTE.TextWindow

Forms designer

<None>

System.ComponentModel.Design.IDesignerHost

HTML designer

<None>

EnvDTE.HTMLWindow

Not only do some of the tool windows in Visual Studio have an object model, but a couple of the document windows have an object model, as well. The Window.Object property of the text editor, .NET Forms designer, and HTML designer windows returns an object appropriate for programming that window object. The object for programming the .NET Forms designer windows is discussed later in this chapter; the objects for programming the text editor and HTML editor windows are discussed in Chapter 10.

Shortcuts to Common Tool Windows

Although acquiring the specific object behind a tool window is not too terribly complicated, Visual Studio makes it easy to get to some of the most common tool window objects by using the ToolWindows object. This object gives you direct access to the object behind the Command Window, the Output window, Solution Explorer, Error List, Task List, and Toolbox. The ToolWindows object can be found on the DTE2 object with code such as this very simple macro:

     Sub FindTaskList()         Dim taskList As TaskList         taskList = CType(DTE, DTE2).ToolWindows.TaskList     End Sub 

The ToolWindows object also gives you quick access to the specific object of other tool windows with the GetToolWindow property. This property accepts as a parameter the GUID for a tool window—the same GUID you would pass to the Windows.Item method, and returns the same value returned from a window's Object property, meaning that this line of code:

 taskList = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindTaskList).Object 

is equivalent to this line of code:

 taskList = CType(DTE, DTE2).ToolWindows.GetToolWindow _ (EnvDTE.Constants.vsWindowKindTaskList) 

The Main Window

Each tool and document window in Visual Studio has a Window object available. However, Visual Studio is also a window, so it's only fair that a Window object be available for that window, as well. Rather than indexing the EnvDTE.Windows collection to find this Window object, you use the MainWindow property of the DTE object:

     Sub FindTheMainWindow()         Dim mainWindow As EnvDTE.Window         mainWindow = DTE.MainWindow     End Sub 

When you work with the Window object for the Visual Studio main window, a few methods and properties don't work as they do when you work with tool or document Window objects. The differences between tool and document Window objects and the Window object for the main window are as follows:

  • The Document, Selection, Object, ProjectItem, and Project properties return null if you're using Microsoft Visual C#® or Visual J#®, and they return Nothing if you're using Microsoft Visual Basic®.

    The set versions of the Caption and Linkable properties generate an exception if called.

  • IsFloating and AutoHides generate an exception if you call the get or set versions of these properties.

  • The Close method generates an exception if called.

Whereas a number of methods and properties don't work on the Window object for the main window, one property is available only for the main window. If an add-in or a macro needs to display a dialog box, you should supply a parent window when the dialog box is shown to correctly manage focus and set the "modalness" of the new window. You can use the main Visual Studio window as the parent window by calling the Window.HWnd property. This property returns a handle to a window—a Windows platform SDK HWND data type. This property is hidden, so when you develop your add-in or macro, it doesn't appear within statement completion. Because the .NET Framework can't use HWND values as a parent, this handle must first be wrapped by a class that implements an interface that the .NET library can accept as a parent. You can implement this interface, System.Windows.Forms.IWin32Window, on your add-in class or on a separate class within a macro project. The IWin32Window interface has one property named Handle; this property returns a System.IntPtr, which contains the handle to a parent window and, in this case, is the value returned from the Window.HWnd property. When it's time to show a form by using the Form.ShowDialog method, you can pass the class that implements the IWin32Window as an argument to this method.

To implement IWin32Window for an add-in, you must first add it to the interface list for your add-in, as shown here:

 public class Connect : Object, Extensibility.IDTExtensibility2, System.Windows.Forms.IWin32Window 

Next, you add the implementation of the Handle property:

 //Implementation of the IWin32Window.Handle property: public System.IntPtr Handle {      get      {          return new System.IntPtr (applicationObject.MainWindow.HWnd);      } } 

Finally, you can display a form (assuming that a form class named Form1 exists within an add-in project) by using code such as this:

 Form1 form1 = new Form1(); form1.ShowDialog(this); 

Implementing this interface within a macro is even easier; the macro samples project that is installed with Visual Studio already contains the code for a class that implements this interface. Located in the Utilities module of the Samples project, this class, named WinWrapper, can be instantiated and passed to any code that requires a parent window, such as the standard Open File dialog box:

 Sub ShowFileOpenDialog()     Dim openFile As New OpenFileDialog     openFile.ShowDialog(New WinWrapper) End Sub 

All you do is copy the WinWrapper class into your macro project, and it's ready to use.




Working with Microsoft Visual Studio 2005
Working with Microsoft Visual Studio 2005
ISBN: 0735623155
EAN: 2147483647
Year: 2006
Pages: 100

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