How to: Display a Custom Icon on the Add-in Button

You can replace the default icon (a smiley-face) that displays next to your add-in command with an icon that is not one of the predefined standard icons as outlined in How to: Change the Default Icon for an Add-in.

You do this by:

  • Placing the icon bitmap as a resource in a satellite DLL file.

  • Setting the MSOButton parameter in the AddNamedCommand2 method to false (which notifies the method to look in the satellite DLL for the icon bitmap).

  • Referencing the ID number of that resource in the commandbar portion of your add-in project.

The procedure below demonstrates how to add a custom icon to the add-in button.

Note

The dialog boxes and menu commands you see might differ from those described in Help depending on your active settings or edition. These procedures were developed with the General Development Settings active. To change your settings, choose Import and ExportSettings on the Tools menu. For more information, see Visual Studio Settings.

To add a custom bitmap as an add-in button icon to a add-in project

  1. Open an existing add-in solution, or create a new add-in solution in Visual Studio.

    If you create a new add-in, be sure to check the "Would you like to create command bar UI for your Add-in?" option.

  2. Add a new resource file to your add-in project. To do this:

    1. Right-click the add-in project in Solution Explorer.

    2. Select New Item on the Add menu.

    3. Select Resources File in the Templates list and click the Add button. Leave its default name (Resources1.resx).

      This starts the Visual Studio Resource Editor.

  3. Click the Show All Files button on tool bar of Solution Explorer.

  4. In the properties for Resource1.resx, set the Build Action property to None.

    Add-ins require an integer value as the bitmap argument. Setting this property allows you to edit the resource file and name its bitmap resource with a numeric identifier, something you cannot do when the .resx file is part of add-in project.

  5. In the Resource Editor, click Add Resource, and then select BMP Image on the New Image menu. For now, leave its default name (Image1.bmp).

    Alternatively, you can select an existing bitmap image that is 16 x 16 pixels and either 16 Color or True Color. Custom icons for add-ins must be 16 x 16 pixels and be either 16-color or True Color.

  6. In the bitmap properties window, change both the Height and Width properties to 16. Set the Colors property to either 16 Color or True Color.

  7. If you created a new bitmap, edit the picture in the Resource Editor.

  8. Right-click the Resource1.resx file in Solution Explorer and select Exclude From Project.

    Doing this prevents unnecessary compiler errors. The file does not need to be in the project. This allows you to use the built-in Resource Editor.

  9. Open the Connect class for the add-in. In the OnConnection method in the AddNamedCommand2 line, change the MSOButton parameter value from true to false, and the Bitmap parameter value from 59 to 1. For example:

    command = commands.AddNamedCommand2(_addInInstance, "MyAddin1", " 
    MyAddin1", "Executes the command for MyAddin1", False, 1, Nothing, 
    CType(vsCommandStatus.vsCommandStatusSupported, Integer) + 
    CType(vsCommandStatus.vsCommandStatusEnabled, Integer), 
    vsCommandStyle.vsCommandStylePictAndText, 
    vsCommandControlType.vsCommandControlTypeButton)
    
    Command command = commands.AddNamedCommand2(_addInInstance, 
    "MyAddin1", "MyAddin1", "Executes the command for MyAddin1", false, 
    1, ref contextGUIDS, 
    (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.
    vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, 
    vsCommandControlType.vsCommandControlTypeButton);
    

    Setting the MSOButton argument to false forces the add-in to look to a resource file for its button bitmap. The number, 1, will be the identifier for that bitmap. (It is set in a later step.)

  10. When you are done, select Save All on the File menu, select Build Solution on the Build menu, and then close the solution.

  11. In Windows Explorer, use Notepad to edit the Resource1.resx file.

  12. Search for all instances of "Image1" and change them to "1." When you are done, save the file.

  13. In the \Resources folder for the add-in, change the bitmap file name from Image1.bmp to 1.bmp.

  14. Use ResGen and Assembly Linker (AL) to build the satellite DLL. To do this:

    1. On the Start menu, point to All Programs, point to Microsoft Visual Studio 2005, point to Visual Studio Tools, and then click Visual Studio Command Prompt.

      This sets certain environment variables so that you can more easily reference Visual Studio tools.

    2. At the command prompt, go to the folder containing the .resx file and type Resgen Resource1.resx.

      Resgen is a utility that compiles the specified .resx file into a .resources file. For more information, see Resource File Generator (Resgen.exe).

    3. Again, at the command prompt, type the following: Al.exe /embed:Resource1.resources /culture:en-US /out:<Add-In Name>.resources.dll.

      Replace <Add-In Name> with the name of your add-in. For example, if your add-in project is named MyAddin1, then the /out: switch would be /out:MyAddin1.resources.dll. If the /out: name does not match the name of your project, then the resource DLL will not be found.

      Assembly Linker (Al.exe) converts the specified .resources file into a satellite resource DLL that you can reference in your add-in. (You can change the /culture switch to a language other than English.) For more information, see Assembly Linker (Al.exe).

  15. Using Windows Explorer, browse to the add-in's DLL directory (usually the \bin folder) and create a folder named en-US (for English US, because you typed en-US as the culture value in Al).

  16. Copy the <Add-In Name>.resources.dll file to the new en-US folder.

  17. In Visual Studio, open the add-in project again and run it.

  18. Click the Tools menu.

    The add-in appears on the Tools menu along with your custom icon.

To add a custom bitmap as an add-in button icon to a native Visual C++ add-in

  1. Follow the same procedures as outlined above, but change the following items.

  2. Create a new Visual C++ Win32 DLL project.

  3. Add a Resource File (.rc).

  4. In Resource View, add a bitmap (16 x 16) and give it a numeric ID.

    The bitmap must be 16 x 16 pixels and either 16 color or True Color.

  5. Update the AddNamedCommand2 method in Connect.cpp with MSOButton set to VARIANT_FALSE, and Bitmap set to the bitmap ID you previously assigned.

  6. Build the DLL.

  7. Create a subfolder "1033" (for English locale) in the native add-in DLL directory.

  8. Copy the satellite DLL to the "1033" directory.

  9. Open AddIn.rgs and add two reg key values "SatelliteDllName" and "SatelliteDllPath." For example:

    HKCU
    {
       NoRemove 'SOFTWARE'
       {
          NoRemove 'Microsoft'
          {
             NoRemove 'VisualStudio'
             {
                NoRemove '8.0'
                {
                   NoRemove 'AddIns'
                   {
                      ForceRemove 
                        'NativeAddinCustBitmap.Connect'
                         {
                           val LoadBehavior = d 0
                           val CommandLineSafe = d 0
                           val CommandPreload = d 1
                           val FriendlyName = s 'NativeAddinCustBitmap'
                           val Description = s 'NativeAddinCustBitmap 
                             Description'
                           val SatelliteDllName = s  
                             'NativeAddinCustBitmapUI.dll'
                           val SatelliteDllPath = s 
                             'C:\Test\CustomBitmap\NativeAddinCustBitmap
                             \NativeAddinCustBitmap\Debug'
                         }
                      }
                   }
                }
             }
          }
       }
    

    In "SatelliteDllPath" do not add the locale ID in the path. It will be automatically appended at runtime.

  10. Rebuild the add-in to register the updated information.

Example

Following is a full add-in code example for a Visual Basic/Visual C#/Visual J# add-in that references a custom icon contained in a satellite resource DLL.

Imports System
Imports Microsoft.VisualStudio.CommandBars
Imports Extensibility
Imports EnvDTE
Imports EnvDTE80

Public Class Connect

    Implements IDTExtensibility2
Implements IDTCommandTarget

    Dim _applicationObject As DTE2
    Dim _addInInstance As AddIn

    Public Sub New()

    End Sub

    Public Sub OnConnection(ByVal application As Object, _
      ByVal connectMode As ext_ConnectMode, ByVal addInInst _
      As Object, ByRef custom As Array) Implements _
      IDTExtensibility2.OnConnection
        _applicationObject = CType(application, DTE2)
        _addInInstance = CType(addInInst, AddIn)
        If connectMode = ext_ConnectMode.ext_cm_UISetup Then

            Dim commands As Commands2 = CType(_applicationObject. _
              Commands, Commands2)
            Dim toolsMenuName As String
            Try

                ' To move the command to a different 
                ' menu, change the word, Tools, to the English version 
                ' of the menu. This code will take the culture, append 
                ' the name of the menu, and then add the command to 
                ' that menu. A list of all the top-level menus is 
                ' in the file, CommandBar.resx.
                Dim resourceManager As System.Resources. _
                  ResourceManager = New System.Resources. _
                  ResourceManager("MyAddin3.CommandBar", _
                  System.Reflection.Assembly.GetExecutingAssembly())

                Dim cultureInfo As System.Globalization.CultureInfo _
                  = New System.Globalization. _
                  CultureInfo(_applicationObject.LocaleID)
                toolsMenuName = resourceManager.GetString(String. _
                  Concat (cultureInfo.TwoLetterISOLanguageName, _
                  "Tools"))

            Catch e As Exception
                ' We tried to find a localized version of the word, 
                ' Tools, but one was not found. Default to the en-US 
                ' word, which may work for the current culture.
                toolsMenuName = "Tools"
            End Try

            ' Place the command on the Tools menu.
            ' Find the MenuBar command bar, which is the top-level 
            ' command bar holding all the main menu items:
            Dim commandBars As CommandBars = _
              CType(_applicationObject.CommandBars, _
              CommandBars)
            Dim menuBarCommandBar As CommandBar = _
              commandBars.Item("MenuBar")

            ' Find the Tools command bar on the MenuBar command bar.
            Dim toolsControl As CommandBarControl = _
              menuBarCommandBar.Controls.Item(toolsMenuName)
            Dim toolsPopup As CommandBarPopup = CType(toolsControl, _
              CommandBarPopup)

            Try
                ' Add a command to the Commands collection.
                Dim command As Command = _
                  commands.AddNamedCommand2(_addInInstance, _
                  "MyAddin3", "MyAddin3", "Executes the command for _
                  MyAddin3", False, 1, Nothing, _
                  CType(vsCommandStatus.vsCommandStatusSupported, _
                  Integer) + CType(vsCommandStatus. _
                  vsCommandStatusEnabled, Integer), _
                  vsCommandStyle.vsCommandStylePictAndText, _
                  vsCommandControlType.vsCommandControlTypeButton)

                ' Find the appropriate command bar on the MenuBar 
                ' command bar.
                command.AddControl(toolsPopup.CommandBar, 1)
            Catch argumentException As System.ArgumentException
                ' If we are here, then the exception is probably 
                ' because a command with that name already exists. If 
                ' so there is no need to recreate 
                ' the command and we can safely ignore the exception.
            End Try
        End If
    End Sub

    Public Sub OnDisconnection(ByVal disconnectMode As _
      ext_DisconnectMode, ByRef custom As Array) Implements _
      IDTExtensibility2.OnDisconnection
    End Sub

    Public Sub OnAddInsUpdate(ByRef custom As Array) Implements _
      IDTExtensibility2.OnAddInsUpdate
    End Sub

    Public Sub OnStartupComplete(ByRef custom As Array) Implements _
      IDTExtensibility2.OnStartupComplete
    End Sub

    Public Sub OnBeginShutdown(ByRef custom As Array) Implements _
      IDTExtensibility2.OnBeginShutdown
    End Sub

    Public Sub QueryStatus(ByVal commandName As String, ByVal _
      neededText As vsCommandStatusTextWanted, ByRef status As _
      vsCommandStatus, ByRef commandText As Object) Implements _
      IDTCommandTarget.QueryStatus
        If neededText = vsCommandStatusTextWanted. _
          vsCommandStatusTextWantedNone Then
            If commandName = "MyAddin3.Connect.MyAddin3" Then
                status = CType(vsCommandStatus.vsCommandStatusEnabled _
                  + vsCommandStatus.vsCommandStatusSupported, _
                  vsCommandStatus)
            Else
                status = vsCommandStatus.vsCommandStatusUnsupported
            End If
        End If
    End Sub

    Public Sub Exec(ByVal commandName As String, ByVal executeOption _
      As vsCommandExecOption, ByRef varIn As Object, ByRef varOut As _
      Object, ByRef handled As Boolean) Implements _
      IDTCommandTarget.Exec
        handled = False
        If executeOption = vsCommandExecOption. _
          vsCommandExecOptionDoDefault Then
            If commandName = "MyAddin3.Connect.MyAddin3" Then
                handled = True
                MsgBox("Add-in is running.")
                Exit Sub
            End If
        End If
    End Sub
End Class
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using System.Resources;
using System.Reflection;
using System.Globalization;

namespace MyAddin1
{
    public class Connect : Object, IDTExtensibility2, IDTCommandTarget
    {
        public Connect()
        {
        }

    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)
        {
            object []contextGUIDS = new object[] { };
            Commands2 commands = 
             (Commands2)_applicationObject.Commands;
            string toolsMenuName;

            try
            {
                ResourceManager resourceManager = new 
                ResourceManager("MyAddin1.CommandBar", 
                Assembly.GetExecutingAssembly());
                CultureInfo cultureInfo = new 
                  System.Globalization.CultureInfo
                  (_applicationObject.LocaleID);
                string resourceName =  String.Concat(cultureInfo.
                TwoLetterISOLanguageName, "Tools");
                toolsMenuName = 
                resourceManager.GetString(resourceName);
            }
            catch
            {
                toolsMenuName = "Tools";
            }

            CommandBar menuBarCommandBar = 
            ((CommandBars)_applicationObject.CommandBars)["MenuBar"];

            CommandBarControl toolsControl = 
            menuBarCommandBar.Controls[toolsMenuName];
            CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

            try
            {
                Command command = 
                  commands.AddNamedCommand2(_addInInstance, "MyAddin1", 
                  "MyAddin1", "Executes the command for MyAddin1", 
                  false, 1, ref contextGUIDS, 
                  (int)vsCommandStatus.vsCommandStatusSupported+(int)
                  vsCommandStatus.vsCommandStatusEnabled,  
                  (int)vsCommandStyle.vsCommandStylePictAndText, 
                  vsCommandControlType.vsCommandControlTypeButton);

                if((command != null) && (toolsPopup != null))
                {
                    command.AddControl(toolsPopup.CommandBar, 1);
                }
            }
            catch
            {
            }
        }
    }

    public void OnDisconnection(ext_DisconnectMode disconnectMode, ref 
      Array custom)
    {
    }

    public void OnAddInsUpdate(ref Array custom)
    {
    }

    public void OnStartupComplete(ref Array custom)
    {
    }

    public void OnBeginShutdown(ref Array custom)
    {
    }
            
    public void QueryStatus(string commandName, 
      vsCommandStatusTextWanted neededText, ref vsCommandStatus status, 
      ref object commandText)
    {
        if(neededText == 
        vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
        {
            if(commandName == "MyAddin1.Connect.MyAddin1")
            {
                status = 
                (vsCommandStatus)vsCommandStatus.
                vsCommandStatusSupported|vsCommandStatus.
                vsCommandStatusEnabled;
            return;
            }
        }
    }

    public void Exec(string commandName, vsCommandExecOption 
    executeOption, ref object varIn, ref object varOut, ref bool 
    handled)
    {
        handled = false;
        if(executeOption == 
          vsCommandExecOption.vsCommandExecOptionDoDefault)
        {
            if(commandName == "MyAddin1.Connect.MyAddin1")
            {
                handled = true;
                System.Windows.Forms.MessageBox.Show("Add-in ran.");
                return;
            }
        }
    }
    private DTE2 _applicationObject;
    private AddIn _addInInstance;
    }
}

See Also

Tasks

How to: Change the Default Icon for an Add-in

How to: Expose an Add-in as a Button on the Toolbar

Concepts

Displaying Add-ins on Toolbars and Menus