Pimp my AutoCAD or Revit Ribbon

Here is another fun possibility to access and manipulate the Revit ribbon explored by Rudolf Honke of

acadGraph CADstudio GmbH
.
He says:

I’ve played around and found a possibility to modify the appearance of the AutoCAD and Revit ribbon bars.

This is the Revit ribbon bar in its usual colour scheme:

Revit ribbon colour scheme

Now let’s change the panel background image and panel headers:

Modified background image and panel headers

What about using a gradient fill and changing the tab header font:

Gradient fill and different tab header font

The style is persistent, even if you tear off the panels:

Free-floating panels
Free-floating panels

You can use different styles in different panels:

Different styles in different panels

So, how to get there?

First, add new references to your VS project:

  • AdWindows.dll
  • UIFramework.dll

You can find them in the same folder as RevitAPI.dll.

Possibly some other references need to be included as well.
Here is a complete list of the references in my project, although some of them may not be needed for UI customizing purposes:

UI customisation references

Now add this to your ExternalApplication class:


using System.Reflection; // for getting the assembly path
using System.Windows.Media; // for the graphics
using System.Windows.Media.Imaging;
 
// use an alias because Autodesk.Revit.UI 
// uses classes which have same names:
 
using adWin = Autodesk.Windows;

Then insert this into your OnStartup method:


public Result OnStartup( UIControlledApplication a )
{
  try
  {
    adWin.RibbonControl ribbon
      = adWin.ComponentManager.Ribbon;
 
    ImageSource imgbg = new BitmapImage(
      new Uri( Path.Combine(
        Path.GetDirectoryName(
          Assembly.GetExecutingAssembly().Location ),
        "yourBackGroundPicture.jpg" ),
        UriKind.Relative ) );
 
    // define an image brush
 
    ImageBrush picBrush = new ImageBrush();
    picBrush.ImageSource = imgbg;
    picBrush.AlignmentX = AlignmentX.Left;
    picBrush.AlignmentY = AlignmentY.Top;
    picBrush.Stretch = Stretch.None;
    picBrush.TileMode = TileMode.FlipXY;
 
    // define a linear brush from top to bottom
 
    LinearGradientBrush gradientBrush
      = new LinearGradientBrush();
 
    gradientBrush.StartPoint
      = new System.Windows.Point( 0, 0 );
 
    gradientBrush.EndPoint
      = new System.Windows.Point( 0, 1 );
 
    gradientBrush.GradientStops.Add(
      new GradientStop( Colors.White, 0.0 ) );
 
    gradientBrush.GradientStops.Add(
      new GradientStop( Colors.Blue, 1 ) );
 
    // change the tab header font
 
    ribbon.FontFamily = new System.Windows.Media
      .FontFamily( "Bauhaus 93" );
 
    ribbon.FontSize = 10;
 
    // iterate through the tabs and their panels
 
    foreach( adWin.RibbonTab tab in ribbon.Tabs )
    {
      foreach( adWin.RibbonPanel panel in tab.Panels )
      {
        panel.CustomPanelTitleBarBackground
          = gradientBrush;
 
        panel.CustomPanelBackground
          = picBrush; // use your picture
 
        //panel.CustomPanelBackground 
        //  = gradientBrush; // use your gradient fill
      }
    }
    adWin.ComponentManager.UIElementActivated += new
      EventHandler<adWin.UIElementActivatedEventArgs>(
        ComponentManager_UIElementActivated );
  }
  catch( Exception ex )
  {
    winform.MessageBox.Show(
      ex.StackTrace + "rn" + ex.InnerException,
      "Error", winform.MessageBoxButtons.OK );
 
    return Result.Failed;
  }
  return Result.Succeeded;
}

You can access the items inside the panels like this:


  foreach( adWin.RibbonItem item
    in panel.Source.Items )
  {
    . . .
  }

Note that each tab, panel, or item provides event handlers, so you can subscribe to their events like this:


  tab.Activated
    += new EventHandler(tab_Activated);
 
  panel.PropertyChanged
    += new PropertyChangedEventHandler(
      panel_PropertyChanged);
 
  item.PropertyChanged
    +=new PropertyChangedEventHandler(
      item_PropertyChanged);

There are many undiscovered properties in the Revit Ribbon classes, so feel free to explore them on your own (and tell us your discoveries).

By the way, AutoCAD 2011 also includes the AdWindows.dll assembly, but there is no UIFramework.dll in the AutoCAD program folder (using AutoCAD MEP 2011).

Using the AdWindows.dll assembly, we can access the ribbon control like this:


using Autodesk.Windows;
RibbonControl ribbon = ComponentManager.Ribbon;

Since both AutoCAD and Revit include the AdWindows.dll assembly, there are actually two ways to access to the ribbon control in Revit;
either using AdWindows.dll or UIFramework.dll, respectively:


  // like in AutoCAD:
 
  RibbonControl ribbon = ComponentManager.Ribbon;
 
  // also possible, but requires 
  // UIFramework.dll reference, 
  // so we could better use the first method:
 
  UIFramework.RevitRibbonControl ribbon
    = UIFramework.RevitRibbonControl.RibbonControl;

Additionally, one can also use Reflection to retrieve the Ribbon bar from a RibbonPanel p created in the ‘normal’ way via its p.RibbonControl method:


private Autodesk.Windows.RibbonPanel
  GetPanelFromPanel(
    Autodesk.Revit.UI.RibbonPanel panel )
{
  FieldInfo fi
    = panel.GetType().GetField(
      "m_RibbonPanel",
      BindingFlags.Instance
      | BindingFlags.NonPublic );
 
  if( null != fi )
  {
    Autodesk.Windows.RibbonPanel p
      = fi.GetValue( panel )
      as Autodesk.Windows.RibbonPanel;
 
    return p;
  }
  return null;
}

So, one can customize the AutoCAD ribbon in a similar way:

Pimp the AutoCAD ribbon

This can be performed by the following little piece of code:


public void Initialize()
{
  RibbonControl ribbon = ComponentManager.Ribbon;
 
  ImageSource imgbg = new BitmapImage(
    new Uri( Path.Combine( Path.GetDirectoryName(
      Assembly.GetExecutingAssembly().Location ),
      "yourBackGroundPicture.jpg" ),
      UriKind.Relative ) );
 
  // define an image brush
 
  ImageBrush picBrush = new ImageBrush();
  picBrush.ImageSource = imgbg;
  picBrush.AlignmentX = AlignmentX.Left;
  picBrush.AlignmentY = AlignmentY.Top;
  picBrush.Stretch = Stretch.None;
  picBrush.TileMode = TileMode.FlipXY;
 
  // define a linear brush from top to bottom
 
  LinearGradientBrush myLinearGradientBrush
    = new LinearGradientBrush();
 
  myLinearGradientBrush.StartPoint
    = new System.Windows.Point( 0, 0 );
 
  myLinearGradientBrush.EndPoint
    = new System.Windows.Point( 0, 1 );
 
  myLinearGradientBrush.GradientStops.Add(
    new GradientStop( Colors.White, 0.0 ) );
 
  myLinearGradientBrush.GradientStops.Add(
    new GradientStop( Colors.Blue, 1 ) );
 
  // change the tab header font
 
  ribbon.FontFamily = new FontFamily( "Bauhaus 93" );
  ribbon.FontSize = 10;
 
  // now iterate through the tabs and their panels
 
  foreach( RibbonTab tab in ribbon.Tabs )
  {
    foreach( RibbonPanel panel in tab.Panels )
    {
      panel.CustomPanelTitleBarBackground
        = myLinearGradientBrush;
 
      panel.CustomPanelBackground = picBrush;
 
      //panel.CustomPanelBackground 
      //  = myLinearGradientBrush; 
    }
  }
}

As you see, only the access to the ribbon control differs, but most of the code is identical to the Revit version.

Since we can access the ribbon bar identically in both AutoCAD and Revit using AdWindows.dll, one could easily build a common module for ribbon operations in general.

Also, an event listener can be achieved for both CAD systems:


ComponentManager.UIElementActivated
  += new EventHandler<Autodesk.Windows
    .UIElementActivatedEventArgs>(
      ComponentManager_UIElementActivated );
 
void ComponentManager_UIElementActivated(
  object sender,
  Autodesk.Windows.UIElementActivatedEventArgs e )
{
  // e.UiElement.PersistId says which item has been pressed
}

By the way, this is my first AutoCAD / Revit hybrid contribution.
This might be interesting for AutoCAD developers too, I think.
But after all, it’s just playing around…

To wrap this up, here is a fully functional example of a Revit external application to play around with which exercises some of this functionality,
PimpMyRevit.
It includes the Visual Studio solution, an add-in manifest, and the external application implementation source code, which looks like this in its entirety:


using System;
using System.IO;
using System.Reflection;
using winform = System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI;
using adWin = Autodesk.Windows;
 
namespace PimpMyRevit
{
  [Regeneration( RegenerationOption.Manual )]
  public class UIApp : IExternalApplication
  {
    public Result OnStartup( UIControlledApplication a )
    {
      try
      {
        adWin.RibbonControl ribbon
          = adWin.ComponentManager.Ribbon;
 
        ImageSource imgbg = new BitmapImage(
          new Uri( Path.Combine(
            Path.GetDirectoryName(
              Assembly.GetExecutingAssembly().Location ),
            "yourBackGroundPicture.jpg" ),
            UriKind.Relative ) );
 
        // define an image brush
 
        ImageBrush picBrush = new ImageBrush();
        picBrush.ImageSource = imgbg;
        picBrush.AlignmentX = AlignmentX.Left;
        picBrush.AlignmentY = AlignmentY.Top;
        picBrush.Stretch = Stretch.None;
        picBrush.TileMode = TileMode.FlipXY;
 
        // define a linear brush from top to bottom
 
        LinearGradientBrush gradientBrush
          = new LinearGradientBrush();
 
        gradientBrush.StartPoint
          = new System.Windows.Point( 0, 0 );
 
        gradientBrush.EndPoint
          = new System.Windows.Point( 0, 1 );
 
        gradientBrush.GradientStops.Add(
          new GradientStop( Colors.White, 0.0 ) );
 
        gradientBrush.GradientStops.Add(
          new GradientStop( Colors.Blue, 1 ) );
 
        // change the tab header font
 
        ribbon.FontFamily = new System.Windows.Media
          .FontFamily( "Bauhaus 93" );
 
        ribbon.FontSize = 10;
 
        // iterate through the tabs and their panels
 
        foreach( adWin.RibbonTab tab in ribbon.Tabs )
        {
          foreach( adWin.RibbonPanel panel in tab.Panels )
          {
            panel.CustomPanelTitleBarBackground
              = gradientBrush;
 
            panel.CustomPanelBackground
              = picBrush; // use your picture
 
            //panel.CustomPanelBackground 
            //  = gradientBrush; // use your gradient fill
          }
        }
        adWin.ComponentManager.UIElementActivated += new
          EventHandler<adWin.UIElementActivatedEventArgs>(
            ComponentManager_UIElementActivated );
      }
      catch( Exception ex )
      {
        winform.MessageBox.Show(
          ex.StackTrace + "rn" + ex.InnerException,
          "Error", winform.MessageBoxButtons.OK );
 
        return Result.Failed;
      }
      return Result.Succeeded;
    }
 
    public Result OnShutdown( UIControlledApplication a )
    {
      adWin.ComponentManager.UIElementActivated
        -= ComponentManager_UIElementActivated;
 
      return Result.Succeeded;
    }
 
    void ComponentManager_UIElementActivated(
      object sender,
      Autodesk.Windows.UIElementActivatedEventArgs e )
    {
      // e.UiElement.PersistId says which item has been pressed
    }
  }
}

Have fun with trying different pictures and colours.
By the way, one could write an add-in which stores different ‘styles’ in a xml file…
Revit for boys, Revit for girls, blue theme or pink theme ;-)

One notice: you have to re-invoke the function after all add-ins have been loaded completely, because it obviously does not affect the panels that are created after the call.
One could put the ribbon control into a global variable that can be used by the OnDocumentOpened event or something like that after all panels have been populated.

Here is my Revit started up after loading this external application, with some of the add-ins affected and others left in their original pristine state:

Partially pimp Revit add-ins ribbon tab

Ribbon Bar Transformations

Some additions to the pimping theme.

You can apply all kinds of transformations to the ribbon bar, including translation, scaling, and rotation. You can turn it upside down, or throw it into space somewhere…

After the transformation, the ribbon buttons remain just as functional as before, e.g. tool tips are displayed and the buttons can be selected as usual.

Because the Ribbon bar is a WPF element, it can be transformed this way:


using adWin = Autodesk.Windows;
  adWin.RibbonControl ribbon
    = adWin.ComponentManager.Ribbon;
 
  TransformGroup group = new TransformGroup();
  //group.Children.Add(new RotateTransform(-2));
  group.Children.Add( new TranslateTransform(
    ribbon.ActualWidth * 0.25, 160 ) );
 
  group.Children.Add( new ScaleTransform(
    scale, scale ) );
 
  group.Children.Add( new SkewTransform(
    15, -3 ) );
 
  ribbon.RenderTransform = group;

Feel free to test different combinations.
Below are some examples.
To begin with, here is The Ribbon bar in its default state:

Revit ribbon bar in default state


  adWin.ComponentManager.Ribbon.RenderTransform
    = new System.Windows.Media.ScaleTransform(
      0.5, 0.75 );

Revit ribbon bar with scaling transform


  adWin.ComponentManager.Ribbon.RenderTransform
    = new System.Windows.Media.RotateTransform(
      5 );

Revit ribbon bar with rotation transform


  adWin.ComponentManager.Ribbon.RenderTransform
    = new System.Windows.Media.TranslateTransform(
      100, 25 );

Revit ribbon bar with translation transform


  adWin.ComponentManager.Ribbon.RenderTransform
    = new System.Windows.Media.SkewTransform(
      -15, 0 );

Revit ribbon bar with skew transform


  System.Windows.Media.TransformGroup g
    = new System.Windows.Media.TransformGroup();
 
  g.Children.Add( new ScaleTransform( 0.75, 0.75 ) );
  g.Children.Add( new SkewTransform( -25, 0 ) );
  g.Children.Add( new TranslateTransform( 50, 10 ) );
 
  adWin.ComponentManager.Ribbon.RenderTransform = g;

Revit ribbon bar with several concatenated transformations

The black area is the container of the Ribbon; it may be possible to place your own WPF forms into it.
But this topic is more WPF than Revit API related.

Ribbon Transformations for AutoCAD

As said, the same thing works in AutoCAD as well.
This is the AutoCAD version:


using Autodesk.Windows;
using Autodesk.AutoCAD.Runtime;
using System.Windows.Media;
 
namespace PimpMyAutoCAD
{
  public class UIApp : IExtensionApplication
  {
    public void Initialize()
    {
      try
      {
        TransformGroup group = new TransformGroup();
 
        group.Children.Add(
          new ScaleTransform( 0.96, 1 ) );
 
        group.Children.Add(
          new SkewTransform( -25, 0 ) );
 
        group.Children.Add(
          new TranslateTransform( 50, 0 ) );
 
        ComponentManager.Ribbon.RenderTransform
          = group;
      }
      catch
      {
      }
    }
 
    public void Terminate()
    {
    }
  }
}

Here is the result of running that:

Pimp my AutoCAD transformation


Comments

12 responses to “Pimp my AutoCAD or Revit Ribbon”

  1. Now that’s style ;)
    I think it would be funny to prank some users with images of buttons that aren’t really buttons at all…. haha I’m totally going to do that

  2. Dear Don,
    I’m sure your users will be suitably delighted and impressed!
    That will be the final proof of the utter completeness and perfection of your app, with nothing else left to do :-)
    Cheers, Jeremy.

  3. what an amazing sample, i couldn’t wait to try it!

  4. Edgardo Avatar
    Edgardo

    Can’t make it work on Revit Arch 2012… I’ve dowloaded 2012 SDK and when I try to load the PimpMyRevit.dll file it tells me that “UIApp implements IExternalCommand but doesn’t contain both RegenerationAttribute and TransactionAttribute!”. Does current DLL file need extra coding to make it work on 2012? Thanks for your help!

  5. Dear Edgardo,
    Yes, the posted version is for Revit 2011. Yes, you probably need to do something to run it in Revit 2012.
    I will definitely not support anybody in doing so, because this if for professional programmers only, who know what they are doing.
    If you run into a problem with this that you have difficulty solving on your own, I strongly suggest you leave this project alone.
    Cheers, Jeremy.

  6. Hi..Jeremy,
    I tired the above sample but I amn’t able to set color to Ribbon tab Header(Example:Home Tab Header)

  7. Dear Sangsen,
    Tough luck, man. You will have to do some debugging then, won’t you? Good luck, have fun!
    Cheers, Jeremy.

  8. Thanks Jeremy,

  9. narsimhamurthy Avatar
    narsimhamurthy

    i want chage revit ribbon color is there any possibility to chage is there any option ..how to do that..tell me sir…i want see my revit interface with diffrent style..with diffrent colors…is it possible if possible how to do sir……..

  10. Dear Narsimhamurthy,
    Pleae read the article right above :-)
    Cheers, Jeremy.

  11. curioustomeetu@gmail.com Avatar
    curioustomeetu@gmail.com

    Dear Jeremy Tammik,
    Your article about tuning UI of AutoCAD or Revit is very interesting.
    I would like to create custom color schemes for AutoCAd which could be selected and activated from Options menu Display tab Window Elements Color scheme. By default – as you know – there are two option for this setting: ‘light’ or ‘dark’ one.
    I would like to get control over all of the colors of AutoCAD graphic user interface and make new color schemes with new names which could be selected from the above mentioned menu.
    I hope you can add some tips to do that.
    Plz give me some hints

  12. this very excellent
    you are the most amazing man I have ever seen
    I tried this options and working very good
    but I have problem in changing the font colors
    can you help me in that

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading