Creating a WPF Tool Window for Visual Studio 2010
I’ve been working on Visual Studio 2010 extensibility for some time now, and I must say that creating a tool window was not the easiest task in the world.
First Attempt: Create an MEF-friendly IToolWindowService
The original goal of this project was creating an MEF service that allowed exporting classes implementing IToolWindowProvider, and have the service manage the creation of the tool windows and their entries on the View > Other Windows menu. This worked, but had several drawbacks that eventually led to the conclusion that this was the wrong approach. First and foremost, this method forced the assemblies providing tool windows to load even when the tool windows weren’t visible. The performance implications of this rule out using MEF as a general solution to this problem. That said, here are some other “little things” that I didn’t have worked out in the MEF solution:
- Tool windows would not save their visible state; they were always hidden the next time Visual Studio was opened.
- The menu commands to show the tool windows were not available until a source file of {insert some hard-coded type} was opened. This is because the service loaded itself by exporting IVsTextViewCreationListener. When I tried to use the “any” content type, I found that the service would cause an InvalidOperationException when the environment loaded because the tool windows were created while the environment was using an IEnumerator to traverse the current windows.
Overview of the Improved Process
Here is an outline of the general process of creating a new tool window. Following the outline, I’ll explain in detail what each step requires.
- Create a VSIX project
- Configure the project to offer a VSPackage and a command table
- Create the WPF control your tool window will be displaying
- Include my helper class WpfToolWindowPane in your project
- Derive a class from WpfToolWindowPane for your tool window
- Set up menu commands
Create a VSIX project
I’m basing this off of the VSIX project, because it’s a nice clean project template. It’s also beneficial because users that already have a VSIX project can easily add a tool window to it.
- Create a project using the “VSIX Project” template (Visual C# > Extensibility). Choose a different solution name than project name or you’ll run into issues later.
- Delete the file VSIXProject.cs.
- Right click the file “source.extension.manifest” and select View Code. Update the Name, Author, and Description fields with information about your extension. Save and close the file.
- Right click the project in Solution Explorer and select Unload Project. Then right click it again and select “Edit MyProject.csproj”
- Locate the line that says <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer> and change the value to true. Save and close the file.
- Right click the project in Solution Explorer and select Reload Project. If Visual Studio gives an error about a project with the same name already being open, restart Visual Studio and try again.
Configure the project to offer a VSPackage and a command table
- Add an XML file to the project named “MyProject.vsct”. In the properties pane for it, change the Build Action to VSCTCompile.
- Add a resources file to the project named “VSPackage.resx” (use exactly that name). Open the file in the resources editor, and at the top set the Access Modifier to No code generation. Save and close the file.
- Unload the project again and open it for editing.
- Set GeneratePkgDefFile to true.
- Below the line with GeneratePkgDefFile, add <RegisterWithCodebase>true</RegisterWithCodebase>.
- Set CopyBuildOutputToOutputDirectory to true.
- Locate the line containing <VSCTCompile Include="MyProject.vsct"/> and change it to <VSCTCompile Include="MyProject.vsct"><ResourceName>1000</ResourceName><SubType>Designer</SubType></VSCTCompile> .
- Find the line containing <EmbeddedResource Include="VSPackage.resx">, and add both <MergeWithCTO>true</MergeWithCTO> and <LogicalName>VSPackage.resources</LogicalName> as children.
- Save, close, and reload the project file.
- Add a new class to the project named MyProjectPackage. Derive the class from Microsoft.VisualStudio.Shell.Package. Set the class GUID by adding the attribute [Guid("00000000-0000-0000-0000-000000000000")] (use the Create GUID tool to create the actual value you’ll use here). Add the attribute [PackageRegistration(UseManagedResourcesOnly = true)] to tell Visual Studio to register the class as a VSPackage. Provide the command table (vsct) by adding the attribute [ProvideMenuResource(1000, 1)].
Create the WPF control your tool window will be displaying
For this part, you can create any control derived directly or indirectly from System.Windows.Control.
Include the helper class WpfToolWindowPane in your project
This helper class handles most of the code required for providing a tool window. You can download WpfToolWindowPane.cs here.
Derive a class from WpfToolWindowPane for your tool window
- Add a new class named ToolNameToolWindowPane to your project and derive it from WpfToolWindowPane. Use the Create GUID tool to create a guid for ToolNameToolWindowPane, and add a GuidAttribute to it.
- In the constructor, set the Caption of your tool window with base.Caption = "Tool Window Name"; .
- Override the CreateToolWindowControl() method to return a new instance of the control you created earlier. The method can be as simple as return new ToolNameControl(base.GlobalServiceProvider); .
Set up menu commands
The last step is adding a command to the View > Other Windows menu so your tool window can be opened.
Setting up the command table
Open the MyProject.vsct file and fill it with the following. Use the same Guid for guidMyProjectPackage as you used for the MyProjectPackage class. For the other two placeholders, create new Guids.
< ?xml version="1.0" encoding="utf-8" ?> <commandtable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable"> <extern href="vsshlids.h"/> <commands package="guidMyProjectPackage"> <buttons> <button guid="guidMyProjectPackageCmdSet" id="cmdidShowToolName" priority="0x100" type="Button"> <!--<Icon guid="guidShowToolNameCmdBmp" id="bmpShowToolName"/>--> <strings> <buttontext>Tool Name</buttontext> </strings> </button> </buttons> <!--<Bitmaps> <bitmap guid="guidShowToolNameCmdBmp" href="Resources\ToolIcon.png" usedList="bmpShowToolName"/> --> </commands> <commandplacements> <commandplacement guid="guidMyProjectPackageCmdSet" id="cmdidShowToolName" priority="0x100"> <parent guid="guidSHLMainMenu" id="IDG_VS_WNDO_OTRWNDWS1" /> </commandplacement> </commandplacements> <symbols> <guidsymbol name="guidMyProjectPackage" value="{00000000-0000-0000-0000-000000000000}" /> <guidsymbol name="guidMyProjectPackageCmdSet" value="{00000000-0000-0000-0000-000000000000}"> <idsymbol name="cmdidShowToolName" value="0x2001" /> </guidsymbol> <!--<GuidSymbol name="guidShowToolNameCmdBmp" value="{00000000-0000-0000-0000-000000000000}"> <idsymbol name="bmpShowToolName" value="1" /> --> </symbols> </commandtable> |
Add a Constants class to your project
Add a class named Constants to your project to make the Guids you used in the command table accessible to your code.
internal static class Constants { public const int ToolWindowCommandId = 0x2001; public const string MyProjectPackageCmdSet = "{00000000-0000-0000-0000-000000000000}"; public static readonly Guid GuidMyProjectPackageCmdSet = new Guid(MyProjectPackageCmdSet); } |
Handling the menu command
Open the MyProjectPackage class. Add a new attribute to provide your tool window: [ProvideToolWindow(typeof(ToolNameToolWindowPane))]. Add the following code to the body of the class:
protected override void Initialize() { base.Initialize(); WpfToolWindowPane.ProvideToolWindowCommand<ToolNameToolWindowPane>(this, Constants.GuidMyProjectPackageCmdSet, Constants.ToolWindowCommandId); } |
Many thanks for this great post! I’m having a hard time getting the correct versions of assemblies referenced to make this to compile. Can you please tell me what assemblies you have used? Or even better provide the source code? Thanks.
March 10th, 2010 at 3:49 pmHello
Am using WPF usercontrol
1 .I have doubt how to create a multiple instance window 2. Can we call in the child window in button click?
July 13th, 2015 at 5:55 am