000

Index Labels

An Issue With Using Ribbon Runtime API

.
I recently ran into an issue with Ribbon Runtime API, which an AutoCAD .NET programmer may also come across, if he/she uses Ribbon Runtime API to create ribbon tab/panel to start his/her command method.

Due to the infamous "Fiber" technology used in AutoCAD 2010 through AutoCAD 2014, most of us .NET programmers know that we need to set "Fiberworld" system variable to 0 in order to debug .NET project with AutoCAD in many, if not all, Visual Studio projects, in the expense of losing ribbon/menu usability. Because of this, the issue I ran into with Ribbon Runtime API went undetected from the tests of a few my projects. And when users reported that some of my commands did not work as expected, I was not able to reproduce what users described. Instead, I was looked into whether AutoCAD setup is good, whether OS is OK...which led to a few AutoCAD repairs/reinstalls, but nothing worked.

What the issue is, then? Here is what happens:

One of my command method, when executed, brings up a modal dialog box. There is a button "Pick >" on the form. If user clicks the button, the form hides, then user picks one or more entities in AutoCAD editor. User can also cancels he picking, of course. Once the picking is done/cancelled, the dialog form shows back with the picking result (if the picking is not cancelled) displayed. Pretty standard operation, eh? Yet, users reported that SOMETIMES, after picking, the dialog form flashed back and disappeared immediately. The code the follows after the dialog box being OKed/being cancelled (there are quite some code after the dialog box is closed to be executed) was also not executed and the command method was simply jumped to its end and the "try...catch..." block did not catch anything. Notice the capital "SOMETIMES"? The same user reported the command works sometimes, and then sometimes not. And whenever I tried with my development computer, it always works (why shouldn't it? It is just something I have done many times before. Really strange!).

Eventually, one of the users noticed, the command works when the command is started from a fully expanded ribbon item, and stops working if the ribbon is minimized (AutoCAD ribbon has 3 minimized states). This explains why I never ran into the issue, because I often have to set "Fiberworld" to 0 with my AutoCAD in order to do debugging (thus, ribbon in my AutoCAD often does not work), so I am used to enter command at command line to run my command methods. The ribbon item that starts the command method is included in a custom ribbon tab/panel that is specifically designed/developed for our custom CAD add-in applications and is dynamically generated by code using Ribbon Runtime API.

After finally being able to reproduce the issue myself, I went ahead to manually created a custom partial CUIx to build the same ribbon tab/panel/item that execute the same command method. With the ribbon built with CUIx, whether it is fully expanded or minimized, the execution of the command method is always succeeded as expected.

Obviously, the said issue only exists with ribbon created programmatically using Ribbon Runtime API. The code shown below reproduces the issue, followed by a video clip.

First a couple of utility classes.

Class MyRibbon, used for creating a ribbon tab, adding a ribbon panel with a button into the tab:

    1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using Autodesk.Windows;
    6 using Autodesk.AutoCAD.ApplicationServices;
    7 using Autodesk.AutoCAD.Ribbon;
    8 
    9 namespace RibbonKillsDialog
   10 {
   11     public class MyRibbon
   12     {
   13         private const string TAB_TITLE = "Test Apps";
   14         private const string TAB_ID = "TestApps";
   15 
   16         private const string PANEL_TITLE = "Test Commands";
   17 
   18         public static void AddMyRibbon()
   19         {
   20             RibbonTab tab = CreateRibbonTab();
   21 
   22             BuildPanel(tab);
   23         }
   24 
   25         public static void RemoveMyRibbon()
   26         {
   27             RemoveRibbonTab();
   28         }
   29 
   30         #region private methods
   31 
   32         private static RibbonTab CreateRibbonTab()
   33         {
   34             Autodesk.Windows.RibbonControl ribbonControl =
   35                 RibbonServices.RibbonPaletteSet.RibbonControl;
   36 
   37             Autodesk.Windows.RibbonTab tab = null;
   38 
   39             //Find existing ribbon tab
   40             foreach (var t in ribbonControl.Tabs)
   41             {
   42                 if (t.Title.ToUpper() == TAB_TITLE.ToUpper() &&
   43                     t.Id.ToUpper() == TAB_ID.ToUpper())
   44                 {
   45                     tab = t;
   46                     break;
   47                 }
   48             }
   49 
   50             //If no existing tab found
   51             if (tab == null)
   52             {
   53                 tab = new Autodesk.Windows.RibbonTab();
   54                 tab.Title = TAB_TITLE;
   55                 tab.Id = TAB_ID;
   56 
   57                 ribbonControl.Tabs.Add(tab);
   58             }
   59 
   60             return tab;
   61         }
   62 
   63         private static void RemoveRibbonTab()
   64         {
   65             Autodesk.Windows.RibbonControl ribbonControl =
   66                 RibbonServices.RibbonPaletteSet.RibbonControl;
   67 
   68             foreach (var t in ribbonControl.Tabs)
   69             {
   70                 if (t.Title.ToUpper() == TAB_TITLE.ToUpper() &&
   71                     t.Id.ToUpper() == TAB_ID.ToUpper())
   72                 {
   73                     ribbonControl.Tabs.Remove(t);
   74                     break;
   75                 }
   76             }
   77         }
   78 
   79         private static void BuildPanel(RibbonTab tab)
   80         {
   81             RibbonPanel panel = null;
   82             RibbonPanelSource panelSource = null;
   83 
   84             foreach (var p in tab.Panels)
   85             {
   86                 if (p.Source.Title.ToUpper() == PANEL_TITLE.ToUpper())
   87                 {
   88                     panel = p;
   89                     panelSource = p.Source;
   90 
   91                     break;
   92                 }
   93             }
   94 
   95             if (panel == null)
   96             {
   97                 panelSource = new RibbonPanelSource();
   98                 panelSource.Title = PANEL_TITLE;
   99 
  100                 AddItemsToPanel(panelSource);
  101 
  102                 panel = new RibbonPanel();
  103                 panel.Source = panelSource;
  104 
  105                 tab.Panels.Add(panel);
  106             }
  107         }
  108 
  109         private static void AddItemsToPanel(RibbonPanelSource source)
  110         {
  111             RibbonButton btn = new RibbonButton();
  112             btn.Text = "Test dialog box";
  113 
  114             RibbonToolTip toolTip = new RibbonToolTip();
  115             toolTip.Title = "Test my dialog box";
  116             toolTip.Content = "Test modal dialog box' visibility change";
  117             toolTip.Command = "TestMyDialog";
  118             btn.ToolTip = toolTip;
  119             btn.CommandHandler = new MyRibbonCommandHandler();
  120             btn.CommandParameter = "._TESTMYDIALOG";
  121 
  122             source.Items.Add(btn);
  123         }
  124 
  125         #endregion
  126     }
  127 
  128     public class MyRibbonCommandHandler : System.Windows.Input.ICommand
  129     {
  130         public bool CanExecute(object parameter)
  131         {
  132             return true;
  133         }
  134 
  135         public event EventHandler CanExecuteChanged;
  136 
  137         public void Execute(object parameter)
  138         {
  139             RibbonCommandItem ribbonItem = parameter as RibbonCommandItem;
  140             if (ribbonItem != null)
  141             {
  142                 Document dwg =
  143                     Application.DocumentManager.MdiActiveDocument;
  144 
  145                 string cmdString =
  146                     ((string)ribbonItem.CommandParameter).TrimEnd();
  147                 if (!cmdString.EndsWith(";"))
  148                 {
  149                     cmdString = cmdString + " ";
  150                 }
  151 
  152                 dwg.SendStringToExecute(cmdString, true, false, true);
  153             }
  154         }
  155     }
  156 }

Class PickUtil, used for moving AutoCAD interaction (picking entities) away from dialog box' code behind:

    1 using Autodesk.AutoCAD.ApplicationServices;
    2 using Autodesk.AutoCAD.DatabaseServices;
    3 using Autodesk.AutoCAD.EditorInput;
    4 
    5 namespace RibbonKillsDialog
    6 {
    7     public class PickUtil
    8     {
    9         public static ObjectId PickEntity(
   10             System.Windows.Forms.Control modalDialog, string selectMessage)
   11         {
   12             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
   13 
   14             ObjectId picked = ObjectId.Null;
   15 
   16             using (EditorUserInteraction inter =
   17                 ed.StartUserInteraction(modalDialog))
   18             {
   19                 PromptEntityOptions opt =
   20                     new PromptEntityOptions("\n" + selectMessage);
   21                 PromptEntityResult res = ed.GetEntity(opt);
   22                 if (res.Status == PromptStatus.OK)
   23                 {
   24                     picked = res.ObjectId;
   25                 }
   26             }
   27 
   28             return picked;
   29         }
   30 
   31         public static ObjectId PickEntity(string selectMessage)
   32         {
   33             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
   34 
   35             ObjectId picked = ObjectId.Null;
   36 
   37             PromptEntityOptions opt =
   38                 new PromptEntityOptions("\n" + selectMessage);
   39             PromptEntityResult res = ed.GetEntity(opt);
   40             if (res.Status == PromptStatus.OK)
   41             {
   42                 picked = res.ObjectId;
   43             }
   44 
   45             return picked;
   46         }
   47     }
   48 }

Here is the code behind a dialog box' form (the form's design can be seen from the video clip):

    1 using System;
    2 using System.Windows.Forms;
    3 
    4 using Autodesk.AutoCAD.DatabaseServices;
    5 
    6 namespace RibbonKillsDialog
    7 {
    8     public partial class MyDialogBox : Form
    9     {
   10         public MyDialogBox()
   11         {
   12             InitializeComponent();
   13         }
   14 
   15         public string PickedId
   16         {
   17             get { return txtId.Text; }
   18         }
   19 
   20         private void ShowResult(ObjectId pickedId)
   21         {
   22             if (pickedId == ObjectId.Null)
   23                 txtId.Text = "";
   24             else
   25                 txtId.Text = pickedId.ToString();
   26         }
   27 
   28         private void btnPick1_Click(object sender, EventArgs e)
   29         {
   30             ObjectId id = PickUtil.PickEntity(this, "Select an entity:");
   31 
   32             ShowResult(id);
   33         }
   34 
   35         private void btnPick2_Click(object sender, EventArgs e)
   36         {
   37             this.Visible = false;
   38 
   39             ObjectId id = PickUtil.PickEntity("Select an entity:");
   40 
   41             ShowResult(id);
   42 
   43             this.Visible = true;
   44         }
   45 
   46         private void txtId_TextChanged(object sender, EventArgs e)
   47         {
   48             btnClose.Enabled = txtId.Text.Trim().Length > 0;
   49         }
   50 
   51         private void btnClose_Click(object sender, EventArgs e)
   52         {
   53             this.DialogResult = DialogResult.OK;
   54         }
   55     }
   56 }

Now, this is the command methods that create custom ribbon programmatically and does the work that shows dialog box:

    1 using Autodesk.AutoCAD.ApplicationServices;
    2 using Autodesk.AutoCAD.EditorInput;
    3 using Autodesk.AutoCAD.Runtime;
    4 
    5 [assembly: CommandClass(typeof(RibbonKillsDialog.MyCommands))]
    6 
    7 namespace RibbonKillsDialog
    8 {
    9     public class MyCommands
   10     {
   11         [CommandMethod("MyRibbonOn")]
   12         public static void TurnOnMyRibbonTab()
   13         {
   14             MyRibbon.AddMyRibbon();
   15         }
   16 
   17         [CommandMethod("MyRibbonOff")]
   18         public static void TurnOffMyRibbonTab()
   19         {
   20             MyRibbon.RemoveMyRibbon();
   21         }
   22 
   23         [CommandMethod("TestMyDialog")]
   24         public static void ShowMyDialog()
   25         {
   26             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
   27 
   28             string id = "";
   29 
   30             try
   31             {
   32                 using (MyDialogBox dlg = new MyDialogBox())
   33                 {
   34                     System.Windows.Forms.DialogResult res =
   35                         Application.ShowModalDialog(dlg);
   36                     if (res == System.Windows.Forms.DialogResult.OK)
   37                     {
   38                         id = dlg.PickedId;
   39                     }
   40                 }
   41             }
   42             catch
   43             {
   44                 ed.WriteMessage("\nSomething was wrong...");
   45             }
   46             finally
   47             {
   48                 ed.WriteMessage("\nPicked entity: {0}",
   49                 string.IsNullOrEmpty(id) ? "None" : id);
   50             }
   51 
   52             Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
   53         }
   54     }
   55 }

In this video clip, as it shows, I first ran the "TestMyDialog" command from command line, as I usually do while programming AutoCAD. It worked as expected, of course. Then I made sure the ribbon works by checking "Fiberworld" was set to 1. Then I ran command "MyRibbonOn" to programmatically create my custom ribbon, and then start command "TestMyDialog" from normally displayed/expanded ribbon. The command also worked. After I minimized AutoCAD ribbon and then start "TestMyDialog" command from the minimized ribbon, the dialog box disappeared after the picking is completed. AutoCAD command line also does not show the messages by the 2 lines of code ed.WriteMessage("\n...) in the "catch..." and "finally..." block.

However, the command "TestMyDialog" would always execute correctly if from ribbon item that is create by CUIx.

Noticed that in the form's 2 "Pick >" buttons, my tried different ways to hide and show the dialog form: using EditorUserInteraction object and calling Form.Hide()/Form.Visible=False/True, just to prove that the issue discussed here is not affected by this difference of how form is hidden/shown.

This very issue exists with 2 AutoCAD versions that I have access to currently: AutoCAD 2012/2014. So, I can fairly be sure that it is the same with AutoCAD 2013.

In searching a solution for this issue, as an AutoCAD product license subscription custom, I requested a technical support from Autodesk and got a rather quick respond that provided a solution (thanks to Autodesk technical support team). It turned out the cure to this is fairly simple: setting focus back to AutoCAD editor after user prior to the dialog box being shown. That is, with AutoCAD 2013 or older, we need to call

Autodesk.AutoCAD.Internal.Util.SetFocusToDwgView();

and with AutoCAD 2014, we need to call

Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Focus();

The code change in the command class is like this (the change is in red):

    1 using Autodesk.AutoCAD.ApplicationServices;
    2 using Autodesk.AutoCAD.EditorInput;
    3 using Autodesk.AutoCAD.Runtime;
    4 
    5 [assembly: CommandClass(typeof(RibbonKillsDialog.MyCommands))]
    6 
    7 namespace RibbonKillsDialog
    8 {
    9     public class MyCommands
   10     {
   11         [CommandMethod("MyRibbonOn")]
   12         public static void TurnOnMyRibbonTab()
   13         {
   14             MyRibbon.AddMyRibbon();
   15         }
   16 
   17         [CommandMethod("MyRibbonOff")]
   18         public static void TurnOffMyRibbonTab()
   19         {
   20             MyRibbon.RemoveMyRibbon();
   21         }
   22 
   23         [CommandMethod("TestMyDialog")]
   24         public static void ShowMyDialog()
   25         {
   26             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
   27 
   28             string id = "";
   29 
   30             Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView();
   31 
   32             try
   33             {
   34                 using (MyDialogBox dlg = new MyDialogBox())
   35                 {
   36                     System.Windows.Forms.DialogResult res =
   37                         Application.ShowModalDialog(dlg);
   38                     if (res == System.Windows.Forms.DialogResult.OK)
   39                     {
   40                         id = dlg.PickedId;
   41                     }
   42                 }
   43             }
   44             catch
   45             {
   46                 ed.WriteMessage("\nSomething was wrong...");
   47             }
   48             finally
   49             {
   50                 ed.WriteMessage("\nPicked entity: {0}",
   51                 string.IsNullOrEmpty(id) ? "None" : id);
   52             }
   53 
   54             Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
   55         }
   56     }
   57 }

Since custom ribbons created programmatically via Ribbon Runtime API are part of the AutoCAD customization in my office, the issue discussed here has to be dealt with. Though the solution is simple and easy enough, I have to be prepared that when I create a command method that may involve user interaction between dialog box and AutoCAD editor, I need to anticipate that the command methods may be executed from runtime-generated and minimized ribbon. That might mean that to make it safe, I'd better ALWAYS call

Autodesk.AutoCAD.Internal.Util.SetFocusToDwgView();

or

Autodesk.AutoCAD.ApplicationServices.Application.MainWindow.Focus();

in the command method before doing anything else, so that if this command method somehow ends up being used in programmatically generated ribbon, I do not have to going back to apply the workaround discussed here in future.

With AutoCAD 2015 being coming out in just a few months, I am hoping this issue will go away, but not holding my breath, now that I know the workaround.




Blog Archive

Labels

2D Drafting 3D design 3D Modeling 3D models 3D Printing 3D rendering 3D scanning 3D Sketch Inventor 3D visualization 3D Web App 3ds Max ACC Additive Manufacturing AEC Technology affordable Autodesk tools AI Design AI in CAD AI in Manufacturing AI Tools AR architectural design architectural modeling architectural visualization Architecture architecture design Architecture Firm Architecture Productivity architecture software Artificial Intelligence As-Built Model augmented reality AutoCAD AutoCAD advice AutoCAD Basics AutoCAD Beginners AutoCAD Civil 3D AutoCAD Civil3D AutoCAD commands AutoCAD efficiency AutoCAD features AutoCAD File Management AutoCAD Layer AutoCAD learning AutoCAD print settings AutoCAD productivity AutoCAD Teaching AutoCAD Techniques AutoCAD tips AutoCAD tools AutoCAD training. AutoCAD tricks AutoCAD Tutorial AutoCAD workflow AutoCAD Xref Autodesk Autodesk 2025 Autodesk 2026 Autodesk 3ds Max Autodesk AI Autodesk AI Tools Autodesk Alias Autodesk AutoCAD Autodesk BIM Autodesk BIM 360 Autodesk Certification Autodesk Cloud Autodesk Construction Cloud Autodesk Docs Autodesk Dynamo Autodesk features Autodesk Forge Autodesk FormIt Autodesk Fusion 360 Autodesk InfraWorks Autodesk Inventor Autodesk Inventor Frame Generator Autodesk Inventor iLogic Autodesk License Autodesk Navisworks Autodesk news Autodesk plugins Autodesk Recap Autodesk Revit Autodesk Software Autodesk Takeoff Autodesk Tips Autodesk training Autodesk update Autodesk Upgrade Autodesk Vault Autodesk Video Autodesk Viewer Automation Automation Tutorial automotive design automotive visualization Backup Basic Commands Basics Beginner Beginner Tips Big Data BIM BIM 360 BIM collaboration BIM Coordination BIM Data BIM for Infrastructure BIM Implementation BIM software BIM technology BIM tools BIM Trends BIM workflow Block Editor Building Maintenance Building Systems ByLayer CAD CAD API CAD Automation CAD commands CAD comparison CAD Data Management CAD Design CAD Evolution CAD File Size Reduction CAD Integration CAD line thickness CAD management CAD modeling CAD Optimization CAD plugins CAD Productivity CAD Security CAD Skills CAD software CAD software 2026 CAD software training CAD technology CAD Tips CAD workflow car design software Case Study CGI design Civil 3D civil engineering Clash Detection Class-A surfacing clean CAD file cleaning command client engagement Cloud CAD Cloud Collaboration Cloud design platform Cloud Engineering Cloud Management Cloud Storage Cloud-First collaboration command abbreviations concept car conceptual workflow construction Construction Analytics Construction BIM Construction Cloud Construction Technology contractor tools Contractor Workflow Contraints cost estimation Create resizable blocks CTB STB Cybersecurity Data Backup data management Data Protection Data Reference Data Security Data Shortcut Design Automation Design Collaboration Design Comparison design review Design Rules design software design software tips Design Technology Design Tools Design Workflow design-to-construction Designer Tools Digital Construction Technology Digital Design Digital engineering digital fabrication digital marketing digital takeoff Digital Twin Digital Twins digital workflow Drafting Standards Drawing Automation Dref Dynamic Block Dynamic Block AutoCAD Dynamic Blocks Dynamic doors Dynamic windows Dynamo automation early stage design eco design editing commands energy efficiency Engineering Engineering Automation engineering data Engineering Design Engineering Innovation Engineering Productivity engineering software Engineering Technology engineering tools Engineering Tools 2025 Engineering Workflow Excel Express Tools External Reference Facility Management Fast Structural Design File Recovery Forge Development Forge Viewer FreeCAD Fusion 360 Fusion 360 tutorial Future Skills Generative Design Geospatial Data GIS Global design teams green building Grips Handoff heavy CAD file Heavy CAD Files HVAC Design Tools HVAC Engineering Hydraulic Modeling iLogic Industry 4.0 infrastructure design Infrastructure Monitoring Infrastructure Planning Infrastructure Technology InfraWorks Insight interactive presentation Interior Design Inventor Inventor API Inventor Drawing Template Inventor Frame Generator Inventor Graphics Issues Inventor IDW Inventor Tips Keyboard Shortcuts Large Projects Laser Scan Learn AutoCAD Licensing Linked Models Machine Learning in CAD maintenance command Management Manufacturing Innovation Mapping Technology marketing visuals Mechanical Design Mechanical Engineering MEP Modeling Mesh-to-BIM Metal Structure Model Optimization Multi-User Environment Navisworks Best Practices ObjectARX .NET API Open Source CAD Organization OVERKILL OVERKILL AutoCAD Palette parametric design parametric family PDF PDM system Plot Style AutoCAD point cloud Post Construction Practice Drawing preconstruction workflow Predictive Maintenance Printing Quality Product Design Product Development product lifecycle product rendering Productivity productivity tools professional printing Professional Tips project management PTC Creo PURGE PURGE AutoCAD realistic rendering ReCap reduce CAD file size rendering engine rendering software Renovation Project Resizable Block Revit Revit add-ins Revit automation Revit Best Practices Revit integration Revit MEP Revit Performance Revit Scripting Revit skills Revit Tips Revit tutorial Revit Workflow Ribbon Scale Autodesk screen Secure Collaboration shortcut keys Shortcuts Siemens NX Simulation Sketch Small Firms Smart Block Smart City Smart Factory Smart Infrastructur Software Compliance Software Management Software Update SolidWorks Steel Structure Design Structural Optimization sustainability sustainable design Sustainable Manufacturing team training guide Technical Drawing time saving tools toolbar Troubleshooting Tutorial urban planning User Interface (UI) Vault Best Practices Vault Lifecycle Vault Mistakes vehicle modeling virtual reality visualization workflow VR VRED Water Infrastructure Water Management What’s New in Autodesk Workbook workflow Workflow Automation Workflow Optimization Worksets Worksharing Workspace XLS Xref