About a week ago I announced that I started to create an alternative implementation of PowerCommands using VSXtra. If you have read my PowerCommand deep dives (Part #19, Part #20, Part #21) you know that I appreciate Pablo’s tool because it delivers a lot of value not only in its functionality but also in its source code.
I decided to create an alternative implementation in order to get ideas where VSXtra could add some more value to VSX development experience. I release the source code of this implementation I call VSXtraCommands, but I do not request a PLK for it as I do not want it to be released as a competing tool, because the idea of PowerCommands is not mine. VSXtraCommands is about to demonstrate that the current implementation of Managed Package Framework has challenges in the area of development experience. If you need the functionality, please go on with using PowerCommands and not VSXtraCommands!
This post is the first part of a mini-series within LearnVSXNow and deals with the command architecture; treats how it is changed compared to the PowerCommands implementation.
In the first part of the post I give a short overview of PowerCommands architecture and then I show how the implementation can be improved using VSXtra features.
A quick overview of PowerCommands architecture
In Part #19 I describe the architecture in details; here I give you a brief summary to start from. The architecture of PowerCommands is quite simple, as you can see in the following figure:

The VSPackage owns a few object types that provide the functionality. Each Command providing the functionality of PowerCommands is independent from the others, they do not interact directly with each other. A few Command objects have an associated UI. For manageability reasons commands are collected into a container named CommandSet. The package contains few Services to manage commands and related functionality. PowerCommands can be customized through options that can be set on Option Pages. A few commands require responding to VS IDE events. These events are watched and handled by Event Listeners.
Command handling
The individual commands within PowerCommands are declared as independent types. PowerCommands provides a simple infrastructure to keep track of available commands. This helps in a few common tasks, like enabling or disabling each command separately, enumerating commands, etc. The architecture of command handling is summarized in the following figure:

The heart of the command handling architecture is the CommandManagerService class that provides the ICommandManagerService to keep track of registered commands. Through this service commands can be registered. The PowerCommandsPackage class uses the CommandSet type to interact with the CommandManagerService. For each command an appropriate instance class is created. When the CommandSet class registers a command instance, it also binds that instance with ID of the commands used by the VS Shell. Through this ID the VS Shell can invoke the command.
The DynamicCommand class
The classes representing the commands’ functionality are derived from OleMenuCommand through the DynamicCommand class that extends OleMenuCommand with common functionality. The blueprint of this class is the following:
public abstract class DynamicCommand : OleMenuCommand
{
// --- Private fields
private static DTE dte;
private static IServiceProvider serviceProvider;
private static PowerCommandsPackage powerCommandsPackage;
// --- Properties
protected static IServiceProvider ServiceProvider { get; }
protected static DTE Dte { get; }
protected static PowerCommandsPackage PowerCommandsPackage { get; }
// --- Methods
public DynamicCommand(IServiceProvider provider, EventHandler onExecute,
CommandID id) : base(onExecute, id);
protected void OnBeforeQueryStatus(object sender, EventArgs e);
protected virtual bool CanExecute(OleMenuCommand command);
}
The base OleMenuCommand class provides events to query the status of a command and execute the command. In the DynamicCommand constructor we pass the command ID and the event handler method for the command execution. DynamicCommand implements the event for the command status query. This query simply calls the CanExecute virtual method that retrieves a bool value indicating if the command is enabled or not.
The class has three static properties to access IServiceProvider, DTE and PowerCommandsPackage instances within the command classes. These instances are frequently used during command execution.
Menu bindings
The command table describing the menu item bindings can be found in the PowerCommands.vsct file. This file is relatively long due to the number of commands defined there. However, the same pattern is repeated for the all commands. Here I show an extract from the .vsct file containing all nodes representing the Copy Path Command:
<?xml version="1.0" encoding="utf-8"?>
<CommandTable xmlns=
"http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<Extern href="stdidcmd.h"/>
<Extern href="vsshlids.h"/>
<Extern href="msobtnid.h"/>
<Commands package="guidPowerCommandsPkg">
<Groups>
<Group guid="guidCopyPathCommand" id="grpidCopyPathCommandGroup"
priority="0x0100">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_SOLNNODE" />
</Group>
<Group guid="guidCopyPathCommand" id="grpidCopyPathCommandGroup1"
priority="0x0100">
<Parent guid="guidSHLMainMenu" id="IDM_VS_CTXT_PROJNODE" />
</Group>
<!-- Other groups -->
</Groups>
<Buttons>
<Button guid="guidCopyPathCommand" id="cmdidCopyPathCommand"
priority="0x0100" type="Button">
<Icon guid="guidCopyPathCommandBitmap" id="bmpPic1" />
<CommandFlag>DynamicVisibility</CommandFlag>
<CommandFlag>DefaultInvisible</CommandFlag>
<Strings>
<ButtonText>Copy Path</ButtonText>
</Strings>
</Button>
<!-- Other button definitions -->
</Buttons>
<Bitmaps>
<Bitmap guid="guidCopyPathCommandBitmap" href="Resources\Empty16.bmp"
usedList="bmpPic1" />
<!-- Other bitmap definitions -->
</Bitmaps>
</Commands>
<CommandPlacements>
<CommandPlacement guid="guidCopyPathCommand" id="cmdidCopyPathCommand"
priority="0x0100">
<Parent guid="guidCopyPathCommand" id="grpidCopyPathCommandGroup"/>
</CommandPlacement>
<!-- Other command placement definitions -->
</CommandPlacements>
<Symbols>
<GuidSymbol name="guidCopyPathCommand"
value="{7F95D8FB-4996-4763-AF41-A2154A831F77}">
<IDSymbol name="cmdidCopyPathCommand" value="0x5A09" />
<IDSymbol name="grpidCopyPathCommandGroup" value="0x67DB" />
<IDSymbol name="grpidCopyPathCommandGroup1" value="0x67DC" />
<!-- Oher IDSymbols -->
</GuidSymbol>
<GuidSymbol name="guidCopyPathCommandBitmap"
value="{841DCDEE-63A0-4D75-A489-B5B7F40D5C0F}">
<IDSymbol name="bmpPic1" value="1" />
</GuidSymbol>
</Symbols>
</CommandTable>
The Copy Path Command is displayed for the solution, project, folder and item levels in the Solution Explorer’s project hierarchy on the context menu. For each location there is a related Group element that binds the group to its corresponding Parent. The command itself is represented by a Button element that does not contain an explicit Parent definition. Since the button is tied to four Group elements, CommandPlacement items are used to define these relations.
There is an icon belonging to the command that is assigned to the button. Definition of Symbols matches with the structure of groups and buttons.
Implementing a command
Let’s see how a concrete command leverages on PowerCommands’ infrastructure! I have chosen the Copy Path Command to explain the important details.
This commands simply copies the full path name of the item selected in Solution Explorer to the clipboard. The command is represented by the CopyPathCommand class inherited from DynamicCommand. Here is the full source code of the class (I changed the comments for clarity):
[Guid("7F95D8FB-4996-4763-AF41-A2154A831F77")]
[DisplayName("Copy Path")]
internal class CopyPathCommand : DynamicCommand
{
// --- Command ID used for this command (look it in the VSCT file)
public const uint cmdidCopyPathCommand = 0x5A09;
// --- Constructs the command instance
public CopyPathCommand(IServiceProvider serviceProvider): base(
serviceProvider,
OnExecute,
new CommandID(
typeof(CopyPathCommand).GUID,
(int)CopyPathCommand.cmdidCopyPathCommand))
{
}
// --- Enable this command only if there is an open solution
protected override bool CanExecute(OleMenuCommand command)
{
if (base.CanExecute(command))
{
return DynamicCommand.Dte.Solution.IsOpen;
}
return false;
}
// --- Execute the command
private static void OnExecute(object sender, EventArgs e)
{
string path = string.Empty;
if (DynamicCommand.Dte.SelectedItems.Item(1).Project != null)
{
// --- Executed at the project level
path = DynamicCommand.Dte.SelectedItems.Item(1).Project.FullName;
}
else if (DynamicCommand.Dte.SelectedItems.Item(1).ProjectItem != null)
{
// --- Executed at the folder level / item level
path =
DynamicCommand.Dte.SelectedItems.Item(1).ProjectItem.get_FileNames(0);
}
else
{
// --- Executed at the solution level
path = DynamicCommand.Dte.Solution.FullName;
}
Clipboard.SetDataObject(IOHelper.SanitizePath(path), true);
}
}
This command is bound to the menu item passing a CommandID in the base constructor that combines the class’s GUID and the cmdidCopyPathCommand ID. The static OnExecute method is responsible for executing the command (it is also passed as an argument in the base constructor).
The overridden CanExecute method first calls the base implementation. Remember, it checks if the command is enabled at all on the Commands option pages. If the command is enabled, it checks if there is an open solution, otherwise it disables the command.
How VSXtra can help implementing PowerCommands’ architecture?
Pablo has built many utility classes and helper codes above the types of Managed Package Framework and used state-of-the-art .NET technologies (like LINQ, C# 3.0 features) in the code to make the result clean, easy-to-read and easy-to-maintain. I have not reinvented the architecture of PowerCommands, because on one hand it is built on the choices Visual Studio Extensibility offers and on the other hand it is simple and great. I rather refactored the patterns used there to improve the development experience. VSXtra was the framework I used for this improvement. I have applied VSXtra patterns in the following parts of PowerCommands:
No-code initialization for services
Automatic command handler binding
Declarative command handler definition
Command handler inheritance support
Initializing the command handler infrastructure
PowerCommands has a few hundred lines of code to initialize the infrastructure managing commands:
// --- PowerCommandsPackage.cs:
[ProvideService(typeof(SCommandManagerService),
ServiceName = "CommandManagerService")]
// --- Other attributes are omitted
public sealed class PowerCommandsPackage : Package, IVsInstalledProduct
{
protected override void Initialize()
{
base.Initialize();
// --- Initialize services defined within the package
(this as IServiceContainer).AddService(
typeof(SCommandManagerService),
new ServiceCreatorCallback(CreateCommandManagerService),
true);
// --- Init the set of commands available within PowerCommands
CommandSet commandSet = new CommandSet(this);
commandSet.Initialize();
// --- Other initialization code omitted
}
private object CreateCommandManagerService(IServiceContainer container,
Type serviceType)
{
if (container != this)
{
return null;
}
if (typeof(SCommandManagerService) == serviceType)
{
return new CommandManagerService();
}
return null;
}
}
// --- CommandSet.cs:
internal class CommandSet
{
public void Initialize()
{
RegisterMenuCommands();
}
private void RegisterMenuCommands()
{
ICommandManagerService commandManager =
this.serviceProvider.GetService<SCommandManagerService,
ICommandManagerService>();
OleMenuCommand openVSCommandPromptCommand =
new OpenVSCommandPromptCommand(this.serviceProvider);
menuCommandService.AddCommand(openVSCommandPromptCommand);
commandManager.RegisterCommand(openVSCommandPromptCommand);
OleMenuCommand transformTemplatesCommand =
new TransformTemplatesCommand(this.serviceProvider);
menuCommandService.AddCommand(transformTemplatesCommand);
commandManager.RegisterCommand(transformTemplatesCommand);
OleMenuCommand collapseProjectsCommand =
new CollapseProjectsCommand(this.serviceProvider);
menuCommandService.AddCommand(collapseProjectsCommand);
commandManager.RegisterCommand(collapseProjectsCommand);
// --- Majority of command registration code omitted
}
}
The initialization code registers the SCommandManagerService that is responsible to keep a list of commands, and then initializes each command. This initialization takes place in the RegisterMenuCommands method of the CommandSet class. Here each command is assigned to the package-level OleMenuCommandService and registered with the SCommandManagerService. The pattern used above is an easy-to-follow one and I think, this is the shortest way (I mean least lines of code) what you can do with MPF today.
However, I have issues with the code above:
It takes a lot of code to register a simple service for on-demand initialization.
Objects representing commands are put together in runtime; they need registration code to work at all.
There is no way to enumerate the commands; the whole concept requires the SCommandManagerService in order to undertake this responsibility.
All these issues are about writing too much plumbing code. I think these can be enlisted by many VSPackage implementations, so I created a few patterns in VSXtra to avoid them.
Initializing the command handler infrastructure with VSXtra
The initialization scenario with VSXtra can be written shorter:
[ProvideService(typeof(SCommandManagerService),
ServiceName = "CommandManagerService")]
// --- Other options are omitted
public sealed class VSXtraCommandsPackage : PackageBase
{
protected override void Initialize()
{
RegisterCommands();
}
private void RegisterCommands()
{
var commandService = this.GetService<SCommandManagerService,
ICommandManagerService>();
if (commandService != null)
{
foreach (var handler in MenuCommandHandler.
GetRegisteredHandlerInstances<VSXtraCommandsPackage>())
{
commandService.RegisterCommand(handler);
}
}
}
}
The code above is really the full initialization code for the command handling infrastructure, there is no other plumbing code! The key concept is that services and objects representing command handlers are declared as standard .NET types and the PackageBase class observes them and does all the “plumbing steps” required making them work correctly. Later I am going to show you how these declarations look like.
The MenuCommandHandler class knows all the command handler types and instances used by our package. The GetRegisteredHandlerInstances<> method provides an enumerator to list them. As you can infer from the method signature, it can retrieve command handlers registered by other packages (we can have more than one package in the same assembly).
In the RegisterCommand method we can directly obtain a reference to the SCommandManagerService, because at this point it has already been registered with our package.
SCommandManagerService is required only if we want to allow other packages to register commands. If we do not need this scenario, the MenuCommandHandler class holds the information of registered commands and can provide it for requesters without the service. Please observe, in the case I omitted the SCommandManagerService I would not need any initialization code for command handler at all!
I decided to keep this service in order to demonstrate some service-related VSXtra features.
Defining command behavior
PowerCommands uses the DynamicCommand abstract class to extract common behavior like querying the command status and executing the command. The concrete command handlers are instances of DynamicCommand-derived classes. For example, the handler class for the Clear All Panes command is the following:
[Guid("8093C326-9C55-4ACC-96F4-B21525333D10")]
[DisplayName("Clear All Panes")]
internal class ClearAllPanesCommand : DynamicCommand
{
public const uint cmdidClearAllPanesCommand = 0x2E52;
public ClearAllPanesCommand(IServiceProvider serviceProvider) : base(
serviceProvider,
OnExecute,
new CommandID(
typeof(ClearAllPanesCommand).GUID,
(int)ClearAllPanesCommand.cmdidClearAllPanesCommand))
{
}
protected override bool CanExecute(OleMenuCommand command)
{
return base.CanExecute(command);
}
private static void OnExecute(object sender, EventArgs e)
{
foreach (object obj in
((DTE2)DynamicCommand.Dte).ToolWindows.OutputWindow.OutputWindowPanes)
{
OutputWindowPane pane = obj as OutputWindowPane;
if (pane != null)
{
pane.Clear();
}
}
}
}
The intention of this code is clear; it is quite easy to understand how the OnExecute method works. The overridden CanExecute method can define some additional task to the check carried out by the base method (it checks only if the command is enabled on the Commands options page).
However, the constructor of the class seems noisy: it requires a service provider (it must be the package instance) and it calls the base constructor providing the service provider instance, the OnExecute method reference and the related command ID as parameters. Because the command execution is defined by a static method (OnExecute must be static in order to be passed to the base constructor), it is not straightforward to create a handler class as a real base class for other command handlers.
Defining command behavior with VSXtra
VSXtra makes the command handler definition more straightforward. Here is the Clear All Panes command implemented in VSXtra:
[Guid(GuidList.guidVSXtraCommandsCmdSetString)]
public partial class VSXtraCommandGroup: CommandGroup<VSXtraCommandsPackage>
{
[CommandId(CmdIDs.ClearAllPanesCommand)]
[DisplayName("Clear All Panes")]
public sealed class ClearAllPanes: CommandHandlerBase
{
protected override void OnExecute(OleMenuCommand command)
{
OutputWindow.OutputWindowPanes.ForEach(pane => pane.Clear());
}
}
}
The VSXtraCommandGroup represents a logical container of commands having the same GUID in their command ID, and the nested CommandHandlerBase der