Thank you to Sreekar Devatha, from DevTech India, for writing this article for the recently published ADN Platform Technologies Customization Newsletter. This article talks about the new Ribbon API referenced in this overview of the new APIs in AutoCAD 2009. A complete sample demonstrating the use of this API is provided as part of the ObjectARX 2009 SDK, under samples/dotNet/Ribbon.
Introduction
Most of the AutoCAD® UI was redesigned in this release. Ribbon, Menu browser and Tooltips are some of the prominent UI features to list. As you might already know the UI enhancements are based on the new Windows® Presentation Foundation (WPF) programming model introduced by Microsoft. So, let’s start with a small introduction to WPF and then we'll move on to the finer points of customizing the Ribbon bar.
What is WPF?
Windows Presentation Foundation (WPF) is a programming model introduced by Microsoft to build rich Windows client applications.
This graphical subsystem introduced in .NET Framework 3.0 provides a clear separation between appearance and behavior of applications. You generally use eXtensible Application Markup Language (XAML) to implement the appearance of an application while using managed programming languages (code-behind) to implement its behavior. XAML is the new XML-based UI definition language from Microsoft, and as such is a core part of WPF.
Without wasting too much time on WPF let us move quickly on to the Ribbon APIs. If you are new to WPF then you could go through the basics of WPF using the links below before starting with the Ribbon APIs.
Windows Presentation Foundation - MSDN
Windows Presentation Foundation - CodeProject
AutoCAD Ribbon
Before diving into the Ribbon APIs it's necessary to understand the Ribbon layout and its terminology which are covered in this and the following section.
The AutoCAD Ribbon provides a single, compact placement for operations that are relevant to the current workspace. It overcomes the need to display multiple toolbars, reducing clutter in the application window. The Ribbon maximizes the area available for work using a single compact interface.
The Ribbon was built using WPF (part of .NET Framework 3.0) and a comprehensive set of APIs have been provided by Autodesk to help external developers customize it.
Ribbon Layout
The different components of the AutoCAD Ribbon are shown below. Also, depicted in the snapshot are the classes corresponding to each component.
Figure: the AutoCAD Ribbon and its layout
The Ribbon control is the top level control which contains everything in the Ribbon. It is composed of a series of panels, which are organized into tabs labeled by task.
Ribbon tabs control the display and order of Ribbon panels on the Ribbon. You add Ribbon tabs to a workspace to control which Ribbon tabs are displayed on the Ribbon. Ribbon tabs do not contain any commands or controls like a Ribbon panel does; instead, they manage the display of Ribbon panels on the Ribbon. Once a Ribbon tab is created, a panel can then be added to it. Ribbon tabs are of two types, standard and contextual. Standard tabs are always displayed while contextual tabs are displayed based on a particular context: for instance a Block Editor Tab is displayed while editing a Block.
Contextual tabs can appear in two modes:
Replace mode: In this mode the contextual tab gets added to the standard tabs as a regular tab. When the contextual tab is clicked it becomes active and the panels in the contextual tabs replace the panels in the previously active tab.
Append mode: In this mode contextual tabs form another tab set similar to the standard tab set and is displayed side-by-side with the standard tabs and panels. There are two tabs active any time and activating a tab in one tab set does not affect active tab in the other tab set.
Ribbon panels are organized by rows, sub-panels, and panel separators. Rows and sub-panels are used to organize how commands and controls are displayed on the Ribbon panel. A row, similar to a toolbar, determines the order and position that commands and controls appear on the Ribbon panel. Rows run horizontally on a Ribbon panel. If all the commands and controls cannot be displayed on the Ribbon panel, a gray down arrow is displayed for expanding the Ribbon panel. Rows can be divided using a sub-panel which, holds rows to order and position commands and controls. Commands and controls can be added to rows and sub-panels, you can remove the commands and controls that you use infrequently, and rearrange the order of commands and controls. Along with commands and controls, you can also create flyouts that contain multiple commands and only take up the space of a single command.
Prerequisites
- .NET Framework 3.0 installs WPF – The UI components in AutoCAD were built using .NET 3.0
- Visual Studio 2005 (with/without SP1)
- Visual Studio 2005 extensions for .NET Framework 3.0 (WCF & WPF), November 2006 CTP
- Visual Studio 2005 or Expression Blend or any text editor like notepad, etc. could be used to edit the XAML files.
Modules, Namespaces & Classes
The core UI framework for AutoCAD is present in AdWindows and AcRibbon contains the Ribbon specific implementation. These are managed UI class libraries developed using .NET 3.0 and WPF. Only .NET APIs are available and no C++ wrappers are provided.
AdWindows.dll
This library implements the framework for the following Autodesk UI features.
- Ribbon classes
- Autodesk controls
- Tooltips
- Menu browser
- Task dialog, etc.
These are the Ribbon-specific classes under the Autodesk.Windows namespace of this DLL.
- RibbonControl
- RibbonTab
- RibbonPanel
- RibbonPanelSource
- RibbonRow
- RibbonItem
- RibbonButton
- RibbonDropDownButton
- RibbonSeperator
- RibbonForm
- RibbonHwnd
- RibbonRowPanel, etc.
For more details regarding these classes refer the ObjectARX® Managed Reference guide available in the ObjectARX 2009 SDK.
AcRibbon.dll
This library was actually meant to be an internal-only DLL except for the very few APIs which are discussed in this article below. All other APIs included in the DLL should be considered as internal-only.
Classes
- Palette that hosts the AutoCAD Ribbon control
Autodesk.AutoCAD.Ribbon.RibbonPaletteSet
- AutoCAD Ribbon control
Autodesk.AutoCAD.Ribbon.RibbonServices. RibbonPaletteSet.RibbonControl
Properties
- Property to access the default Ribbon host window which is a palette
Autodesk.AutoCAD.Ribbon.RibbonServices. RibbonPaletteSet
Note: You are advised not to use any of the internal APIs as they are unsupported and could be changed or dropped without prior notice.
Custom Ribbon Tab
As discussed above in the Ribbon Layout section, we need to create panels with Ribbon items placed on them. Then, these panels should be categorized based on their usage and hosted on your application-specific Ribbon tabs. To demonstrate this we'll now look into the finer points of the API by adding a simple button to a panel and then host the panel on a tab (Custom Tab).
Button
In this section we'll add a button to the ribbon bar. If we take a look at the classes listed above we have the RibbonButton class which can be used to create a button to be placed on the Ribbon. So, let’s start with the creation of a RibbonButton instance as below:
RibbonButton button = new RibbonButton();
button.Text = "Click Me";
// resourceDictionary
// A XAML resource dictionary that defines a ButtonImage
button.LargeImage =
resourceDictionary["ButtonImage"] as BitmapImage;
button.Orientation = Orientation.Vertical;
button.Size = RibbonItemSize.Large;
button.ShowText = true;
button.ShowImage = true;
button.Id = "ClickMe_1";
Now, this button instance should be placed on a panel that can then be hosted by a tab. But before actually creating the panel we need a row in which to place the button, as discussed earlier.
// Create a Row to add the RibbonButton
RibbonRow row = new RibbonRow();
row.Items.Add(button);
// Create a Ribbon panel source in which to
// place ribbon items
RibbonPanelSource panelSource =
new RibbonPanelSource();
panelSource.Title = "Custom Panel";
panelSource.Rows.Add(row);
// Create a panel for holding the panel
// source content
RibbonPanel panel = new RibbonPanel();
panel.Source = panelSource;
The panel should be hosted on the tab which in turn should be added to the Ribbon control and the equivalent code to achieve this is below:
// Create a tab to manage the above panel
RibbonTab tab = new RibbonTab();
tab.Title = "Custom Tab";
tab.Id = "CustomTab";
tab.IsContextualTab = false;
tab.Panels.Add(panel);
// Now add the tab to AutoCAD Ribbon bar...
RibbonControl ribbonControl =
Autodesk.AutoCAD.Ribbon.RibbonServices.
RibbonPaletteSet.RibbonControl;
ribbonControl.Tabs.Add(tab);
// ... and activate the tab
ribbonControl.ActiveTab = tab;
The below snapshot shows the button added to AutoCAD's Ribbon.
Figure: the button added to the Ribbon bar
One more item that was missing in the above code was an event to identify the click of the button. The following code implements the click event.
button.Click += new RoutedEventHandler(button_Click);
private static void button_Click(
object sender, RoutedEventArgs e)
{
RibbonButton button = sender as RibbonButton;
if (button != null && (button.Id == "ClickMe_1")
{
MessageBox.Show("Click Me clicked ", "Click Me");
e.Handled = true;
}
}
The above code might also be implemented using a combination of XAML and C# code-behind as shown below.
XAML that defines the RibbonTab
<adw:RibbonTab
x:Key="TabXaml" Title="Custom Tab XAML" Id="CustomTabXaml">
<adw:RibbonPanel >
<adw:RibbonPanelSource Title="Custom Panel XAML" >
<!--Add a ribbon row-->
<!--Note: You could add only rows
to the panel source content-->
<adw:RibbonRow x:Uid="adw:RibbonRow_1">
<!--Add Ribbon Items here-->
<!--The items could be any RibbonItem derived classes-->
<!--Like RibbonButton
RibbonDropDownButton,
RibbonForm,
RibbonHwnd,
RibbonLabel
RibbonMenuButton,
RibbonRowPanel,
RibbonSeperator,
RibbonToggleButton
or any RibbonItem derived custom controls-->
<adw:RibbonButton Id="ClickMe_2" ShowText="true">
<adw:RibbonButton.Orientation>
<Orientation>
Vertical
</Orientation>
</adw:RibbonButton.Orientation>
<adw:RibbonButton.Image>
<BitmapImage
UriSource="Images/bitmap1.bmp"/>
</adw:RibbonButton.Image>
<adw:RibbonButton.LargeImage>
<BitmapImage
UriSource="Images/bitmap1.bmp"/>
</adw:RibbonButton.LargeImage>
<adw:RibbonButton.Size>
<adw:RibbonItemSize>
Large
</adw:RibbonItemSize>
</adw:RibbonButton.Size>
<adw:RibbonButton.Text>
Click Me
</adw:RibbonButton.Text>
<adw:RibbonButton.ToolTip>
<src:RibbonToolTip
BasicText = "Click Me basic help"
CommandName = "ClickMe"
ExtendedURISource =
"/MyRibbon;component/Dictionary1.xaml"
ExtendedURISourceKey = "ClickMe_ToolTip"
HelpSource = "./Help/readme.chm"
HelpTopic =
"WS1a9193826455f5ff1dbc298511635bea8752e2f"/>
</adw:RibbonButton.ToolTip>
</adw:RibbonButton>
</adw:RibbonRow>
</adw:RibbonPanelSource>
</adw:RibbonPanel>
</adw:RibbonTab>
C# code-behind to add a button to the ribbon bar using the tab defined in XAML
[CommandMethod("AddButtonXAML")]
public static void AddButtonXAML()
{
// Create a RibbonTab using the resourceDictionary
RibbonTab tab =
resourceDictionary["TabXaml"] as RibbonTab;
// Find the ribbon button and add the event
RibbonRow row = tab.Panels[0].Source.Rows[0];
RibbonItemCollection coll = row.Items;
foreach (RibbonItem item in coll)
{
if (item is RibbonButton)
{
RibbonButton button = (RibbonButton)item;
if (button.Id == "ClickMe_2")
{
button.Click +=
new RoutedEventHandler(button_Click);
}
}
}
// Now add the tab to AutoCAD Ribbon bar and activate it
ribbonControl.Tabs.Add(tab);
ribbonControl.ActiveTab = tab;
}
ToolTip
The next thing you would want to do once you add your objects to the Ribbon bar is to display a tooltip for these objects.
The ToolTip property of the RibbonItem class accepts an object so, we could assign a control object to it to display the control’s content as a tooltip. In this example here we define a Grid control. The control intern uses the Autodesk.Windows.ProgressivePanel class to implement the extended tooltip feature that is available with the AutoCAD tooltips.
XAML
<Grid x:Key="ClickMe_ToolTip">
<StackPanel>
<!--Header Part-->
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<TextBlock Text="ClickMe">
<TextBlock.FontWeight>
<FontWeight>
Bold
</FontWeight>
</TextBlock.FontWeight>
</TextBlock>
</StackPanel>
<!--Basic help information -->
<StackPanel Margin="5,5,5,5">
<TextBlock
Text="This is basic help of click me command">
<TextBlock.TextWrapping>
<TextWrapping>
Wrap
</TextWrapping>
</TextBlock.TextWrapping>
</TextBlock>
</StackPanel>
<!--Extended help information -->
<adw:ProgressivePanel Margin="5,5,5,5">
<StackPanel/>
<!--Click Me Extended Tooltip-->
<Grid>
<StackPanel Orientation="Vertical" Margin="0,0,0,0">
<TextBlock>
Click Me extended ToolTip
</TextBlock>
<Image Margin="40,10,0,0"
Width="150" Height="150"
Source="/MyRibbon;component/Images/Smiley.png" />
</StackPanel>
</Grid>
</adw:ProgressivePanel>
<!-- Footer Part -->
<Line Stroke="Black" StrokeThickness="2" X2="250"/>
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<Grid VerticalAlignment="Center"
HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="21" />
<ColumnDefinition Width="179" />
</Grid.ColumnDefinitions>
<Image HorizontalAlignment="Left" Grid.Column="0"
Width="16" Height="16">
<Image.Source>
/MyRibbon;component/Images/Help.gif
</Image.Source>
</Image>
<TextBlock HorizontalAlignment="Left" Grid.Column="1"
FontWeight="Bold">
Press F1 for more help
</TextBlock>
</Grid>
</StackPanel>
</StackPanel>
</Grid>
button.ToolTip = resourceDictionary["ClickMe_ToolTip"];
We can do away with this statement above if we define the button in the XAML file by adding the tooltip to RibbonButton in the XAML as below:
<adw:RibbonButton.ToolTip>
<!-- Define tooltip here, above XAML without
x:Key value could be used -->
</adw:RibbonButton.ToolTip>
Here's a snapshot of the extended tooltip:
Figure: Ribbon object tooltip
Although we can display tooltip using a control as done above, we will not be able to implement the F1 event-handling mechanism using this approach. The ToolTip UI controls like Autodesk.Windows.ToolTip or System.Windows.Controls.ToolTip with F1 event handlers will not help us here because the Ribbon bar does not accept them similar to the way we could not use the Button class to add a button to the Ribbon bar. This particular feature could easily run into an article in itself, so we'll stop at this point to continue in a future article.

Subscribe via RSS
An Excellent in-sight to the API's of AutoCAD. Keep up the good blogging Kean!
(Through the Interface is the #1 Site for AutoCAD API!!!)
Posted by: Alexander | April 10, 2008 at 02:30 AM
Hi Kean, a useful insight as always!!
I've been experimenting with the XAML interface and I was wondering what you thought of the posts) here: -
http://tinyurl.com/3hyt37 (a post from the Autodesk discussion forums)
In particular I wanted your opinion on whether it would be possible to implement (as Colin French in the above post suggests) a gallery of Dimension styles that, on "mouse-over" updates the current view, and how useful you think this might prove?
Posted by: AlexF | June 25, 2008 at 11:41 AM
I don't see a way of safely doing "preview" modifications of styles without some replumbing work (i.e. not through an external API, which would actually effect the modification, not just preview it).
Kean
Posted by: Kean | June 25, 2008 at 05:09 PM
I guess it'll just have to go on my wish list for a future version then. :)
Incidentally, do you happen to have any links/documentation about what exactly the Extended Tooltips can show/limitations on size etc? - the documentation provided with 2009 is sketchy at best. I've downloaded the objectarx samples but they are pretty sparse as far as useable information goes.
Posted by: AlexF | June 26, 2008 at 12:55 PM
I haven't, I'm afraid: I haven't yet spent much time with this API, either - this post was provided by a member of my team, so I really only provided editing services. :-)
Kean
Posted by: Kean | June 26, 2008 at 04:06 PM
Ok, not to worry then.
Posted by: AlexF | June 27, 2008 at 10:51 AM
Hi Kean:
I see that there is a ribboncombobox but I can not seem to get it to work.
this works:
RibbonComboBox cmb1 = new RibbonComboBox;
this causes an error:
row1.Items.Add(cmb1);
Posted by: Trevor Templeton | December 08, 2008 at 06:11 PM
Hi Trevor,
Sorry - I didn't write this article, and am not familiar with the code. I suggest posting your question to the ADN website, if you're a member, or to the AutoCAD .NET Discussion Group, if not.
Regards,
Kean
Posted by: Kean Walmsley | December 08, 2008 at 10:02 PM
Hello,
thank you for all these usefull informations (and not only this article :) )
I use Ribbon, in an application, thanks to AdWindows.dll, acRibbon.dll (and also acdbmgb.dll and acmdg.dll)
I tried to make my app runs on Autocad 2010 and I saw that there is no ribbon dll anymore ?
Do you know a way to make an application using Ribbon, working on both 2009 and 2010 ?
Posted by: Wilhelm | April 14, 2009 at 04:55 PM
Hi Willhelm,
I'm pretty sure the functionality in AcRibbon.dll has been combined into AdWindows.dll (or perhaps AcWindows.dll).
You will also need to migrate your code, as there are differences in the way AutoCAD 2010's ribbon has been implemented. More on that in an upcoming post (the information provided via ADN will be able to help in the meantime, if you're a member).
Regards,
Kean
Posted by: Kean Walmsley | April 14, 2009 at 05:34 PM
Hi,
thank you for your help.
I succed making my application compile, using adWindows.dll and acWindows.dll
I had to change RibbonRow to RibbonRowPanel.
The only thing I can't do (for the moment^^) is Clicking on a RibbonButton. They removed the "Click" event.
Thank again
Regards,
Wilhelm
Posted by: Wilhelm | April 15, 2009 at 10:02 AM
Hello Kean,
I applied the first method in your article. Everything works fine, but I can't figure out how to open the custom ribbon tab at the start of autocad 2009. I can open the tab when I manually run the command. I applied the IExtension application and Initialize() function, imported ads_queueexpr, and have this line in Initialize function: ads_queueexpr("(command \"Ribbon\")") before I run the function to create the tab. But it seems Ribbon can't be edited before autocad starts fully. Is there a solution to this? Thnaks.
Posted by: Stardust1611 | April 30, 2009 at 09:49 AM
I'm afraid I don't know the answer to this: this post was for AutoCAD 2009 and was provided by a guest author.
In AutoCAD 2010 I know you can use the CUIX API to have your custom ribbon tab loaded on startup - perhaps someone on the discussion groups knows how to do this for 2009.
Kean
Posted by: Kean Walmsley | April 30, 2009 at 01:06 PM
Hello Kean,
do you have in plan to write an article about AutoCAD 2010 Ribbon like this one? As Wilhelm mentioned, the Click event is missing. How do we make the button work?
Posted by: Stardust1611 | May 11, 2009 at 02:33 PM
Yes, it's getting higher up my list...
Kean
Posted by: Kean Walmsley | May 12, 2009 at 09:40 AM
Hi Kean,
Any thoughts to what can be done to make up for the missing Click event in RibbonButton.
Posted by: cadCzar | June 03, 2009 at 06:02 PM
For those of you who are ADN members, you can find a technical solution on the ADN site which should help: TS88988 - Adding a Ribbon Tab at runtime (non CUIx file).
I will publish something based on this when I have the time to research it myself, but in the meantime this is the approach, from what I can tell:
When you create your button, it needs to have a command string and a handler:
The command handler class, derived from ICommand, then takes care of executing the command using SendStringToExecute:
public class AdskCommandHandler: System.Windows.Input.ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { //is from a Ribbon Button? RibbonButton ribBtn = parameter as RibbonButton; if (ribBtn != null) Application.DocumentManager.MdiActiveDocument.SendStringToExecute( (String)ribBtn.CommandParameter, true, false, true ); //is from s Ribbon Textbox? RibbonTextBox ribTxt = parameter as RibbonTextBox; if (ribTxt != null) System.Windows.Forms.MessageBox.Show(ribTxt.TextValue); } }I hope this is enough to get you going,Kean
Posted by: Kean Walmsley | June 04, 2009 at 11:19 AM
An additional comment Fenton Webb on this:
It is actually possible to specify the controlling class (the System.Windows.Input.ICommand) of the raw RibbonButton using the x:Class property... This is referred to as “code behind”. Something like this, where CsMgdAcad8.AdskCommandHandler is the class derived from System.Windows.Input.ICommand:
Kean
Posted by: Kean Walmsley | June 04, 2009 at 01:57 PM