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:
Now let's change the panel background image and panel headers:
What about using a gradient fill and changing the tab header font:
The style is persistent, even if you tear off the panels:
You can use 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:
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 + "\r\n" + 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:
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 + "\r\n" + 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:
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:
adWin.ComponentManager.Ribbon.RenderTransform = new System.Windows.Media.ScaleTransform( 0.5, 0.75 );
adWin.ComponentManager.Ribbon.RenderTransform = new System.Windows.Media.RotateTransform( 5 );
adWin.ComponentManager.Ribbon.RenderTransform = new System.Windows.Media.TranslateTransform( 100, 25 );
adWin.ComponentManager.Ribbon.RenderTransform = new System.Windows.Media.SkewTransform( -15, 0 );
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;
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:
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
Posted by: Crazy Don | February 25, 2011 at 19:13
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.
Posted by: Jeremy Tammik | March 02, 2011 at 07:27
what an amazing sample, i couldn't wait to try it!
Posted by: guest | March 10, 2011 at 09:35
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!
Posted by: Edgardo | June 30, 2011 at 17:54
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.
Posted by: Jeremy Tammik | July 03, 2011 at 16:59
Hi..Jeremy,
I tired the above sample but I amn't able to set color to Ribbon tab Header(Example:Home Tab Header)
Posted by: sangsen | February 08, 2012 at 04:31
Dear Sangsen,
Tough luck, man. You will have to do some debugging then, won't you? Good luck, have fun!
Cheers, Jeremy.
Posted by: Jeremy Tammik | February 08, 2012 at 04:35
Thanks Jeremy,
Posted by: sangsen | February 08, 2012 at 05:34
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........
Posted by: narsimhamurthy | March 15, 2013 at 09:27
Dear Narsimhamurthy,
Pleae read the article right above :-)
Cheers, Jeremy.
Posted by: Jeremy Tammik | March 15, 2013 at 09:46
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
Posted by: [email protected] | April 28, 2013 at 15:49
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
Posted by: Hazem | September 13, 2013 at 09:33