When you create a simple tool window with the VSPackage wizard, it creates a class derived from ToolWindowPane for you that delegates the user interface responsibility to a user control. The following code is one example:
[Guid("4469031d-23e0-483c-8566-ce978f6c9a6f")]
public class MyToolWindow : ToolWindowPane
{
private MyControl control;
public MyToolWindow() : base(null)
{
this.Caption = Resources.ToolWindowTitle;
this.BitmapResourceID = 301;
this.BitmapIndex = 1;
control = new MyControl();
}
override public IWin32Window Window
{
get
{
return (IWin32Window)control;
}
}
}
This model for tool window declaration is quite simple. Any time you use the pattern VSPackage wizard generates for you, you have to create the basic chunk of tool window code like the one above. If you do not want to repeat the pattern you can extract the stereotype behavior into a separate class. In this blog I will show you how.
Modified tool window declaration pattern
The pattern above can be written much shorter, one way is like this:
[Guid("d3533dc0-583a-43e4-be6c-0107b5616b0b")]
[InitialCaption("$ToolWindowTitle")]
[BitmapResourceId(301)]
public class MyToolWindow : VsxToolWindowPane<MyPackage, MyControl>
{
// --- The class body is really empty...
}
If all your tool window logic is embedded into the MyControl user control (in case of simple tool windows it can be so), that’s all you have to put into the code to fully generate the tool window. Of course you have to define MyControl.
From the code above you can see that the magic is within the VsxToolWindowPane generic class and in the attributes decorating MyToolWindow. In the remaining part of this post I show you how the “magic” is done.
The VsxToolWindowPane class
Inside its body VsxToolWindowPane uses the same pattern as I showed you in the introduction part of this blog. The code of the class is quite simple:
public abstract class VsxToolWindowPane<TPackage, TUIControl> : ToolWindowPane
where TPackage: Package
where TUIControl: UserControl, new()
{
private readonly TUIControl _UIControl;
protected VsxToolWindowPane() : base(null)
{
// --- Obtain attributes of the class
foreach (object attr in GetType().GetCustomAttributes(false))
{
InitialCaptionAttribute captionAttr = attr as InitialCaptionAttribute;
if (captionAttr != null)
{
Caption = StringResolver<TPackage>.Resolve(captionAttr.Value);
continue;
}
BitmapResourceIdAttribute resIdAttr = attr as BitmapResourceIdAttribute;
if (resIdAttr != null)
{
BitmapResourceID = resIdAttr.ResourceId;
BitmapIndex = resIdAttr.BitmapIndex;
continue;
}
}
// --- Create the user control
_UIControl = new TUIControl();
}
public TUIControl UIControl
{
get { return _UIControl; }
}
public sealed override IWin32Window Window
{
get { return _UIControl; }
}
}
VsxToolWindowPane uses two type arguments. TPackage represents the package owning the tool window; TUIControl is a placeholder for the user control implementing the user interface of the tool window.
The default constructor scans through the attributes decorating the VsxToolWindowPane derived class and recognizes the InitialCaption and BitmapResourceId attributes. These attributes are used to set up the tool window’s appearance. The constructor also creates an instance of the user control representing the tool window’s UI; this instance can be accessed through the UIControl property. The overridden Window property tells to the pane which Win32 window handle represents the UI.
One subtle detail in the code above is the usage of the StringResolver type. This type resolves resource strings in the VSPackage.resx or Resources.resx files. To look for details, read the LVN! Sidebar #2.
Decorator attributes
The definition of tool window attributes is quite straightforward:
[AttributeUsage(AttributeTargets.Class)]
public sealed class InitialCaptionAttribute: StringAttribute
{
public InitialCaptionAttribute(string value) : base(value)
{
}
}
[AttributeUsage(AttributeTargets.Class)]
public sealed class BitmapResourceIdAttribute: Attribute
{
private readonly int _ResourceId;
private readonly int _BitmapIndex;
public BitmapResourceIdAttribute(int resourceId):
this (resourceId, 1)
{
}
public BitmapResourceIdAttribute(int resourceId, int bitmapIndex)
{
_ResourceId = resourceId;
_BitmapIndex = bitmapIndex;
}
public int ResourceId
{
get { return _ResourceId; }
}
public int BitmapIndex
{
get { return _BitmapIndex; }
}
}
Sample tool window definition
To demonstrate tool window definition I will you a short sample. Let’s assume that our tool window embeds a WebBrowser control and when showing up automatically navigates to a concrete page. To create the tool window, first define a user control with the name of LearnVSXNowToolControl and dock a WebBrowser control to fill the whole user control. Name it Browser. Add a public Navigate method to the user control. After adding it, its code should look like this below:
public partial class LearnVSXNowToolControl : UserControl
{
public LearnVSXNowToolControl()
{
InitializeComponent();
}
public void Navigate(string url)
{
Browser.Navigate(url);
}
}
Now we can create the tool window with the pattern a demonstrated above. Name it to LearnVSXNowToolWindow, and add the following code to it:
[Guid("d3533dc0-583a-43e4-be6c-0107b5616b0b")]
[InitialCaption("$ToolWindowTitle")]
[BitmapResourceId(301)]
public class LearnVSXNowToolWindow :
VsxToolWindowPane<HowToPackage, LearnVSXNowToolControl>
{
public LearnVSXNowToolWindow()
{
UIControl.Navigate("http://www.codeplex.com/LearnVSXNow");
}
}
Our tool window uses the firs bitmap in the bitmap strip with resource ID 301. The initial caption of the tool window is taken from the ToolWindowTitle property of the Resources.resx file. To add functionality to the window, we create a constructor that navigates to the LearnVSXNow! home page.
Using the tool window
Even if you declared the tool window with the VsxToolWindowPane<,> class, do not forget that the tool window should be registered with the ProvideToolWindow attribute decorating your package and you have to write code for showing up the tool window. For details about tool windows, see LearnVSXNow! - Part #4.
Posted
Mar 26 2008, 08:13 AM
by
inovak