Dotneteers.net
All for .net, .net for all!

LVN! Sidebar #4 - Command handlers

When you create a simple menu command with the VSPackage wizard, it creates code snippets in you package class:

// --- Package attributes omitted for clarity

public sealed class SimpleCommandPackage : Package

{

  // --- Constructor generated by VSPackage wizard is omitted

  protected override void Initialize()

  {

    base.Initialize();

 

    OleMenuCommandService mcs = GetService(typeof(IMenuCommandService))

      as OleMenuCommandService;

    if ( null != mcs )

    {

      CommandID menuCommandID = new CommandID(GuidList.guidSimpleCommandCmdSet,

        (int)PkgCmdIDList.cmdidMyFirstCommand);

       MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID );               

       mcs.AddCommand( menuItem );

    }

  }

 

  // --- Menu item callback function

  private void MenuItemCallback(object sender, EventArgs e)

  {

    // --- The generated code pops up a message box, cod is omitted

  }

}

The code here is simple and clear. However, if we use a few dozen commands, the code to bind commands and their handlers gets quite long. If we have some menus, a few of them should be enabled or disabled depending on the state of our package or even the menu text of commands should be dynamically changed. This situation will cause us a few headaches:

—  The Initialize method will be long full with the same code pattern repeated frequently

—  Our package class will be filled up with private event handler methods

—  A command can have up to three handlers: one for the command execution, one for the status query and one for command text setup. These methods can be located from each other quite faraway in the code.

To avoid our headache, we can make some useful refactoring. In this sidebar article I show you how I make the code clear from this noise.

The command handler pattern

I created a pattern where each command handler has a separate type to encapsulate all the event handlers it is responsible for. Here is the blueprint for a concrete command handler class:

[CommandID(GuidList.GuidHowToPackageCmdSetString,

  PkgCmdIDList.cmdShowLearnVSXNowTool)]

internal sealed class LearnVSXNowViewCommandHandler : MenuCommandHandler

{

  protected override void OnExecute(OleMenuCommand command)

  {

    // --- Command handler code goes here

  }

}

The class uses an attribute to declaratively bind the command handler instance with the command it is intended for. In the Initialize method of the package class we can physically create the binding:

protected override void Initialize()

{

  base.Initialize();

  // --- Other initialization code

   new LearnVSXNowViewCommandHandler(this).Bind();

}

This pattern in much easier to follow, since the initialization code is reduced to one line per command and it is easy to navigate to the real command handler code since it can be found in one class.

The CommandIDAttribute class

To specify which command is bound to the command handler we use the (Guid, uint) pair identifying the command. Since a command handler is related to a well-specified command known at construction time, we use a decorating attribute to declare this binding. The attribute is named CommandID and it contains two properties describing the (Guid, uint) pair:

[AttributeUsage(AttributeTargets.Class)]

public sealed class CommandIDAttribute : Attribute

{

  private readonly Guid _Guid;

  private readonly uint _Command;

 

  public CommandIDAttribute(string guid, uint command)

  {

    _Guid = new Guid(guid);

    _Command = command;

  }

 

  public Guid Guid

  {

    get { return _Guid; }

  }

 

  public uint Command

  {

    get { return _Command; }

  }

}

Due to the way attributes work we cannot pass a Guid instance as an attribute property at compile time, so the attribute constructor accepts the Guid parameter in string form.

The MenuCommandHandler class

The essence of the patter is implemented in the MenuCommandHandler class that has the following blueprint:

public abstract class MenuCommandHandler

{

  protected internal MenuCommandHandler(Package package);

 

  public CommandID CommandId { get; }

  protected Package Package { get; }

  protected IServiceProvider ServiceProvider { get; }

  protected OleMenuCommand MenuCommand { get; }

  public bool IsBound { get; }

 

  protected virtual void OnExecute(OleMenuCommand command);

  protected virtual void OnQueryStatus(OleMenuCommand command);

  protected virtual void OnChange(OleMenuCommand command);

  public void Bind();

 

  private void ExecuteMenuCommandCallback(object sender, EventArgs e);

  private void ChangeCallback(object sender, EventArgs e);

  private void BeforeStatusQueryCallback(object sender, EventArgs e);

}

A command always belongs to a package so we pass that package to the constructor. Within the handler instance we can access the package and even its IServiceProvider object. I implemented the handler so that it uses an OleMenuCommand instance within. Unless we call the Bind method the handler is not bound to the command. This mechanism allows you to make any kind of initialization before binding the handler to the command. Actually you are allowed calling Bind in the constructor of your derived handler class.

To respond to the command events you have to override one or more of the following methods: OnExecute, OnQueryStatus and OnChange.

The constructor of the class simply reads the values of the associated CommandIDAttribute since these values determine the command our handler is bound to:

public abstract class MenuCommandHandler

{

  private readonly Package _Package;

  private readonly CommandID _CommandId;

  private OleMenuCommand _MenuCommand;

 

  protected internal MenuCommandHandler(Package package)

  {

    _Package = package;

    foreach (object attr in GetType().GetCustomAttributes(false))

    {

      CommandIDAttribute idAttr = attr as CommandIDAttribute;

      if (idAttr != null)

      {

        _CommandId = new CommandID(idAttr.Guid, (int)idAttr.Command);

      }

    }

  }

We also store the owner package in the constructor that also provides access to package and VS services, so we have two properties to access it:

  protected Package Package

  {

    get { return _Package; }

  }

 

  protected IServiceProvider ServiceProvider

  {

    get { return _Package; }

  }

Through the ServiceProvider property we can call the GetService method. The Bind method uses this approach to create an OleMenuCommand instance:

  public void Bind()

  {

    if (_Package == null) return;

    OleMenuCommandService mcs =

      ServiceProvider.GetService(typeof(IMenuCommandService))

      as OleMenuCommandService;

    if (mcs == null) return;

 

    _MenuCommand = new OleMenuCommand(

      ExecuteMenuCommandCallback,

      ChangeCallback,

      BeforeStatusQueryCallback,

      _CommandId);

    mcs.AddCommand(_MenuCommand);

  }

All we have left is to call the appropriate virtual event methods in menu command event handlers:

  private void ExecuteMenuCommandCallback(object sender, EventArgs e)

  {

    OleMenuCommand command = sender as OleMenuCommand;

    if (command != null) OnExecute(command);

  }

 

  private void ChangeCallback(object sender, EventArgs e)

  {

    OleMenuCommand command = sender as OleMenuCommand;

    if (command != null) OnChange(command);

  }

 

  private void BeforeStatusQueryCallback(object sender, EventArgs e)

  {

    OleMenuCommand command = sender as OleMenuCommand;

    if (command != null) OnQueryStatus(command);

  }

Creating a command handler class

Using the MenuCommandHandler as a base class it is quite easy to create our own command handler class. For example, the following code creates a menu handler displaying a tool window:

[CommandID(GuidList.GuidHowToPackageCmdSetString,

  PkgCmdIDList.cmdShowLearnVSXNowTool)]

internal sealed class LearnVSXNowViewCommandHandler : MenuCommandHandler

{

  public LearnVSXNowViewCommandHandler(Package package) : base(package)

  {

  }

 

  protected override void OnExecute(OleMenuCommand command)

  {

    ToolWindowPane window =

      Package.FindToolWindow(typeof(LearnVSXNowToolWindow), 0, true);

    if ((null == window) || (null == window.Frame))

    {

      throw new NotSupportedException(Resources.CanNotCreateWindow);

    }

    IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame;

    Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show());

  }

}

The code is straightforward and shows how easy to create a custom command handler. In the case we want to access our package with its strong type, we can change the constructor of the class according to the following pattern:

internal sealed class MyCommandHandler : MenuCommandHandler

{

  private readonly MyPackage _MyPackage   public MyCommandHandler(MyPackage package) : base(package)

  {

    _MyPackage = package;  }   protected override void OnExecute(OleMenuCommand command)

  {

    // ...

    _Mypackage.SomeMethod();

    // ...

  }

}

Is used the pattern introduced here and it definitely made my code easier to create and maintain.


Posted Apr 06 2008, 08:14 AM by inovak
Filed under: ,

Comments

29decibel wrote re: LVN! Sidebar #4 - Command handlers
on Wed, Feb 18 2009 12:02

wonderful pattern

great thanks

Clomiphene 50 mg wrote re: LVN! Sidebar #4 - Command handlers
on Wed, Feb 27 2013 17:38

CPkvK2 I truly appreciate this blog.Really thank you! Awesome.