Creating Managed Outlook Buttons with Icons

I've been working on an Outlook Add-in in C# using the Visual Studio Shared Add-in project. I referenced this article on how to create a custom icon for your button: Custom Button Faces in a Managed Code Add-in for the Microsoft Office System

In addition to this article, most of the information I've seen on the internet and msdn discusses how to set the icon of a button (CustomFace) using the clipboard and calling CommandBarButton.PaseFace(). How ugly. Furthermore, if you do this then you cannot avoid the problems of the mask not getting applied (which looks really ugly, see the screenshot from the MSDN article).

You also  have to deal with issues such as saving and restoring the clipboard contents so that the user does not lose data.

If you are using Outlook 2003 there is a much better way. Here is how you do it:

Create Project

This article assumes you've created a Shared Add-in project already. For a good overview of this see https://blogs.msdn.com/dancre/archive/2004/03/21/93712.aspx

Create Bitmaps of Icons

  1. Using your favorite bitmap editor (I just use mspaint.exe) create a bitmap that is 16x16 px and paste in the icon you wish to use.

  2. Again, using the same bitmap editor create a second bitmap for the mask. This bitmap should contain white for any region that you want to appear transparent, and use RGB (0,255,0) for any area that you want to appear in the CommandBarButton.

    For example, say we want to use the Bold icon. You would create a bitmap like this:

    And the mask would look like:

     

  3. Save both the bitmap as "bold.bmp" and "bold_mask.bmp".

Create Resource Files

Now we want to add these two bitmaps to a resource file that we can embed in our assembly. I used the instructions on Custom Button Faces in a Managed Code Add-in for the Microsoft Office System with some modifications.

Build the Resource Editor that ships with Visual Studio

  1. Go to localdrive:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Samples\Tutorials\resourcesandlocalization\reseditor
  2. Run build.bat

To add items to a resource file by using the Resource Editor:

  1. In the Resource Editor, in the Add section, select System.Drawing.Bitmap as the type of item to add to the resource file from the drop-down list.
  2. In the text box to the right of the Add section, type bold as the name of the first item to add. This enables the Add button.
  3. Click Add.
  4. Type bold_mask as the name of second item to add and then click Add.
  5. Next, click File and then click Save As. . .
  6. Type IconResource.resX as the name for the project and browse to the parent directory of the sample add-in project.
  7. In the Save as type list, select ResX Files (*.resX) and then click Save to save the resource file
  8. Close the Resource Editor.

Add the resource file to your project, and set the Build Action to Embedded Resource.

Create the Buttons and set the Picture and Mask properties

Now we can create the buttons and set the Picture and Mask properties of the CommandBar. However, you cannot just use this code:

 object missing = Missing.Value;
 
CommandBarButton outlookButton = (
CommandBarButton)commandBars["Standard"].Controls.Add(
MsoControlType.msoControlButton, missing, missing, missing, true);
toolbarButton.Style = button.ButtonStyle;
 
try
{
        ResourceManager rm = new ResourceManager(
        this.GetType().Namespace + ".IconResource", 
        this.GetType().Assembly);
 
        using (Bitmap bmp = (Bitmap)rm.GetObject("bold"))
        {
            if (bmp != null) outlookButton.Picture = bmp;
        }
 
        using (Bitmap bmp = (Bitmap)rm.GetObject("bold_mask"))
        {
            if (bmp != null) outlookButton.Mask= bmp;
        }
 
}
catch
{
}

You see CommandBar.Picture and CommandBar.Mask do not accept bitmaps, but an instance of stdole.IPictureDisp. Great, so how does one create such an object? Not so easy. Thanks to this article though there is a solution. First you need to create this class:

 using System;
using System.Drawing;
using System.Windows.Forms;
using stdole;
 public class AxHost2 : AxHost
{
    public AxHost2() : base(null)
    {
    }
    public new static IPictureDisp GetIPictureDispFromPicture(Image image)
    {
        return (IPictureDisp)AxHost.GetIPictureDispFromPicture(image);
    }
}

This class inherits from System.Windows.Forms.AxHost which has a protected member GetIPictureDispFromPicture which will return an IPictureDisp  from an Image. So now that we have this class, we can modify the code above to read like so:

 using (Bitmap bmp = (Bitmap)rm.GetObject("bold"))
{
    if (bmp != null) outlookButton.Picture = 
     (IPictureDisp)AxHost2.GetIPictureDispFromPicture(bmp);
}
using (Bitmap bmp = (Bitmap)rm.GetObject("bold_mask"))
{
    if (bmp != null) outlookButton.Mask= 
     (IPictureDisp)AxHost2.GetIPictureDispFromPicture(bmp);
}

And that's it. Now you don't have to use the Cliboard and PasteFace to create your icons.