Ribbon Embed Image

Previous posts discussed the Revit 2010 Ribbon API, the associated Ribbon SDK Ribbon sample, and my use of it for the MEP sample ribbon panel. Krispy5 very friendlily posted a series of comments to the latter describing a detailed solution to embed the images used in the ribbon buttons into the Revit external application executable assembly. I implemented a sample application RibbonEmbedImage to make Krispy's solution more readable and readily available. This sample is obviously less a demonstration of any Revit API functionality; rather it shows how to make use of Visual Studio and the .NET framework to handle bitmaps as embedded resources.

RibbonEmbedImage implements the following:

This is what the resulting custom ribbon panel looks like when I drag it off the ribbon to a free floating state:

RibbonEmbedImage sample custom ribbon panel

Let's look at the source code of the items mentioned above one by one. There is no code to display for the bitmap, but as said, it needs to have its build action set to 'Embedded Resource'.

Here are the namespaces we make use of:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows.Media.Imaging;
using Autodesk.Revit;

Besides the standard references to System and RevitAPI, these will require references to the following assemblies:

The application defines its own namespace RibbonEmbedImage, and within it two classes named App and Command, i.e. the following global source code structure:

namespace RibbonEmbedImage
{
  public class App : IExternalApplication
  {
  }
 
  public class Command : IExternalCommand
  {
  }
}

Within the App class, we first of all define a constant message to be used as the ribbon panel button tooltip and displayed by the external command invoked by the button:

public const string Message =
  "Ribbon Embed Image says 'Hello world'"
  + " via an embedded bitmap resource.";

Then we add the definition of Krispy's method GetEmbeddedImage to read the embedded bitmap resource and return a BitmapSource instance:

static BitmapSource GetEmbeddedImage( string name )
{
  try
  {
    Assembly a = Assembly.GetExecutingAssembly();
    Stream s = a.GetManifestResourceStream( name );
    return BitmapFrame.Create( s );
  }
  catch
  {
    return null;
  }
}

It extracts a bitmap resource embedded within the executing assembly and returns a corresponding BitmapSource object. The 'name' argument is the fully qualified resource name, i.e. includes the application namespace etc.

Next comes the AddRibbonPanel method which creates a custom ribbon panel labelled "Ribbon Embed Image" containing a push button labelled "Hello" and displaying the embedded resource bitmap as its image:

static void AddRibbonPanel(
  ControlledApplication a )
{
  string path = Assembly.GetExecutingAssembly().Location;
 
  PushButtonData data = new PushButtonData(
    "Hello", "Hello", path, "RibbonEmbedImage.Command" );
 
  BitmapSource bitmap = GetEmbeddedImage( 
    "RibbonEmbedImage.Bitmap1.bmp" );
 
  data.Image = bitmap;
  data.LargeImage = bitmap;
  data.ToolTip = Message;
 
  RibbonPanel panel = a.CreateRibbonPanel(
    "Ribbon Embed Image" );
 
  RibbonItem item = panel.AddButton( data );
}

The only remaining part of the external application class is the implementation of the required interface methods and the call to AddRibbonPanel in the OnStartup method:

public IExternalApplication.Result OnStartup( 
  ControlledApplication a )
{
  AddRibbonPanel( a );
  return IExternalApplication.Result.Succeeded;
}
 
public IExternalApplication.Result OnShutdown(
  ControlledApplication a )
{
  return IExternalApplication.Result.Succeeded;
}

All that is needed for the external command is the implementation of the required interface, i.e. the Execute method. It reuses the application class Message constant and simply passes it back as a warning message to Revit:

    public IExternalCommand.Result Execute( 
      ExternalCommandData commandData, 
      ref string message, 
      ElementSet elements )
    {
      message = App.Message;
      return IExternalCommand.Result.Failed;
    }

Here is the complete RibbonEmbedImage source code and Visual Studio solution.

Many thanks to Krispy for providing this solution! I hope it will prove useful to everybody needing bitmaps or any other resources in a Revit plug-in, whether it be for ribbon panel buttons or other purposes.