SlideShare une entreprise Scribd logo
1  sur  10
Télécharger pour lire hors ligne
Visual	Studio	tool	windows
Author: Paul Eremeev
Date: 24.10.2012
Abstract
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.
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:
[ProvideToolWindow(typeof(MyWindowPane), Orientation =
ToolWindowOrientation.Right, Style = VsDockStyle.Tabbed, Window =
Microsoft.VisualStudio.Shell.Interop.ToolWindowGuids.Outputwindow,
MultiInstances = false, Transient = true, Width = 500, Height = 250,
PositionX = 300, PositionY = 300)]
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:
[ProvideToolWindowVisibility(typeof(MyWindowPane),
/*UICONTEXT_SolutionExists*/"f1536ef8-92ec-443c-9ed7-fdadf150da82")]
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:
private void ShowMyWindow(object sender, EventArgs e)
{
ToolWindowPane MyWindow = this.FindToolWindow(typeof(MyToolWindow),
0, true);
if ((null == MyWindow) || (null == MyWindow.Frame))
{
throw new NotSupportedException(Resources.CanNotCreateWindow);
}
IVsWindowFrame windowFrame = (IVsWindowFrame) MyWindow.Frame;
ErrorHandler.ThrowOnFailure(windowFrame.Show());
}
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:
private void CreateMyWindow(object sender, EventArgs e)
{
for (int i = 0; ; i++)
{
// Find existing windows.
var currentWindow =
this.FindToolWindow(typeof(MyToolWindow), i, false);
if (currentWindow == null)
{
// Create the window with the first free ID.
var window =
(ToolWindowPane)this.CreateToolWindow(typeof(MyToolWindow), i);
if ((null == window) || (null == window.Frame))
{
throw new
NotSupportedException(Resources.CanNotCreateWindow);
}
IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;
ErrorHandler.ThrowOnFailure(windowFrame.Show());
break;
}
}
}
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:
Guid gd = Guid.Empty;
windowFrame.SetFramePos(VSSETFRAMEPOS.SFP_fDockBottom, ref gd, 20, 20,
200, 200);
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:
public void OnConnection(object application,
ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
EnvDTE80.Windows2 window;
AddIn add_in;
object ctlobj = null;
Window myWindow;
// Get the window object
add_in = _applicationObject.AddIns.Item(1);
window = (Windows2)_applicationObject.Windows;
// This section specifies the path and class name of the windows
// control that you want to host in the new tool window, as well as
// its caption and a unique GUID.
string assemblypath = "C:MyToolwindowMyToolWindowControl.dll";
string classname = " MyToolWindowControl.MyUserControl";
string guidpos = "{E87F0FC8-5330-442C-AF56-4F42B5F1AD11}";
string caption = "My Window";
// Creates the new tool window and inserts the user control into it.
myWindow = window.CreateToolWindow2(add_in, assemblypath,
classname, caption, guidpos, ref ctlobj);
myWindow.Visible = true;
}
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.
[Guid("870ab1d8-b434-4e86-a479-e49b3c6797f0")]
public class MyToolWindow : ToolWindowPane
{
public MyToolWindow():base(null)
{
this.Caption = Resources.ToolWindowTitle;
this.BitmapResourceID = 301;
this.BitmapIndex = 1;
...
}
}
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:
public MyUserControl control;
public MyToolWindow():base(null)
{
this.Caption = Resources.ToolWindowTitle;
this.BitmapResourceID = 301;
this.BitmapIndex = 1;
this.control = new MyUserControl();
}
public override IWin32Window Window
{
get { return (IWin32Window)control; }
}
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:
public MyToolWindow():base(null)
{
this.Caption = Resources.ToolWindowTitle;
this.BitmapResourceID = 301;
this.BitmapIndex = 1;
base.Content = new MyWPFUserControl();
}
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:
public sealed class WindowStatus: IVsWindowFrameNotify3
{
// Private fields to keep track of the last known state
private int x = 0;
private int y = 0;
private int width = 0;
private int height = 0;
private bool dockable = false;
#region Public properties
// Return the current horizontal position of the window
public int X
{
get { return x; }
}
// Return the current vertical position of the window
public int Y
{
get { return y; }
}
// Return the current width of the window
public int Width
{
get { return width; }
}
// Return the current height of the window
public int Height
{
get { return height; }
}
// Is the window dockable
public bool IsDockable
{
get { return dockable; }
}
#endregion
public WindowStatus()
{}
#region IVsWindowFrameNotify3 Members
// This is called when the window is being closed
public int OnClose(ref uint pgrfSaveOptions)
{
return Microsoft.VisualStudio.VSConstants.S_OK;
}
// This is called when a window "dock state" changes.
public int OnDockableChange(int fDockable, int x, int y, int w,
int h)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
this.dockable = (fDockable != 0);
return Microsoft.VisualStudio.VSConstants.S_OK;
}
// This is called when the window is moved
public int OnMove(int x, int y, int w, int h)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
return Microsoft.VisualStudio.VSConstants.S_OK;
}
// This is called when the window is shown or hidden
public int OnShow(int fShow)
{
return Microsoft.VisualStudio.VSConstants.S_OK;
}
/// This is called when the window is resized
public int OnSize(int x, int y, int w, int h)
{
this.x = x;
this.y = y;
this.width = w;
this.height = h;
return Microsoft.VisualStudio.VSConstants.S_OK;
}
#endregion
}
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:
public class MyToolWindow: ToolWindowPane
{
public override void OnToolWindowCreated()
{
base.OnToolWindowCreated();
// Register to the window events
WindowStatus windowFrameEventsHandler = new WindowStatus();
ErrorHandler.ThrowOnFailure(
((IVsWindowFrame)this.Frame).SetProperty(
(int)__VSFPROPID.VSFPROPID_ViewHelper,
(IVsWindowFrameNotify3)windowFrameEventsHandler));
}
...
}
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
1. MSDN. Kinds of Windows.
2. MSDN. Tool Windows.
3. MSDN. Tool Window Essentials.
4. MSDN. Tool Window Walkthroughs.
5. MSDN. Arranging and Using Windows in Visual Studio.
6. MZ-Tools. HOWTO: Understanding toolwindow states in Visual Studio.
Other articles in this series
0. Introduction.
1. Creating, debugging and deploying extension packages for Microsoft Visual Studio
2005/2008/2010/2012.
2. Visual Studio Automation Object Model. EnvDTE interfaces.
3. Visual Studio commands.
4. Visual Studio tool windows.
5. Integrating into Visual Studio settings.
6. Visual C++ project model.

Contenu connexe

Tendances

Tendances (12)

SwtBot: Unit Testing Made Easy
SwtBot: Unit Testing Made EasySwtBot: Unit Testing Made Easy
SwtBot: Unit Testing Made Easy
 
Java swing
Java swingJava swing
Java swing
 
Emergency androidstudiochapter
Emergency androidstudiochapterEmergency androidstudiochapter
Emergency androidstudiochapter
 
Awt and swing in java
Awt and swing in javaAwt and swing in java
Awt and swing in java
 
java2 swing
java2 swingjava2 swing
java2 swing
 
04b swing tutorial
04b swing tutorial04b swing tutorial
04b swing tutorial
 
A test framework out of the box - Geb for Web and mobile
A test framework out of the box - Geb for Web and mobileA test framework out of the box - Geb for Web and mobile
A test framework out of the box - Geb for Web and mobile
 
Dojo1.0_Tutorials
Dojo1.0_TutorialsDojo1.0_Tutorials
Dojo1.0_Tutorials
 
AWT Packages , Containers and Components
AWT Packages , Containers and ComponentsAWT Packages , Containers and Components
AWT Packages , Containers and Components
 
Java Swing
Java SwingJava Swing
Java Swing
 
iOS Automation: XCUITest + Gherkin
iOS Automation: XCUITest + GherkiniOS Automation: XCUITest + Gherkin
iOS Automation: XCUITest + Gherkin
 
Mobile 2.0 Open Ideas WorkShop: Building Social Media Enabled Apps on Android
Mobile 2.0 Open Ideas WorkShop: Building Social Media Enabled Apps on AndroidMobile 2.0 Open Ideas WorkShop: Building Social Media Enabled Apps on Android
Mobile 2.0 Open Ideas WorkShop: Building Social Media Enabled Apps on Android
 

En vedette

En vedette (20)

Wade Not in Unknown Waters. Part Four.
Wade Not in Unknown Waters. Part Four.Wade Not in Unknown Waters. Part Four.
Wade Not in Unknown Waters. Part Four.
 
How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.How to make fewer errors at the stage of code writing. Part N1.
How to make fewer errors at the stage of code writing. Part N1.
 
Intel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correctionIntel IPP Samples for Windows - error correction
Intel IPP Samples for Windows - error correction
 
Software diseases: memset
Software diseases: memsetSoftware diseases: memset
Software diseases: memset
 
What comments hide
What comments hideWhat comments hide
What comments hide
 
Re-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large reportRe-checking the ReactOS project - a large report
Re-checking the ReactOS project - a large report
 
Comparing static analysis in Visual Studio 2012 (Visual C++ 2012) and PVS-Studio
Comparing static analysis in Visual Studio 2012 (Visual C++ 2012) and PVS-StudioComparing static analysis in Visual Studio 2012 (Visual C++ 2012) and PVS-Studio
Comparing static analysis in Visual Studio 2012 (Visual C++ 2012) and PVS-Studio
 
Cppcheck
CppcheckCppcheck
Cppcheck
 
What do static analysis and search engines have in common? A good "top"!
What do static analysis and search engines have in common? A good "top"!What do static analysis and search engines have in common? A good "top"!
What do static analysis and search engines have in common? A good "top"!
 
Creating, debugging and deploying extension packages for Microsoft Visual Stu...
Creating, debugging and deploying extension packages for Microsoft Visual Stu...Creating, debugging and deploying extension packages for Microsoft Visual Stu...
Creating, debugging and deploying extension packages for Microsoft Visual Stu...
 
Static analysis should be used regularly
Static analysis should be used regularlyStatic analysis should be used regularly
Static analysis should be used regularly
 
Errors detected in C++Builder
Errors detected in C++BuilderErrors detected in C++Builder
Errors detected in C++Builder
 
PVS-Studio advertisement - static analysis of C/C++ code
PVS-Studio advertisement - static analysis of C/C++ codePVS-Studio advertisement - static analysis of C/C++ code
PVS-Studio advertisement - static analysis of C/C++ code
 
The D language comes to help
The D language comes to helpThe D language comes to help
The D language comes to help
 
Visual C++ project model
Visual C++ project modelVisual C++ project model
Visual C++ project model
 
100% code coverage by static analysis - is it that good?
100% code coverage by static analysis - is it that good?100% code coverage by static analysis - is it that good?
100% code coverage by static analysis - is it that good?
 
Difficulties of comparing code analyzers, or don't forget about usability
Difficulties of comparing code analyzers, or don't forget about usabilityDifficulties of comparing code analyzers, or don't forget about usability
Difficulties of comparing code analyzers, or don't forget about usability
 
Wade not in unknown waters. Part one.
Wade not in unknown waters. Part one.Wade not in unknown waters. Part one.
Wade not in unknown waters. Part one.
 
Farewell to #define private public
Farewell to #define private publicFarewell to #define private public
Farewell to #define private public
 
Lesson 7. The issues of detecting 64-bit errors
Lesson 7. The issues of detecting 64-bit errorsLesson 7. The issues of detecting 64-bit errors
Lesson 7. The issues of detecting 64-bit errors
 

Similaire à Visual Studio tool windows

Creating a windowed program
Creating a windowed programCreating a windowed program
Creating a windowed program
myrajendra
 
Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development
Mahmoud Hamed Mahmoud
 
BOF-5110 Extending the Groovy SwingBuilder
BOF-5110 Extending the Groovy SwingBuilderBOF-5110 Extending the Groovy SwingBuilder
BOF-5110 Extending the Groovy SwingBuilder
Danno Ferrin
 
Guice tutorial
Guice tutorialGuice tutorial
Guice tutorial
Anh Quân
 
MEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr WlodekMEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr Wlodek
infusiondev
 

Similaire à Visual Studio tool windows (20)

The Ring programming language version 1.5.4 book - Part 71 of 185
The Ring programming language version 1.5.4 book - Part 71 of 185The Ring programming language version 1.5.4 book - Part 71 of 185
The Ring programming language version 1.5.4 book - Part 71 of 185
 
Creating a windowed program
Creating a windowed programCreating a windowed program
Creating a windowed program
 
Integrating into Visual Studio settings
Integrating into Visual Studio settingsIntegrating into Visual Studio settings
Integrating into Visual Studio settings
 
Visual Studio Automation Object Model. EnvDTE interfaces
Visual Studio Automation Object Model. EnvDTE interfacesVisual Studio Automation Object Model. EnvDTE interfaces
Visual Studio Automation Object Model. EnvDTE interfaces
 
UIAutomator
UIAutomatorUIAutomator
UIAutomator
 
Lightning Talk - Xamarin
Lightning Talk - Xamarin Lightning Talk - Xamarin
Lightning Talk - Xamarin
 
JAVA AWT
JAVA AWTJAVA AWT
JAVA AWT
 
The Ring programming language version 1.9 book - Part 82 of 210
The Ring programming language version 1.9 book - Part 82 of 210The Ring programming language version 1.9 book - Part 82 of 210
The Ring programming language version 1.9 book - Part 82 of 210
 
The Ring programming language version 1.5.3 book - Part 81 of 184
The Ring programming language version 1.5.3 book - Part 81 of 184The Ring programming language version 1.5.3 book - Part 81 of 184
The Ring programming language version 1.5.3 book - Part 81 of 184
 
Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development Windows Store app using XAML and C#: Enterprise Product Development
Windows Store app using XAML and C#: Enterprise Product Development
 
Bridge Pattern
Bridge PatternBridge Pattern
Bridge Pattern
 
BOF-5110 Extending the Groovy SwingBuilder
BOF-5110 Extending the Groovy SwingBuilderBOF-5110 Extending the Groovy SwingBuilder
BOF-5110 Extending the Groovy SwingBuilder
 
JavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
JavaCro'14 - Building interactive web applications with Vaadin – Peter LehtoJavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
JavaCro'14 - Building interactive web applications with Vaadin – Peter Lehto
 
JEDI Slides-Intro2-Chapter20-GUI Event Handling.pdf
JEDI Slides-Intro2-Chapter20-GUI Event Handling.pdfJEDI Slides-Intro2-Chapter20-GUI Event Handling.pdf
JEDI Slides-Intro2-Chapter20-GUI Event Handling.pdf
 
02 hello world - Android
02   hello world - Android02   hello world - Android
02 hello world - Android
 
Tang doanh thu quang cao di dong
Tang doanh thu quang cao di dongTang doanh thu quang cao di dong
Tang doanh thu quang cao di dong
 
Google GIN
Google GINGoogle GIN
Google GIN
 
Guice tutorial
Guice tutorialGuice tutorial
Guice tutorial
 
MEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr WlodekMEF Deep Dive by Piotr Wlodek
MEF Deep Dive by Piotr Wlodek
 
[Ultracode Munich #4] Short introduction to the new Android build system incl...
[Ultracode Munich #4] Short introduction to the new Android build system incl...[Ultracode Munich #4] Short introduction to the new Android build system incl...
[Ultracode Munich #4] Short introduction to the new Android build system incl...
 

Dernier

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Dernier (20)

A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 

Visual Studio tool windows

  • 1. Visual Studio tool windows Author: Paul Eremeev Date: 24.10.2012 Abstract 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. 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
  • 2. 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: [ProvideToolWindow(typeof(MyWindowPane), Orientation = ToolWindowOrientation.Right, Style = VsDockStyle.Tabbed, Window = Microsoft.VisualStudio.Shell.Interop.ToolWindowGuids.Outputwindow, MultiInstances = false, Transient = true, Width = 500, Height = 250, PositionX = 300, PositionY = 300)] 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: [ProvideToolWindowVisibility(typeof(MyWindowPane), /*UICONTEXT_SolutionExists*/"f1536ef8-92ec-443c-9ed7-fdadf150da82")] 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: private void ShowMyWindow(object sender, EventArgs e) { ToolWindowPane MyWindow = this.FindToolWindow(typeof(MyToolWindow),
  • 3. 0, true); if ((null == MyWindow) || (null == MyWindow.Frame)) { throw new NotSupportedException(Resources.CanNotCreateWindow); } IVsWindowFrame windowFrame = (IVsWindowFrame) MyWindow.Frame; ErrorHandler.ThrowOnFailure(windowFrame.Show()); } 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: private void CreateMyWindow(object sender, EventArgs e) { for (int i = 0; ; i++) { // Find existing windows. var currentWindow = this.FindToolWindow(typeof(MyToolWindow), i, false); if (currentWindow == null) { // Create the window with the first free ID. var window = (ToolWindowPane)this.CreateToolWindow(typeof(MyToolWindow), i); if ((null == window) || (null == window.Frame)) { throw new NotSupportedException(Resources.CanNotCreateWindow); } IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; ErrorHandler.ThrowOnFailure(windowFrame.Show()); break; } } } 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: Guid gd = Guid.Empty;
  • 4. windowFrame.SetFramePos(VSSETFRAMEPOS.SFP_fDockBottom, ref gd, 20, 20, 200, 200); 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: public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom) { _applicationObject = (DTE2)application; _addInInstance = (AddIn)addInInst; EnvDTE80.Windows2 window; AddIn add_in; object ctlobj = null; Window myWindow; // Get the window object add_in = _applicationObject.AddIns.Item(1); window = (Windows2)_applicationObject.Windows; // This section specifies the path and class name of the windows // control that you want to host in the new tool window, as well as // its caption and a unique GUID. string assemblypath = "C:MyToolwindowMyToolWindowControl.dll"; string classname = " MyToolWindowControl.MyUserControl"; string guidpos = "{E87F0FC8-5330-442C-AF56-4F42B5F1AD11}"; string caption = "My Window"; // Creates the new tool window and inserts the user control into it. myWindow = window.CreateToolWindow2(add_in, assemblypath, classname, caption, guidpos, ref ctlobj); myWindow.Visible = true; } 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.
  • 5. 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. [Guid("870ab1d8-b434-4e86-a479-e49b3c6797f0")] public class MyToolWindow : ToolWindowPane { public MyToolWindow():base(null) { this.Caption = Resources.ToolWindowTitle; this.BitmapResourceID = 301; this.BitmapIndex = 1; ... } } 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: public MyUserControl control; public MyToolWindow():base(null) { this.Caption = Resources.ToolWindowTitle; this.BitmapResourceID = 301; this.BitmapIndex = 1; this.control = new MyUserControl(); } public override IWin32Window Window { get { return (IWin32Window)control; } } 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.
  • 6. 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: public MyToolWindow():base(null) { this.Caption = Resources.ToolWindowTitle; this.BitmapResourceID = 301; this.BitmapIndex = 1; base.Content = new MyWPFUserControl(); } 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: public sealed class WindowStatus: IVsWindowFrameNotify3 { // Private fields to keep track of the last known state private int x = 0; private int y = 0; private int width = 0;
  • 7. private int height = 0; private bool dockable = false; #region Public properties // Return the current horizontal position of the window public int X { get { return x; } } // Return the current vertical position of the window public int Y { get { return y; } } // Return the current width of the window public int Width { get { return width; } } // Return the current height of the window public int Height { get { return height; } } // Is the window dockable public bool IsDockable { get { return dockable; } } #endregion public WindowStatus() {} #region IVsWindowFrameNotify3 Members // This is called when the window is being closed public int OnClose(ref uint pgrfSaveOptions) { return Microsoft.VisualStudio.VSConstants.S_OK; } // This is called when a window "dock state" changes. public int OnDockableChange(int fDockable, int x, int y, int w, int h)
  • 8. { this.x = x; this.y = y; this.width = w; this.height = h; this.dockable = (fDockable != 0); return Microsoft.VisualStudio.VSConstants.S_OK; } // This is called when the window is moved public int OnMove(int x, int y, int w, int h) { this.x = x; this.y = y; this.width = w; this.height = h; return Microsoft.VisualStudio.VSConstants.S_OK; } // This is called when the window is shown or hidden public int OnShow(int fShow) { return Microsoft.VisualStudio.VSConstants.S_OK; } /// This is called when the window is resized public int OnSize(int x, int y, int w, int h) { this.x = x; this.y = y; this.width = w; this.height = h; return Microsoft.VisualStudio.VSConstants.S_OK; } #endregion } 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: public class MyToolWindow: ToolWindowPane { public override void OnToolWindowCreated() { base.OnToolWindowCreated();
  • 9. // Register to the window events WindowStatus windowFrameEventsHandler = new WindowStatus(); ErrorHandler.ThrowOnFailure( ((IVsWindowFrame)this.Frame).SetProperty( (int)__VSFPROPID.VSFPROPID_ViewHelper, (IVsWindowFrameNotify3)windowFrameEventsHandler)); } ... } 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 1. MSDN. Kinds of Windows. 2. MSDN. Tool Windows. 3. MSDN. Tool Window Essentials. 4. MSDN. Tool Window Walkthroughs. 5. MSDN. Arranging and Using Windows in Visual Studio. 6. MZ-Tools. HOWTO: Understanding toolwindow states in Visual Studio. Other articles in this series 0. Introduction. 1. Creating, debugging and deploying extension packages for Microsoft Visual Studio 2005/2008/2010/2012. 2. Visual Studio Automation Object Model. EnvDTE interfaces. 3. Visual Studio commands.
  • 10. 4. Visual Studio tool windows. 5. Integrating into Visual Studio settings. 6. Visual C++ project model.