Roy Osherove

View Original

Declarative Visual Studio addin buttons with icons

in the previous post I said how I hated doing custom icons for addins and showed how you can make it work. In this post I’ll show what I feel is a better way of managing the actions on the buttons, and how to declaratively create buttons with icons that are loaded automatically.

First, You can download the source here. What you’ll find there are a bunch of helper classes and a base class that allows you to declare an addin button like this:

    [AddinButtonCaption("yo","some description")]

    [AddinButtonIconName("boldhs.bmp")]

    [AddinUnderMenuName("MenuBar","Tools")]

    public class HelloWorldAddinAction:AddinActionBase

    {

        public HelloWorldAddinAction(CommandBarButton relatedButton)

            : base(relatedButton){}

 

        public override void Execute()

        {

            MessageBox.Show("hello there!");

        }

    }

You set the caption, icon and location of the addin button using the attributes on top of the class. You need to inherit from AddinActionBase and override the Execute() method to do anything useful. You also get access to the related button to change its properties later on.  The icon path is hard coded to be the location of the running assembly\icons.

here is another button that switches icons and text and is located under the “Test” menu:

 

    [AddinButtonCaption("Another Button!","and descccc")]

    [AddinButtonIconName("book_openhs.bmp")]

    [AddinUnderMenuName("MenuBar","Test")]

    class ActionThatReplacesItsIcon:AddinActionBase

    {

        private bool isOn = false;

        public ActionThatReplacesItsIcon(CommandBarButton relatedButton)

            : base(relatedButton)

        {

        }

 

        public override void Execute()

        {

            MessageBox.Show("Another one!");

            isOn = !isOn;

            if(!isOn)

                SetButtonIcon("book_anglehs.bmp");

            else

                SetButtonIcon("book_openhs.bmp");

 

            RelatedButton.Caption = isOn.ToString();

 

        }

    }

It uses the “RelatedButton” property to set caption on the button, and uses a derived method “SetButtonIcon” to change icons.

 

So – how do you initialize all these buttons? in the “OnConnection” method, you call a special “AddinReflector” class that does all the work for you:

        private DTE2 _applicationObject;

        private AddIn _addInInstance;

 

        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)

        {

            _applicationObject = (DTE2)application;

            _addInInstance = (AddIn)addInInst;

            if(connectMode == ext_ConnectMode.ext_cm_UISetup)

            {

                Commands2 commands = (Commands2)_applicationObject.Commands;

                AddinReflector.LoadAllButtonsInCurrentAssembly(commands, _addInInstance,_applicationObject);

            }

        }

 

You need override another method to make the methods execute correctly:

public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)

        {

            handled = false;

            if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)

            {

                AddinActionBase action = AddinRegistry.GetActionByName(commandName);

                action.Execute();

                handled=true;

            }

        }

 

The idea is simplicity: you want to add another button? with an icon? just create a simple class with some attributes and you’re done.

You can download the source here

  • Icons are hard coded to be found under [current assembly location]\icons
  • the classes need to be declared in the current assembly (easily changed if you’d like)