- Extend and customize tool windows
- Tool windows
- Extend existing tool windows
- Add a tool window
- Prerequisites
- Create a tool window
- Add a control to the tool window
- Add a toolbar to the tool window
- Add a command to the toolbar
- Add a MediaPlayer property to FirstToolWindowControl
- Instantiate the tool window and toolbar
- To implement a menu command in the tool window
- Set the default position for the tool window
- Test the tool window
- Visual Studio tool windows
- Contents
- Introduction
- Registering and initializing user tool windows
- Registering, initializing and invoking a tool window in VSPackage
- Creating and invoking a window from Add-In extension
- Implementing a user toolwindow in a VSPackage module
- Hosting user components
- Handling tool windows events
- Controlling window state
- References
- Other articles in this series
Extend and customize tool windows
Visual Studio provides several different types of windows, for example tool windows, document windows, and dialog windows. Other windows such as the Properties window, the Output window, and the Task List window, are types of tool windows.
Tool windows
Visual Studio tool windows are usually read-only windows that are not file-based. In this they differ from document windows, which display files in read-write mode. The Toolbox, Solution Explorer, Properties window, and Web Browser are examples of tool windows.
To find out how to create a simple tool window, see Add a tool window.
To register a tool window with Visual Studio, see Register a tool window.
Tool windows are single-instance by default, meaning that only one instance of the tool window can be open at a time. After a single-instance tool window is opened, it remains open until the IDE is closed. When you close a single-instance tool window, only its visibility changes. You can also create multi-instance tool windows, such that multiple instances of the window can be open simultaneously. See Create a multi-instance tool window for more information.
Tool windows can be dynamic, meaning that they are visible whenever their related UI context applies. The use of auto-visibility can reduce the clutter of windows in the IDE. For more information, see Open a dynamic tool window.
Tool windows can be docked, floating, or tabbed in the document frame. The tool window frame is provided by the IDE and is used to control the size, location, docking state, and other persistent properties. The tool window pane displays the contents. The default size and location apply only when the tool window is first opened; after that the tool window state is persisted.
Tool window panes can host WPF user controls and support toolbars. You can override the Window property to return the handle of the hosted control.
You can add many different features to tool windows. For example, you can add a toolbar: Add a toolbar to a tool window or a shortcut menu: Add a shortcut menu in a tool window. You can add a Search control that allows you to search items inside your tool window: Add search to a tool window.
You can subscribe to tool window events: Subscribe to an event.
Extend existing tool windows
You can add information about your tool window to a new Options page and a new setting on the Properties page, write to the Task List and Output windows. For more information, see Extend the Properties, Task List, Output, and Options windows.
Add a tool window
In this walkthrough you learn how to create a tool window and integrate it into Visual Studio in the following ways:
Add a control to the tool window.
Add a toolbar to a tool window.
Add a command to the toolbar.
Implement the commands.
Set the default position for the tool window.
Prerequisites
The Visual Studio SDK is included as an optional feature in Visual Studio setup. For more information, see Install the Visual Studio SDK.
Create a tool window
Create a project named FirstToolWin using the VSIX template, and add a custom tool window item template named FirstToolWindow.
For more information about creating an extension with a tool window, see Create an extension with a tool window.
Add a control to the tool window
Remove the default control. Open FirstToolWindowControl.xaml and delete the Click Me! button.
In the Toolbox, expand the All WPF Controls section and drag the Media Element control to the FirstToolWindowControl form. Select the control, and in the Properties window, name this element mediaElement1.
Add a toolbar to the tool window
By adding a toolbar in the following manner, you guarantee that its gradients and colors are consistent with the rest of the IDE.
In Solution Explorer, open FirstToolWindowPackage.vsct. The .vsct file defines the graphical user interface (GUI) elements in your tool window by using XML.
In the section, find the node whose name attribute is guidFirstToolWindowPackageCmdSet . Add the following two elements to the list of elements in this node to define a toolbar and a toolbar group.
Just above the section, create a section that resembles this:
There are several different kinds of menu. This menu is a toolbar in a tool window, defined by its type attribute. The guid and id settings make up the fully qualified ID of the toolbar. Typically, the
of a menu is the containing group. However, a toolbar is defined as its own parent. Therefore, the same identifier is used for the and
elements. The priority attribute is just ‘0’.
Toolbars resemble menus in many ways. For example, just as a menu may have groups of commands, toolbars may also have groups. (On menus, the command groups are separated by horizontal lines. On toolbars, the groups are not separated by visual dividers.)
Add a section that contains a element. This defines the group whose ID you declared in the section. Add the section just after the section.
By setting the parent GUID and ID to the GUID and ID of the toolbar, you add the group to the toolbar.
Add a command to the toolbar
Add a command to the toolbar, which is displayed as a button.
In the section, declare the following IDSymbol elements just after the toolbar and toolbar group declarations.
Add a Button element inside the section. This element will appear on the toolbar in the tool window, with a Search (magnifying glass) icon.
Open FirstToolWindowCommand.cs and add the following lines in the class just after the existing fields.
Doing this makes your commands available in code.
Add a MediaPlayer property to FirstToolWindowControl
From the event handlers for the toolbar controls, your code must be able to access the Media Player control, which is a child of the FirstToolWindowControl class.
In Solution Explorer, right-click FirstToolWindowControl.xaml, click View Code, and add the following code to the FirstToolWindowControl class.
Instantiate the tool window and toolbar
Add a toolbar and a menu command that invokes the Open File dialog and plays the selected media file.
Open FirstToolWindow.cs and add the following using directives:
Inside the FirstToolWindow class, add a public reference to the FirstToolWindowControl control.
At the end of the constructor, set this control variable to the newly-created control.
Instantiate the toolbar inside the constructor.
At this point, the FirstToolWindow constructor should look like this:
Add the menu command to the toolbar. In the FirstToolWindowCommand.cs class, add the following using directive:
In the FirstToolWindowCommand class, add the following code at the end of the ShowToolWindow() method. The ButtonHandler command will be implemented in the next section.
To implement a menu command in the tool window
In the FirstToolWindowCommand class, add a ButtonHandler method that invokes the Open File dialog. When a file has been selected, it plays the media file.
In the FirstToolWindowCommand class, add a private reference to the FirstToolWindow window that gets created in the FindToolWindow() method.
Change the ShowToolWindow() method to set the window you defined above (so that the ButtonHandler command handler can access the window control. Here is the complete ShowToolWindow() method.
Add the ButtonHandler method. It creates an OpenFileDialog for the user to specify the media file to play, and then plays the selected file.
Set the default position for the tool window
Next, specify a default location in the IDE for the tool window. Configuration information for the tool window is in the FirstToolWindowPackage.cs file.
In FirstToolWindowPackage.cs, find the ProvideToolWindowAttribute attribute on the FirstToolWindowPackage class, which passes the FirstToolWindow type to the constructor. To specify a default position, you must add more parameters to the constructor following example.
The first named parameter is Style and its value is Tabbed , which means that the window will be a tab in an existing window. The docking position is specified by the Window parameter, n this case, the GUID of the Solution Explorer.
For more information about the types of windows in the IDE, see vsWindowType.
Test the tool window
Press F5 to open a new instance of the Visual Studio experimental build.
On the View menu, point to Other Windows and then click First Tool Window.
The media player tool window should open in the same position as Solution Explorer. If it still appears in the same position as before, reset the window layout (Window / Reset Window Layout).
Visual Studio tool windows
Contents
This article covers the extension of Visual Studio IDE through integration of a custom user toolwindow into the environment. Discussed are the issues of window registration and initialization in VSPackage and Add-In plug-in modules, hosting of user components and handling of window’s events and states.
This article is obsolete. You can read the new version of this article here.
Introduction
Tool windows are child windows of Visual Studio MDI (Multiple Document Interface) interface and they are responsible for presenting various pieces of information to the user. Solution Explorer and Error List are the examples of tool windows. Usually tool windows’ contents are not associated with any files and do not contain any editors, as separate document windows are reserved for such tasks.
For instance, PVS-Studio extension package integrates several tool windows into the IDE, with Output Window being the primary one. All other of its tool windows can be opened from this main window, as, for example, a search window for the grid. PVS-Studio Output Window itself can be opened from Visual Studio main menu (PVS-Studio -> Show PVS-Studio Output Window), but it also will be invoked automatically each time the analysis starts.
In most cases IDE creates and utilizes just a single instance for each one of its toolwindows, and this instance will be preserved until IDE itself needs to shut down. Therefore, pressing the ‘close’ button on a tool window does actually hide it, and when this window is invoked for the second time, it becomes visible again, thus preserving any data that it contained before being ‘closed’. But still, is it possible to crate Multi-Instance toolwindows in the IDE, which are the windows that can exist in several instances at once. A toolwindow can also be associated with a certain UI context (as the so called dynamic window), and such window will be automatically displayed when the user enters this context.
Integration of a tool window into the IDE is supported by VSPackage and Add-In extensions (although the methods for it are different); it requires the specification of the window’s initial settings and its registration in the system registry.
Registering and initializing user tool windows
A VSPackage project template that is installed together with Visual Studio SDK allows you to create a sample tool window in the extension project which this template generates. Such a project should already contain all of the basic components which will be described below, so it could be conveniently used as a sample for experimenting with Visual Studio toolwindow integration process for VSPackage plug-ins.
Registering, initializing and invoking a tool window in VSPackage
Registering a custom user window in the environment requires writing of the data that defines this window into a special section of Visual Studio registry hive. This process can be automated by generating a pkgdef file that can contain all of the required window registration information. The contents of this pkgdef files can be specified through special registration attributes of your Package subclass.
The immediate registration of a user-created tool window into VSPackage extension is handled by ProvideToolWindow attribute of Package subclass:
Let’s examine several parameters of this attribute. The ‘Typeof’ parameter points to user implementation of the window’s client area (a subclass of ToolWindowPane). The ‘MultiInstances’ parameter enables the Multi-Instance mode for a window, in which multiple instances of the window can be opened simultaneously. The Orientation, Size and Style parameters specify the initial position of a window when it is opened for the first time by the user. It should be noted that the position specified by these parameters will only be used once, when a tool window is displayed for the first time; at all of the subsequent iterations of opening this window, the IDE will be restoring its screen position from the previous one, that is the position before a window was closed. The ‘Transient’ parameter indicates whether the window will be automatically opened after Visual Studio environment is loaded in case it already have been opened during the previous session of the IDE.
It should also be remembered that the initialization of a user window by VSPackage (the initialization itself will be covered later) does not necessarily occur at the same moment as the initialization of a Package subclass for which we provided this registration attribute. For example, after implementing a tool window for PVS-Studio plug-in, we’ve encountered an issue in which our custom window was automatically opened (but not focused/displayed) and placed among other window tabs at the bottom of the main window, and it was done immediately after Visual Studio started up, even though we’ve passed the ‘Transient=true’ parameter to the ProvideToolWindow attribute. Although the plug-in itself is always initialized at IDE start-up, the window had not been fully initialized until after a first call to it, which was evident by the corrupted icon on aforementioned tab.
A dynamic visibility context can be specified for a window by the ProvideToolWindowVisibility attribute:
In this example, the window is set to be automatically displayed when the user enters the «Solution Exists» UI context. Take a note that each one of user’s toolwindow requires a separate attribute and a window’s type should be passed as a first argument to it.
The FindToolWindow method of a Package subclass can be utilized to create and display a toolwindow from a VSPackage extension. This method returns a reference to the specified toolwindow object, creating it if necessary (for instance, in case a single-instance window is called for a first time). Following is the example of invoking a single-instance toolwindow:
In this example, the window will be created in case it is called for the first time, or the window will be made visible in case it had been created before and then hidden. The FindToolWindow ‘s third argument of the bool type specifies whether a new instance of a window should be created if the method was unable to find an already existing one.
To create a Multi-Instance tool window, the CreateToolWindow method can be used. It allows the creation of a window with a pre-defined identifier. An example of invoking such window:
Note that in this example the FindToolWindow method receives ‘false’ value as its third argument, i.e. we are searching for an unoccupied index before initializing a new window instance.
As was mentioned above, the environment will preserve position of a window after it is closed. But if, for whatever reason, it is necessary to specify the size and position of a window, it could be achieved through the SetFramePos method of the IVsWindowFrame interface:
A call to the SetFramePos() should always be made only after the Show() method is executed.
Creating and invoking a window from Add-In extension
A user tool window can be initialized from an Add-In extension with the help of the EnvDTE Window2 interface:
In the example above, a user toolwindow was created using the MyToolWindowControl.MyUserControl as a client area control. The MyToolWindowControl.MyUserControl class could either be located in the same assembly as the add-in that initializes it, or it could be provided by a stand-alone assembly with a full COM visibility (though the ‘Register for COM Interop’ option in project settings). The regular composite UserControl subclass could be utilized as MyUserControl.
Implementing a user toolwindow in a VSPackage module
Tool window consists of a frame border and a client area. A frame is provided by the environment and is responsible for performing docking with other interface objects of the environment, as well as for size and position of the window itself. A client area is a pane, controlled by a user, which houses the contents of a window. Tool windows can host user-created WinForms and WPF components and are capable of handling regular events, such as OnShow , OnMove, etc.
A user toolwindow, or its client area to be more precise, can be implemented by inheriting the class representing a standard empty IDE window — ToolWindowPane.
The Guid attribute is used to uniquely identify each custom user window. In case a plug-in module creates several windows of different types, each one of them should be identified by its own unique Guid. A ToolWIndowPane subclass can be subsequently modified and host user-controlled components.
Hosting user components
A base ToolWindowPane class implements an empty tool window of the environment. Inheriting form this class allows hosting user-created WinForms or WPF components.
Up until Visual Studio 2008 version, toolwindows only provided a native supported for WinForms user components, although it still was possible to host WPF components through the WPF Interoperability ElementHost object. Starting from Visual Studio 2010, toolwindows themselves are based on WPF technology, although they still provide a backward compatibility for hosting of WinForms components.
To host a user-created WinForms component inside a user toolwindow, the Window property of the ToolWindowPane base class should be overridden:
In the example above, the ‘MyUserControl’ object is a regular composite component of the System.Windows.Forms.UserControl type and it can host any other user component inside itself. UserControl can also host WPF components by using WPF ElementHost object.
Starting from Visual Studio 2010, WPF components can be hosted by tool windows natively. To do this, a reference to the WPF component should be passed to the ‘Content’ property of a base class:
Please note that using the two methods described above simultaneously is not possible. When a reference to WPF component is assigned to the base.Content property, an overridden Window property is ignored.
The main PVS-Studio ‘Output’ window of our extension plug-in hosts a virtual grid based on SourceGrid open-source project. This window provides an interface for handling the results of static analysis. The grid itself is bound to a regular ADO.NET table of the System.Data.Datatable type, which is utilized for storing analysis results. Until 4.00 version of PVS-Studio extension, it utilized a regular IDE ‘Error List’ window, but as the analyzer evolved, the capabilities of this default window became insufficient. Apart from being un-extendable with such specific static analysis UI elements as, for example, false positive suppression and filtering mechanisms, the Error List is itself basically a ‘real’ grid, as it stores all of the displayed elements inside itself. Therefore, this grid only permits an adequate handling of 1-2k messages at a time, performance wise, as a greater number of messages already can cause quite a noticeable lag to the environment’s UI. On the other hand, our own practice of using static analysis on relatively large projects, such as Chromium or LLVM, demonstrated that a total number of diagnostic messages (taking into account all of the marked false alarms and low-lever user diagnostics as well) could easily reach tens of thousands or even more.
Therefore, by implementing a custom output window, based on virtual grid that is connected to a DB table, PVS-Studio is able to display and provide convenient handling for hundreds of thousands of diagnostic messages at once. Also, the ability for a convenient and flexible filtering of the analysis results is quite an important aspect of handling a static analyzer, as the manual examination even of only such a «tiny» amount of messages as 1-2k is nearly impossible for a single user. The storage of analysis results in a Datatable object by itself provides quite a convenient filtering mechanism based on a simple SQL queries, even more so because the results of such queries become visible immediately inside the bound virtual grid.
Handling tool windows events
A client area of a tool window (represented by our ToolWindowPane subclass) can process the regular events of user-interface interactions. The IVsWindowFrameNotify3 interface can be used for subscribing to window events. Let’s provide an example of implementing this interface:
As evident by this sample code above, the WindowsStatus class implementing the interface is able to process such window state changes, as the alterations in window’s size, position, visibility properties and so on. Now, let’s subscribe our window for handling these events. It requires the OnToolWindowCreated method to be overridden in our ToolWindowPane subclass:
Controlling window state
A window state can be controlled through event handlers of our IVsWindowFrameNotify3 implementation.
The ‘OnShow’ method notifies the extension package about changes in tool window’s visibility state, allowing to track the appearance of the window to a user, when, for example, user switches windows by clicking on window tabs. Current visibility state could be obtained by the fShow parameter, which corresponds to the __FRAMESHOW list.
The ‘OnClose’ method notifies about the closure of a window frame, allowing to define IDE behavior in case ofthis event with the pgrfSaveOptions parameter, which controls the default document saving dialog (__FRAMECLOSE).
The OnDockableChange method informs the package on window’s docking status changes. The fDockable parameter indicates whether a window is docked to another one; other parameters control window’s size and position before and after the docking event.
The parameters of ‘OnMove’ and ‘OnSize’ methods provide window’s coordinates and size while it is being dragged of resized.
References
- MSDN. Kinds of Windows.
- MSDN. Tool Windows.
- MSDN. Tool Window Essentials.
- MSDN. Tool Window Walkthroughs.
- MSDN. Arranging and Using Windows in Visual Studio.
- MZ-Tools. HOWTO: Understanding toolwindow states in Visual Studio.
Other articles in this series
Use PVS-Studio to search for bugs in C, C++, C# and Java
We offer you to check your project code with PVS-Studio. Just one bug found in the project will show you the benefits of the static code analysis methodology better than a dozen of the articles.