| Author: |
Carlos J. Quintero (Microsoft MVP) |
Applies to: |
Microsoft Visual Studio .NET 2002 |
| Created: |
January 2006 |
|
Microsoft Visual Studio .NET 2003 |
| Updated: |
February 2008 |
|
Microsoft Visual Studio 2005 |
| |
|
|
Microsoft Visual Studio 2008 |
Introduction
A Visual Studio .NET add-in can show two kind of windows:
- Modal windows: these windows are regular Windows forms that are shown by
calling the ShowDialog method.
- Modeless windows: although you could use regular Windows forms for this
purpose by calling the Show method, these windows would not be dockable and
they have some problems with the keyboard focus. The proper way of creating a
modeless dockable window is using the EnvDTE.Windows.CreateToolWindow()
function. This article explains how to do this.
More Information
A toolwindow created with the CreateToolWindow function has the EnvDTE.Window
type, as any other built-in toolwindow of the IDE. The toolwindow works hosting
a usercontrol, provided by the add-in, with the actual content of the window.
The way of providing the usercontrol from the add-in varies (as explained
later), and it is the cause of a lot of problems that arise from the fact that
the Visual Studio is an ActiveX/COM application, and it expects ActiveX
usercontrols, while the add-in can be a managed DLL, and it expects to supply
.NET usercontrols.
Before dealing with the problem of passing the usercontrol to the
CreateToolWindow function, lets take a look at this function:
Function CreateToolWindow(ByVal AddInInst As
EnvDTE.AddIn, _
ByVal ProgID As String, ByVal Caption As String, _
ByVal GuidPosition As String, ByRef DocObj As Object) As
EnvDTE.Window
The function returns the EnvDTE.Window created, and it receives the following
parameters:
- AddInInst (EnvDTE.AddIn type): the instance of the add-in. This instance
was passed on the OnConnection method.
- ProgID (String type): the ProgID of the usercontrol that will be hosted
by the toolwindow (more on this later).
- Caption (String type): the caption of the toolwindow.
- GuidPosition (String type): a unique GUID that identifies the toolwindow.
Visual Studio uses this identifier to persist the size and position of the
toolwindow. So, each toolwindow should have a different GUID.
- DocObj (Object type): after calling the function, it returns the
instance of the usercontrol that has been created. This instance can be also
retrieved at any time with the Window.Object property. In both cases the
usercontrol is returned as Object type, so you must cast it to the actual
type of your usercontrol.
Officially, the CreateToolWindow function expects to receive the ProgID of an
ActiveX control and internally it creates it from that ProgID using the COM
mechanisms for that purpose. So, if your add-in is an ActiveX C++ add-in, there
is no problem. However, if your add-in is a .NET add-in (VB.NET, C#), it can
only provide a .NET usercontrol, not an ActiveX control. To solve this problem,
Microsoft provides a sample with source code of a so called shim control
that is an ActiveX C++ ATL-based control that can host a .NET usercontrol inside
it. Since this was an afterthought it poses a series of problems:
- The shim control is not installed with Visual Studio or with the .NET
Framework. Your add-in must install its own copy.
- To avoid conflicts with other shim controls, you should not use the
Microsoft shim control directly: you need to change the GUID and ProgID of
your shim control. This requires a recompilation using C++, so you need a
Visual Studio IDE with that language installed.
- It is not supported by Microsoft (since it is sample code).
- It has problems with the TabIndex of the controls of the .NET
usercontrol hosted inside it and other keyboard-related problems.
Suffice to say that Microsoft provided several versions of its shim control
trying to fix the problems:
Due to the problems of the Microsoft shim controls, there is another
implementation made by the company
Xtreme Simplicity,
and it is available in the same section of the Yahoo forum, the "XSShimGen.zip"
file. This is the best implementation of a shim control and it comes with a
wizard that generates a new shim control with a fresh ProgID and GUID, and even
a sample add-in project to test it.
So, the idea is that the toolwindow hosts the ActiveX shim control, and in
turn the shim control hosts your .NET usercontrol:
- To host the ActiveX shim control in the toolwindow, you pass its COM
ProgID to the CreateToolWindow function.
- To host the .NET usercontrol in the ActiveX shim control, you need to
call to a function (typically named HostUserControl) provided by the shim
control. Again, the way of passing the .NET usercontrol to this function
varies among different available implementations of shim controls. Some of
them require that your .NET usercontrol must be registered for COM Interop,
and the HostUserControl function creates it. Others require an already
created instance of the .NET usercontrol (a more clean approach).
Also, notice that you need to create a toolwindow only once (the first time
that it is required) in the lifetime of your add-in. Since toolwindows are never
destroyed (when you close them you are really making them invisible), the next
time that a toolwindow is required to show you simply make it visible by calling
Window.Visible = True. This implies that you need to keep the instances of the
created toolwindows at class level, not a procedure level.
The samples provided above use all these concepts. If you are thinking that
all this stuff about shim controls is a mess, you are right, they are incredibly
painful. So, several successful attempts have been made to avoid the need for a
shim control, but they vary for each Visual Studio version:
- Using Visual Studio .NET 2002, it happens that (unofficially) the
CreateToolWindow actually admits a .NET usercontrol instead of an ActiveX
control.
- Using Visual Studio .NET 2003, the previous trick does not work: the
first time that you call the CreateToolWindow function, it throws an
exception, although the second time it succeeds. But some people have been
smart enough to provide a workaround. The explanation is in this post of the
Yahoo forum:
http://groups.yahoo.com/group/vsnetaddin/message/1446
- Using Visual Studio 2005 and 2008, Microsoft finally did it right and a new
EnvDTE80.Windows2.CreateToolWindow2 function is provided in the EnvDTE80.dll
assembly (that can be used in Visual Studio 2008 too), which does not
require an ActiveX shim control. Rather, it requires the assembly and the
class of the .NET usercontrol. You can read the documentation about this new
function here:
http://msdn2.microsoft.com/library/envdte80.windows2.createtoolwindow2.aspx.
Note: according to some reports, if you use the CreateToolWindow2 function
and the usercontrol is not in the assembly of the add-in (but in a separate
assembly), the last parameter (DocObj) is returned null although the
usercontrol is hosted correctly.
Go back to the 'Resources for Visual Studio .NET extensibility' section for more articles like this
You can code, design, locate code and document your apps much faster using VB.NET, C#, C++ or Visual J#:
