.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to load a custom ribbon tab on startup?

17 REPLIES 17
SOLVED
Reply
Message 1 of 18
luckyboy82292
447 Views, 17 Replies

How to load a custom ribbon tab on startup?

Hey there!

I have a test code for adding a custom tab to ribbon and adding a panel. But it doesn't load at startup. And when I change the workspace, it disappears. If someone shed some light on this issue would be greatly appreciated. Thank you!

code:

using AutoCAD_Debugger;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Windows;
using System;
using AddRibbon = AddRibbonSpace.AddRibbon;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(AddRibbon))]

namespace AddRibbonSpace
{
    class AddRibbon : IExtensionApplication
    {
        void IExtensionApplication.Initialize()
        {
            Application.Idle += LoadRibbonMenuOnIdle;
        }
        void IExtensionApplication.Terminate() { }

        private void LoadRibbonMenuOnIdle(object sender, EventArgs e)
        {
            var ribbonControl = Autodesk.Windows.ComponentManager.Ribbon;
            if (ribbonControl != null)
            {
                createRibbon();

                Application.SystemVariableChanged += (o, arg) =>
                {
                    if (arg.Name.ToUpper() == "WSCURRENT")
                    {
                        createRibbon();
                    }
                };
                Application.Idle -= LoadRibbonMenuOnIdle;
            }
        }

        private const string MY_TAB_ID = "MY_TAB_ID";

        [CommandMethod("addMyRibbon")]
        public void createRibbon()
        {
            Autodesk.Windows.RibbonControl ribCntrl =
                      Autodesk.AutoCAD.Ribbon.RibbonServices.RibbonPaletteSet.RibbonControl;
            //can also be Autodesk.Windows.ComponentManager.Ribbon;     

            //add the tab 
            RibbonTab ribTab = new RibbonTab();
            ribTab.Title = "My custom tab";
            ribTab.Id = MY_TAB_ID;
            ribCntrl.Tabs.Add(ribTab);

            //create and add both panels
            addPanel1(ribTab);
            addPanel2(ribTab);

            //set as active tab 
            ribTab.IsActive = true;
        }

        private void addPanel2(RibbonTab ribTab)
        {
            //create the panel source 
            RibbonPanelSource ribPanelSource = new RibbonPanelSource();
            ribPanelSource.Title = "Edit Registry";

            //create the panel 
            RibbonPanel ribPanel = new RibbonPanel();
            ribPanel.Source = ribPanelSource;
            ribTab.Panels.Add(ribPanel);

            //create button1 
            RibbonButton ribButtonDrawCircle = new RibbonButton();
            ribButtonDrawCircle.Text = "Draw Circle";
            ribButtonDrawCircle.ShowText = true;

            //pay attention to the SPACE after the command name 
            ribButtonDrawCircle.CommandParameter = "DrawCircle ";
            ribButtonDrawCircle.CommandHandler = new AdskCommandHandler();

            ribPanelSource.Items.Add(ribButtonDrawCircle);

        }

        private void addPanel1(RibbonTab ribTab)
        {
            //throw new NotImplementedException();
        }

    }
}

namespace AutoCAD_Debugger
{
    class AdskCommandHandler : System.Windows.Input.ICommand
    {
        public bool CanExecute(object parameter)
        {
            return true;
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
            //is from Ribbon Button 
            RibbonButton ribBtn = parameter as RibbonButton;
            if (ribBtn != null)
            {
                //execute the command
                Autodesk.AutoCAD.ApplicationServices.Application
                  .DocumentManager.MdiActiveDocument
                  .SendStringToExecute((string)ribBtn.CommandParameter, true, false, true);
            }
        }
    }
}

 

17 REPLIES 17
Message 2 of 18
Domzinator
in reply to: luckyboy82292

you have to add some code to check for workspace changes, to readd your ribbon again, which i see you have done, im not sure if its 100% correct the way you have it

Here is an example of how i handle mine:

 

 

  public void Initialize()
  {
      acApp.Idle += OnApplicationIdle;
      acApp.SystemVariableChanged += OnSystemVariableChanged;
  }

  private static void OnSystemVariableChanged(object sender, SystemVariableChangedEventArgs e)
  {
      try
      {
          // Check if the changed system variable is WSCURRENT (workspace name)
          if (e.Name == "WSCURRENT")
          {
              string currentWorkspaceName = (string)acApp.GetSystemVariable("WSCURRENT");
              // Re-add the ribbon tab and panel when the workspace changes
              EnsureRibbonTabExists();
              EnsureRibbonPanelExists();
          }
      }
      catch (Exception ex)
      {
          Application.ShowAlertDialog($"Error Encountered during workspace change. {ex.Message}");
      }
  }

 

Civil 3D Certified Professional
Message 3 of 18


@Domzinator wrote:

you have to add some code to check for workspace changes, to readd your ribbon again, which i see you have done, im not sure if its 100% correct the way you have it


Monitoring changes to the WSCURRENT system variable doesn't cover all the bases, and isn't necessary if you handle the WorkspaceLoaded event, which fires not only when the current workspace changes, but also when the current workspace is reloaded (which also requires custom ribbon content to be added back to the ribbon).

 

Using the MENULOAD, CUILOAD/UNLOAD commands will wipe out the ribbon and reload its content without changing the current workspace, which doesn't change the WSCURRENT sysvar, but does cause the WorkspaceLoaded event to fire.

 

The other issue the OP's code doesn't address is the case where the user has the ribbon turned off when they start AutoCAD, and then at some point during the session, they issue the RIBBON command to show it. So, code that manages ribbon content has to deal with that possibility as well.

 

That's why I referred you to the code in my reply to the thread you previously posted. That code already accounts for all of the possible scenarios that one must deal with.

 

After your comment about the code's complexity, I refactored it to eliminate all of the dependencies it had on other code in that library, so that the RibbonEventManager class can be used without the need for other files. The class was overloaded with related functions that aren't always needed, and probably shouldn't have been part of it, so I moved that to another class.

 

The reason that you may have found that code confusing, could be because you haven't hit all of the bumps in the road which the code solves (as noted above), and since the larger problem is itself complicated, the solution ends up being complicated as well, at least until one has a good understanding of all the issues related to managing ribbon content.

 

The refactored code (that covers all the bases) can be found here. You can use it as-is, or simply look at it to see how it solves all of the problems, rather than only the ones you've found up to this point.

 

And, some example code can be found here

 

 

Message 4 of 18

See my reply to @Domzinator for a link to code that addresses all the issues relating to adding ribbon content and keeping it on the ribbon. The comments in the code lay out the problem in all its dimensions, and how the code deals with it.

 

You don't handle the SystemVariableChanged event and watch for WSCURRENT, because that misses reloading of the ribbon that's done by the CUILOAD/MENULOAD/UNLOAD commands. See my comments in the other reply about that. You should instead handle the WorkspaceLoaded event, which will fire when the current workspace changes, or when a partial menu file is loaded/unloaded, which requires you to add your content to the ribbon again.

 

Your code also recreates the ribbon content each time it must be added back to the ribbon. It should create the content once, and add that when needed instead of recreating it each time it needs to be added again.

Message 5 of 18

I have done some practice on this issue and found a good & working solution. But it doesn't handle the changes make in CUI file. And the tab just disappears after changes made in CUI files. But always handle the workspace changes. I hope it could help some beginners.

Code example:

public class RibbonLauncher : IExtensionApplication
{
    private const string MY_TAB_ID = "MyAdvancedPluginsTab";
    private bool wasActive = false;
    private string lastWorkSpace = string.Empty;

    public void Initialize()
    {
        Application.Idle += OnAutoCADInitialized;
        Application.SystemVariableChanged += OnSystemVariableChanged;
        Application.DocumentManager.DocumentActivated += OnDocumentActivated;
        Application.DocumentManager.DocumentCreated += OnDocumentCreated;
    }

    private void OnAutoCADInitialized(object sender, EventArgs e)
    {
        CreateRibbon();
        Application.Idle -= OnAutoCADInitialized;
    }

    private void OnSystemVariableChanged(object sender, SystemVariableChangedEventArgs e)
    {
        if (e.Name.ToUpper() == "WSCURRENT")
        {
            CreateRibbon();
            lastWorkSpace = (string)Application.GetSystemVariable("WSCURRENT");
        }
    }

    private void OnDocumentActivated(object sender, DocumentCollectionEventArgs e)
    {
        CreateRibbon();
    }

    private void OnDocumentCreated(object sender, DocumentCollectionEventArgs e)
    {
        CreateRibbon();
    }

    public void Terminate()
    {
        Application.SystemVariableChanged -= OnSystemVariableChanged;
        Application.DocumentManager.DocumentActivated -= OnDocumentActivated;
        Application.DocumentManager.DocumentCreated -= OnDocumentCreated;
    }

    private void CreateRibbon()
    {
        try
        {
            RibbonControl ribCntrl = Autodesk.AutoCAD.Ribbon.RibbonServices.RibbonPaletteSet.RibbonControl;
            if (ribCntrl == null) return;

            var existingTab = ribCntrl.Tabs.FirstOrDefault(t => t.Id == MY_TAB_ID);
            if (existingTab != null && existingTab.IsActive) return;

            if (existingTab != null)
            {
                ribCntrl.Tabs.Remove(existingTab);
            }

            RibbonTab ribTab = new RibbonTab { Title = "Advanced Tools", Id = MY_TAB_ID };
            ribCntrl.Tabs.Add(ribTab);


            AddSimplePanel(ribTab, "CAD Tools");

            if (wasActive)
                ribTab.IsActive = true;

            ribTab.Activated += RibTab_Activated;
            ribTab.Deactivated += RibTab_Deactivated;
        }
        catch (Exception ex)
        {
            Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage($"\nError creating ribbon tab: {ex.Message}");
        }
    }

    private void RibTab_Activated(object sender, EventArgs e)
    {
        if (sender is RibbonTab activatedTab && activatedTab.Id == MY_TAB_ID)
        {
            wasActive = true;
            Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\n'Advanced Tools' tab was activated.\n");
        }
    }

    private void RibTab_Deactivated(object sender, EventArgs e)
    {
        if (sender is RibbonTab deactivatedTab && deactivatedTab.Id == MY_TAB_ID)
        {
            string wsCurrent = (string)Application.GetSystemVariable("WSCURRENT");
            if (wsCurrent != lastWorkSpace)
            {
                Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage($"\n'WSCURRENT' changed: {wsCurrent}. Tab not deactivated.\n");
                return;
            }
            Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\n'Advanced Tools' tab was deactivated.\n");
            wasActive = false;
        }
    }

    private void AddSimplePanel(RibbonTab tab, string panelTitle)
    {
        RibbonRowBreak rbrk = new RibbonRowBreak();
        RibbonPanelSource panelSource = new RibbonPanelSource { Title = panelTitle };
        RibbonPanel ribPanel = new RibbonPanel { Source = panelSource };
        tab.Panels.Add(ribPanel);

        RibbonRowPanel rowPanel = new RibbonRowPanel();
        panelSource.Items.Add(rowPanel);

        var buttons = new[]
        {
            ("Command 1", "COMMAND1 "),
            ("Command 2", "COMMAND2 "),
            ("Command 3", "COMMAND3 ")
        };

        foreach (var (text, command) in buttons)
        {
            var button = new RibbonButton
            {
                Text = text,
                ShowText = true,
                Orientation = System.Windows.Controls.Orientation.Horizontal,
                Size = RibbonItemSize.Standard,
                CommandParameter = command,

            };
            button.CommandHandler = new RelayCommand();
            rowPanel.Items.Add(button);
            rowPanel.Items.Add(rbrk);
        }
    }

    private void AddComplexPanel(RibbonTab tab, string panelTitle)
    {
        RibbonPanelSource panelSource = new RibbonPanelSource { Title = panelTitle };
        RibbonPanel ribPanel = new RibbonPanel { Source = panelSource };
        tab.Panels.Add(ribPanel);

        RibbonRowPanel topRow = new RibbonRowPanel();
        panelSource.Items.Add(topRow);

        var largeButtons = new[]
        {
            ("Draw", "DRAW "),
            ("Modify", "MODIFY ")
        };

        foreach (var (text, command) in largeButtons)
        {
            RibbonButton button = new RibbonButton
            {
                Text = text,
                CommandParameter = command,
                ShowText = true,
                Size = RibbonItemSize.Large
            };
            button.CommandHandler = new RelayCommand();
            topRow.Items.Add(button);
        }

        RibbonRowPanel bottomRow = new RibbonRowPanel();
        panelSource.Items.Add(bottomRow);

        var smallButtons = new[]
        {
            ("Line", "LINE "),
            ("Circle", "CIRCLE "),
            ("Arc", "ARC ")
        };

        foreach (var (text, command) in smallButtons)
        {
            RibbonButton button = new RibbonButton
            {
                Text = text,
                CommandParameter = command,
                ShowText = true,
                Size = RibbonItemSize.Standard
            };
            button.CommandHandler = new RelayCommand();
            bottomRow.Items.Add(button);
        }
    }

    private void AddSplitPanel(RibbonTab tab, string panelTitle)
    {
        RibbonPanelSource panelSource = new RibbonPanelSource { Title = panelTitle };
        RibbonPanel ribPanel = new RibbonPanel { Source = panelSource };
        tab.Panels.Add(ribPanel);

        RibbonSplitButton splitButton = new RibbonSplitButton
        {
            Text = "Options",
            ShowText = true,
            Size = RibbonItemSize.Large,
        };

        var splitItems = new[]
        {
            ("Option 1", "OPTION1 "),
            ("Option 2", "OPTION2 "),
            ("Option 3", "OPTION3 ")
        };

        foreach (var (text, command) in splitItems)
        {
            RibbonButton button = new RibbonButton
            {
                Text = text,
                CommandParameter = command,
                ShowText = true
            };
            button.CommandHandler = new RelayCommand();
            splitButton.Items.Add(button);
        }

        RibbonRowPanel rowPanel = new RibbonRowPanel();
        rowPanel.Items.Add(splitButton);
        panelSource.Items.Add(rowPanel);
    }

    private void AddStackedPanel(RibbonTab tab, string panelTitle)
    {
        RibbonPanelSource panelSource = new RibbonPanelSource { Title = panelTitle };
        RibbonPanel ribPanel = new RibbonPanel { Source = panelSource };
        tab.Panels.Add(ribPanel);

        RibbonRowPanel rowPanel = new RibbonRowPanel();
        panelSource.Items.Add(rowPanel);

        // Add buttons vertically
        var stackedButtons = new[]
        {
            ("Settings 1", "SETTINGS1 "),
            ("Settings 2", "SETTINGS2 "),
            ("Settings 3", "SETTINGS3 ")
        };

        foreach (var (text, command) in stackedButtons)
        {
            RibbonButton button = new RibbonButton
            {
                Text = text,
                CommandParameter = command,
                ShowText = true,
                Size = RibbonItemSize.Standard,
                Orientation = System.Windows.Controls.Orientation.Vertical
            };
            button.CommandHandler = new RelayCommand();
            rowPanel.Items.Add(button);
        }
    }

    public class RelayCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter) => true;

        public void Execute(object parameter)
        {
            if (parameter is RibbonButton ribBtn)
            {
                string commandToExecute = (string)ribBtn.CommandParameter;
                try
                {
                    Document doc = Application.DocumentManager.MdiActiveDocument;
                    if (doc != null)
                    {
                        doc.SendStringToExecute(commandToExecute, true, false, true);
                    }
                    else
                    {
                        Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage("\nNo active document.");
                    }
                }
                catch (Exception ex)
                {
                    Application.DocumentManager.MdiActiveDocument?.Editor.WriteMessage($"\nError executing '{commandToExecute}': {ex.Message}");
                }
            }
        }
    }
}

 

Message 6 of 18

The code you posted does not solve all of the problems that exist in keeping ribbon content on the ribbon. You can read my previous comments in this thread regarding how to do that and you can refer to the code I link to which shows how it can be done. Also, I do not understand what purpose there is to handling document activation events at all especially considering how much work your code is doing each time a document is activated. It is throwing away the existing ribbon content and recreating it. Unless you are trying to provide different ribbon content for each document I don't see any point to that. 

Message 7 of 18

Hi sir,

I just moved to another method to add ribbons, panels, rows, and buttons. I have done some code but what I need now is how to handle the user's modifications. I mean I don't want it to be changed by CUI editor. If edited then it will run and will be restored. And now it is unloading and loading the code multiple times at once. Means there are many events which is doing it. Any help on this would be greatly appreciated. Thank you!

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Customization;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application;
using Exception = Autodesk.AutoCAD.Runtime.Exception;

public class AddRibbon : IExtensionApplication
{
    #region Global Variables
#pragma warning disable IDE1006
    private static Editor ed { get; set; }
    public bool isCuiLoaded = false;
    private FileSystemWatcher _cuiFileWatcher;
#pragma warning restore IDE1006
    #endregion

    public void Initialize()
    {
        Application.Idle += OnIdle;
        Application.DocumentManager.DocumentActivated += OnDocActivated;

        // Setup CUI file watcher
        var cuiFileName = "CADTools.cuix";
        var cuiPath = Path.GetDirectoryName(Application.GetSystemVariable("MENUNAME") + ".cuix");
        var cuiFilePath = Path.Combine(cuiPath, cuiFileName);

        StartCuiFileWatcher(cuiFilePath);
    }

    private void OnIdle(object sender, EventArgs e)
    {
        CheckAndCreateCuiFile();
        Application.Idle -= OnIdle;
    }

    private void OnDocActivated(object sender, DocumentCollectionEventArgs e)
    {
        if (Application.DocumentManager.Count != 0)
        {
            Application.DocumentManager.DocumentActivated -= OnDocActivated;
            SafeExecuteWithSystemVariables(() =>
            {
                LoadCui();
            });
        }
    }

    public void Terminate()
    {
        Application.Idle -= OnIdle;
        Application.DocumentManager.DocumentActivated -= OnDocActivated;
        StopCuiFileWatcher();
    }

    private void StartCuiFileWatcher(string cuiFilePath)
    {
        _cuiFileWatcher?.Dispose();

        var cuiDirectory = Path.GetDirectoryName(cuiFilePath);
        var cuiFileName = Path.GetFileName(cuiFilePath);

        _cuiFileWatcher = new FileSystemWatcher
        {
            Path = cuiDirectory,
            Filter = cuiFileName,
            NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
            EnableRaisingEvents = true
        };

        _cuiFileWatcher.Changed += OnCuiFileChanged;
    }

    private void StopCuiFileWatcher()
    {
        if (_cuiFileWatcher != null)
        {
            _cuiFileWatcher.Changed -= OnCuiFileChanged;
            _cuiFileWatcher.Dispose();
            _cuiFileWatcher = null;
        }
    }

    private void OnCuiFileChanged(object sender, FileSystemEventArgs e)
    {
        try
        {
            SafeExecuteWithSystemVariables(() =>
            {
                ReloadCui(e.FullPath);
            });
            ed?.WriteMessage($"\nCUI file '{e.FullPath}' was modified and reloaded.");
        }
        catch (Exception ex)
        {
            ed?.WriteMessage($"\nError reloading CUI file: {ex.Message}");
        }
    }

    private static void ReloadCui(string cui)
    {
        var cuiName = Path.GetFileNameWithoutExtension(cui);
        ed?.Document.SendStringToExecute("_.CUIUNLOAD " + cuiName + " ", false, false, false);
        ed?.Document.SendStringToExecute("_.CUILOAD \"" + cui + "\" ", false, false, false);
    }

    [CommandMethod("AddRibbon")]
    public void Loadcuicommand()
    {
        SafeExecuteWithSystemVariables(() =>
        {
            LoadCui();
        });
    }

    private void LoadCui()
    {
        ed = Application.DocumentManager.MdiActiveDocument?.Editor;
        var myCuiFileName = "CADTools.cuix";
        var cuiPath = Path.GetDirectoryName(Application.GetSystemVariable("MENUNAME") + ".cuix");
        var myCuiFilePath = Path.Combine(cuiPath, myCuiFileName);
        var wsCurrent = (string)Application.GetSystemVariable("WSCURRENT");

        var mainCui = (string)Application.GetSystemVariable("MENUNAME") + ".cuix";
        var mainCs = new CustomizationSection(mainCui);
        var pcfc = mainCs.PartialCuiFiles;

        CheckAndCreateCuiFile();

        var myCs = new CustomizationSection(myCuiFilePath);

        // Add ribbon tabs
        var ribTabCol = new[]
        {
            AddOrGetRibbonTab(myCs, "CAD Tools"),
        };
        var validTabs = ribTabCol.Select(tab => tab.Name).ToArray();
        RemoveUnusedTabs(myCs, validTabs);

        // Add tab panels
        var tabName = "CADTools";
        var cadToolsPanels = new[]
        {
            (tabName, "CAD Tools"),
        };
        RemoveUnusedPanels(myCs, tabName, cadToolsPanels);
        AddOrGetRibbonPanelsToTabs(myCs, cadToolsPanels, ribTabCol);

        AddButtons.AddSimpleButton(myCs, "CADTools", ed);

        myCs.Save();
        ed?.WriteMessage("\nRibbon setup completed.");
    }

    private void CheckAndCreateCuiFile()
    {
        var myCuiFileName = "CADTools.cuix";
        var cuiPath = Path.GetDirectoryName(Application.GetSystemVariable("MENUNAME") + ".cuix");
        var myCuiFilePath = Path.Combine(cuiPath, myCuiFileName);

        if (!File.Exists(myCuiFilePath))
        {
            var pcs = new CustomizationSection();
            pcs.SaveAs(myCuiFilePath);
        }
    }

    private static RibbonTabSource AddOrGetRibbonTab(CustomizationSection cs, string tabText)
    {
        var ribRoot = cs.MenuGroup.RibbonRoot;
        var tabName = tabText.Replace(" ", "");
        var tabId = $"{tabName}_TabId";
        var tab = ribRoot.FindTab(tabId);
        if (tab == null)
        {
            tab = new RibbonTabSource(ribRoot)
            {
                Name = tabName,
                Text = tabText,
                ElementID = tabId,
                Id = tabId
            };
            ribRoot.RibbonTabSources.Add(tab);
        }
        return tab;
    }

    private static void RemoveUnusedTabs(CustomizationSection cs, string[] validTabs)
    {
        var ribRoot = cs.MenuGroup.RibbonRoot;

        // Get all tabs in RibbonTabSources
        var tabsToDelete = new List<RibbonTabSource>();

        foreach (RibbonTabSource tab in ribRoot.RibbonTabSources)
        {
            // Check if the tab is not in the valid list
            if (!validTabs.Contains(tab.Name))
            {
                tabsToDelete.Add(tab);
            }
        }

        // Remove the tabs that are not in the valid list
        foreach (var tab in tabsToDelete)
        {
            ribRoot.RibbonTabSources.Remove(tab);
        }
    }

    private static void AddOrGetRibbonPanelsToTabs(CustomizationSection cs, (string tabName, string panelText)[] panels, RibbonTabSource[] ribTabCol)
    {
        var ribRoot = cs.MenuGroup.RibbonRoot;

        foreach (var (tabName, panelText) in panels)
        {
            // Find the tab that matches the tabName
            RibbonTabSource tab = ribTabCol.FirstOrDefault(t => t.Name == tabName);

            if (tab != null)
            {
                var panelName = panelText.Replace(" ", "");
                var panelId = $"{panelName}_PanelId";

                // Check if the panel already exists
                RibbonPanelSource existingPanel = null;
                foreach (RibbonPanelSource panel in ribRoot.RibbonPanelSources)
                {
                    if (panel.ElementID == panelId || panel.Name == panelName)
                    {
                        existingPanel = panel;
                        break;
                    }
                }

                if (existingPanel != null)
                {
                    // Check if the panel is already referenced in the tab
                    bool isReferenced = false;
                    foreach (var item in tab.Items)
                    {
                        if (item is RibbonPanelSourceReference refItem && refItem.PanelId == existingPanel.ElementID)
                        {
                            isReferenced = true;
                            break;
                        }
                    }

                    if (!isReferenced)
                    {
                        // Add the panel reference to the tab
                        var panelRef = new RibbonPanelSourceReference(tab)
                        {
                            PanelId = existingPanel.ElementID
                        };
                        tab.Items.Add(panelRef);
                    }

                    ed?.WriteMessage($"Panel '{panelText}' already exists in tab '{tabName}'.");
                }
                else
                {
                    // Create the new panel if not found
                    var newPanel = new RibbonPanelSource(ribRoot)
                    {
                        Name = panelName,
                        Text = panelText,
                        ElementID = panelId,
                        Id = panelId
                    };
                    ribRoot.RibbonPanelSources.Add(newPanel);

                    // Add the panel reference to the tab
                    var newPanelRef = new RibbonPanelSourceReference(tab)
                    {
                        PanelId = newPanel.ElementID
                    };
                    tab.Items.Add(newPanelRef);

                    ed?.WriteMessage($"Created new panel: {panelText} in tab '{tabName}'");
                }
            }
            else
            {
                ed?.WriteMessage($"Tab '{tabName}' not found, skipping panel '{panelText}'.");
            }
        }
    }

    private static void RemoveUnusedPanels(CustomizationSection cs, string tabName, (string tabName, string panelText)[] validPanels)
    {
        var ribRoot = cs.MenuGroup.RibbonRoot;

        // Create a dictionary for easy lookup of valid panel names by tab name
        var validPanelsDict = validPanels
            .GroupBy(panel => panel.tabName)
            .ToDictionary(g => g.Key, g => new HashSet<string>(g.Select(p => p.panelText.Replace(" ", "")), StringComparer.OrdinalIgnoreCase));

        // Find the RibbonTabSource based on the specified tabName
        var formattedTabName = tabName.Replace(" ", "");
        RibbonTabSource tab = null;
        foreach (RibbonTabSource t in ribRoot.RibbonTabSources)
        {
            if (string.Equals(t.Name, formattedTabName, StringComparison.OrdinalIgnoreCase))
            {
                tab = t;
                break;
            }
        }

        if (tab == null)
        {
            ed?.WriteMessage($"\nTab '{tabName}' not found.");
            return;
        }

        // Check if the tab has valid panels defined
        if (!validPanelsDict.ContainsKey(tab.Name))
        {
            ed?.WriteMessage($"\nNo valid panels specified for tab '{tabName}'.");
            return;
        }

        // Get the list of valid panel IDs for the tab
        var validPanelIds = new HashSet<string>(
            validPanelsDict[tab.Name].Select(panelText => $"{panelText.Replace(" ", "")}_PanelId"),
            StringComparer.OrdinalIgnoreCase
        );

        // Collect unused panels
        var panelsToDelete = new List<RibbonPanelSource>();
        var panelReferencesToDelete = new List<RibbonPanelSourceReference>();

        // Remove panel references from the tab
        foreach (var item in tab.Items.OfType<RibbonPanelSourceReference>())
        {
            if (!validPanelIds.Contains(item.PanelId))
            {
                panelReferencesToDelete.Add(item);

                // Also find the panel source for removal
                var panelSource = ribRoot.FindPanel(item.PanelId);
                if (panelSource != null)
                {
                    panelsToDelete.Add(panelSource);
                }
            }
        }

        foreach (var panelRef in panelReferencesToDelete)
        {
            tab.Items.Remove(panelRef);
            ed?.WriteMessage($"\nRemoved unused panel reference '{panelRef.PanelId}' from tab '{tabName}'.");
        }

        ed?.WriteMessage($"\nCleanup of unused panels in tab '{tabName}' completed.");
    }

    private void SafeExecuteWithSystemVariables(Action action)
    {
        object cmdEcho = Application.GetSystemVariable("CMDECHO");
        object fileDia = Application.GetSystemVariable("FILEDIA");

        try
        {
            Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.filedia " + 0 + " ", false, false, false);
            Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.cmdecho " + 0 + " ", false, false, false);

            action.Invoke();
        }
        catch (Exception ex)
        {
            ed?.WriteMessage($"\nError: {ex.Message}\n");
        }
        finally
        {
            Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.filedia " + fileDia + " ", false, false, false);
            Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.cmdecho " + cmdEcho + " ", false, false, false);
        }
    }
}

class AddButtons
{
    public static void AddSimpleButton(CustomizationSection cs, string panelName, Editor ed)
    {
        try
        {
            if (cs == null) return;

            RibbonPanelSource panelsrc=GetRibbonPanel(cs, panelName);
            if (panelsrc== null) return;

            MacroGroup macGroup;
            if (cs.MenuGroup.MacroGroups.Count == 0)
            {
                macGroup = new MacroGroup(cs.MenuGroupName, cs.MenuGroup);
            }
            else
            {
                macGroup = cs.MenuGroup.MacroGroups[0];
            }

            panelSrc.Items.Clear();
            RibbonRow ribbonRow1 = new RibbonRow(panelSrc);
            if (panelSrc.Items.Count == 0)
            {
                panelSrc.Items.Add(ribbonRow1);
            }

            RibbonCommandButton button1 = new RibbonCommandButton(ribbonRow1)
            {
                Text = "Extract\nCross Sections"
            };
            MenuMacro menuMac1 = macGroup.CreateMenuMacro("Button1", "^C^COO", "Button1_Tag", "Button1_Help",
                MacroType.Any, "RCDATA_16_EXPORT_FILE", "RCDATA_16_EXPORT_FILE", "Button1_Label_Id");
            button1.MacroID = menuMac1.ElementID;
            button1.ButtonStyle = RibbonButtonStyle.LargeWithText;
            button1.KeyTip = "Button1 Key Tip";
            button1.TooltipTitle = "Button1 Tooltip Title!";
            ribbonRow1.Items.Add(button1);

            RibbonRowPanel subPanel1 = new RibbonRowPanel(ribbonRow1);
            ribbonRow1.Items.Add(subPanel1);

            RibbonRow ribbonRow2 = new RibbonRow(subPanel1);
            subPanel1.Items.Add(ribbonRow2);

            RibbonCommandButton button2 = new RibbonCommandButton(ribbonRow2)
            {
                Text = "Export XS into PDF Files"
            };
            MenuMacro menuMac2 = macGroup.CreateMenuMacro("Button2", "^C^CPTP", "Button2_Tag", "Button2_Help",
                MacroType.Any, "RCDATA_16_EXPORT_FILE", "RCDATA_16_EXPORT_FILE", "Button2_Label_Id");
            button2.MacroID = menuMac2.ElementID;
            button2.ButtonStyle = RibbonButtonStyle.SmallWithText;
            button2.KeyTip = "Button2 Key Tip";
            button2.TooltipTitle = "Button2 Tooltip Title!";
            ribbonRow2.Items.Add(button2);

            RibbonRow ribbonRow3 = new RibbonRow(subPanel1);
            subPanel1.Items.Add(ribbonRow3);

            RibbonCommandButton button3 = new RibbonCommandButton(ribbonRow3)
            {
                Text = "Export XS Data into CSV"
            };
            MenuMacro menuMac3 = macGroup.CreateMenuMacro("Button3", "^C^CFF", "Button3_Tag", "Button3_Help",
                MacroType.Any, "RCDATA_16_EXPORT_FILE", "RCDATA_16_EXPORT_FILE", "Button3_Label_Id");
            button3.MacroID = menuMac3.ElementID;
            button3.ButtonStyle = RibbonButtonStyle.SmallWithText;
            button3.KeyTip = "Button3 Key Tip";
            button3.TooltipTitle = "Button3 Tooltip Title!";
            ribbonRow3.Items.Add(button3);


            cs.Save();
        }
        catch (Exception ex)
        {
            ed.WriteMessage($"\n{ex.Message}\n{ex.StackTrace}");
        }
    }

    private static RibbonPanelSource GetRibbonPanel(CustomizationSection cs, string panelName)
    {
        foreach (RibbonPanelSource panel in cs.MenuGroup.RibbonRoot.RibbonPanelSources)
        {
            if (panel.Name == panelName)
            {
                return panel;
            }
        }
        return null;
    }
}
Message 8 of 18
norman.yuan
in reply to: luckyboy82292

You did not explain why: the code you just posted (message 7) uses the API from AcCui.dll (Autodesk.AutoCAD.Customization namespace), while the code in your original post uses  AdWindows.dll (Autodesk.Windows namespace). The former manipulate ribbon items created by CUIX file, which can also be modified by user via command "CUI/CUIX", meaning whatever your code create in the ribbon, can be changed by user; the latter, though, create ribbon items that user cannot change, as long as you make sure these custom ribbon items are loaded into the ribbon control PROPERLY.

 

If your purpose is to create ribbon items that should not be changed by user, you would go back to your original code (using AdWindows.dll),  

 

Norman Yuan

Drive CAD With Code

EESignature

Message 9 of 18

Please explain, what is the purpose of this:

 

private void SafeExecuteWithSystemVariables(Action action)
{
   object cmdEcho = Application.GetSystemVariable("CMDECHO");
   object fileDia = Application.GetSystemVariable("FILEDIA");

   try
   {
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.filedia " + 0 + " ", false, false, false);
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.cmdecho " + 0 + " ", false, false, false);

      action.Invoke();
   }
   catch (Exception ex)
   {
      ed?.WriteMessage($"\nError: {ex.Message}\n");
   }
   finally
   {
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.filedia " + fileDia + " ", false, false, false);
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.cmdecho " + cmdEcho + " ", false, false, false);
   }
}

 

 

Why are you using SendStringToExecute() to set system variables? And, do you understand that SendStringToExecute() does not execute the command until after your managed code ends and returns control back to AutoCAD (which is long after your action has executed)?

 

Application.SetSystemVariable() is the API to set the values of system variables.

 

As far as the mess you've created with the balance of that code, I can't help with that, because it looks to be a completely unworkable solution that creates more problems than it solves.

Message 10 of 18

I really appreciate your thoughts. But my code is working (I forgot to mention it). It's adding the ribbon tab and panel. Adding some buttons etc. And I just move to this method because it is more precise than my first method. With this method, I can control size of buttons, can assign icon to a button easily. I mean, I am happy with this method. But as I said, I have some issues with events which are working not as expected. 

Message 11 of 18

Sir I have tried it and it's working as expected for me. If there is any other alternative method, so please share with me. Thank you!

private void SafeExecuteWithSystemVariables(Action action)
{
   object cmdEcho = Application.GetSystemVariable("CMDECHO");
   object fileDia = Application.GetSystemVariable("FILEDIA");

   try
   {
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.filedia " + 0 + " ", false, false, false);
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.cmdecho " + 0 + " ", false, false, false);

      action.Invoke();
   }
   catch (Exception ex)
   {
      ed?.WriteMessage($"\nError: {ex.Message}\n");
   }
   finally
   {
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.filedia " + fileDia + " ", false, false, false);
      Application.DocumentManager.MdiActiveDocument.SendStringToExecute("_.cmdecho " + cmdEcho + " ", false, false, false);
   }
}
Message 12 of 18

Here are the Screenshots of what this code does.

 

Screenshot 2025-01-03 002358.pngScreenshot 2025-01-03 002533.png

 

 

Message 13 of 18

You are mistaken. It is not working as expected. The commands that you are sending do not execute until after your code has returned.

 

Use Application.SetSystemVariable() to set the values of system variables. 

Message 14 of 18

I am not expert in this field but as I said it's working fine for me. If it doesn't work as expected, I'll find you here again. Thank you!
Message 15 of 18


@luckyboy82292 wrote:
I am not expert in this field but as I said it's working fine for me. If it doesn't work as expected, I'll find you here again. Thank you!

@luckyboy82292 

Leave your ego at the door.

What you think "is working fine"  does nor take into consideration is that Commands executed with SendStringToExecute are asynchronous and are not invoked until the .NET command has ended.

link added:

https://help.autodesk.com/view/OARX/2025/ENU/?guid=GUID-F4A36181-39FB-4923-A2AF-3333945DB289

 

I'd suggest if someone advises you of errors in your code logic you should listen to them.

. . . or they won't be bothered responding when you come back to  "find them" for assistance.

 

Regards,


// Called Kerry in my other life.

Everything will work just as you expect it to, unless your expectations are incorrect.
Sometimes the question is more important than the answer.

class keyThumper<T> : Lazy<T>;      another  Swamper

Message 16 of 18

I will refrain from responding to your posts from now on because you seem to think you know better. 

Message 17 of 18

Oh! Sorry for that if I say something wrong. I don't think I know better but this method is working. So I was just telling that. And please give me a better alternative. Thank you! :folded_hands:

Message 18 of 18

Thank you bro! I don't have ego. It was just a discussion to review something. And get a better solution.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

AutoCAD Inside the Factory


Autodesk Design & Make Report