Ver versión en español    
 
Home
10 Reasons to use MZ-Tools
MZ-Tools 6.0 for VS.NET
Editions
Features
Online Documentation
MZ-Tools SDK
Download
Purchase
Version History (RSS)  
FAQ & Support
MZ-Tools 3.0 for VB6 & VBA
Features
Online Documentation
Download (freeware)
Donations (Paypal)
Version History (RSS)  
FAQ & Support
User Reviews
Community Place
Contact  
For Add-In Developers
About
   
 
User Testimonials

I'm an avid supporter of MZ-Tools. It's a product I couldn't do without and your level of support is outstanding.

Jan Hyde (Visual Basic MVP)

You will soon wonder how you ever lived without it.

Andy Maggs

More user reviews
 
HOWTO: Create a dockable toolwindow from a Visual Studio .NET add-in

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

MZ-Tools 6.0 for Visual Studio .NET

You can code, design, locate code and document your apps much faster using VB.NET, C#, C++ or Visual J#:

Buy MZ-Tools Now Download MZ-Tools Demo

   Top