Introduction
It was quite a long time when I was quite interested in issues with Revit API.
Today, I have an interesting discovery to share with you that I recently came across. It's a rather annoying glitch in the Revit API regarding hot reloading the Ribbon. This will be addressed through this article, providing you with an overview of how to hot reload a button or panel from the Ribbon within the Revit API.
Remove Panel
Removing a panel is relatively straightforward, as you can easily see in the example below, but it won't work as expected.
using AW = Autodesk.Windows;
using Autodesk.Revit.UI;
public static void RemovePanel(string tabName, AW.RibbonPanel panel)
{
AW.RibbonControl ribbon = AW.ComponentManager.Ribbon;
foreach (AW.RibbonTab tab in ribbon.Tabs)
{
if (tab.Name == tabName)
{
tab.Panels.Remove(panel);
}
}
}
Although the removal command is executed, you'll encounter an error when attempting to add a panel with a similar name back to the Ribbon. This is due to the fact that the Revit API has a Private Dictionary to store RibbonItemDictionary. What you need to do is add a cleanup step for RibbonItemDictionary
after removing the panel. Many thanks to @Nice3Point for this brilliant idea.
using AW = Autodesk.Windows;
using Autodesk.Revit.UI;
public static void RemovePanelClear(string tabName, string panelName)
{
AW.RibbonControl ribbon = AW.ComponentManager.Ribbon;
AW.RibbonPanel ribbonPanel = GetPanel(tabName, panelName);
//Remove panel
foreach (AW.RibbonTab tab in ribbon.Tabs)
{
if (tab.Name == tabName)
{
tab.Panels.Remove(ribbonPanel);
var uiApplicationType = typeof(UIApplication);
var ribbonItemsProperty = uiApplicationType.GetProperty("RibbonItemDictionary",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)!;
var ribbonItems =
(Dictionary<string, Dictionary<string, Autodesk.Revit.UI.RibbonPanel>>)ribbonItemsProperty
.GetValue(typeof(UIApplication));
if (ribbonItems.TryGetValue(tab.Id, out var tabItem)) tabItem.Remove(panelName);
}
}
}
Searching for a panel is also easily done within the Revit API:
using AW = Autodesk.Windows;
public static AW.RibbonPanel GetPanel(string tabName, string panelName)
{
AW.RibbonControl ribbon = AW.ComponentManager.Ribbon;
foreach (AW.RibbonTab tab in ribbon.Tabs)
{
if (tab.Name == tabName)
{
foreach (AW.RibbonPanel panel in tab.Panels)
{
if (panel.Source.Title == panelName)
{
return panel;
}
}
}
}
return null;
}
UIControlledApplication vs UIApplication
With the UIControlledApplication
class used in IExternalApplication
and UIApplication
used in IExternalCommand
, it's important to note that you should use UIApplication
to make changes in the Ribbon if you intend to trigger changes for a panel from a button. However, both can accomplish this task.
You can identify the Ribbon you are working with through UIApplication
.
var uiApplication = commandData.Application;
var TabName = "Test";
application.GetRibbonPanels(TabName);
Or through UIControlledApplication
via IExternalApplication
like code below:
public Result OnStartup(UIControlledApplication a)
{
var TabName = "Test";
application.GetRibbonPanels(TabName);
return Result.Succeeded;
}
To write a unified function between `UIApplication` and `UIControlledApplication`, you can use a common function through the `dynamic` approach supported in C# with the `Microsoft.CSharp` NuGet library:
```csharp
public static void Reload(dynamic application){
var TabName = "Test";
application.GetRibbonPanels(TabName);
}
Remove Button
And now, to be clearer, you can easily remove any unwanted button directly without needing to restart Revit.
using AW = Autodesk.Windows;
public void RemoveItem(string tabName, string panelName, string itemName)
{
AW.RibbonControl ribbon = AW.ComponentManager.Ribbon;
foreach (AW.RibbonTab tab in ribbon.Tabs)
{
if (tab.Name == tabName)
{
foreach (AW.RibbonPanel panel in tab.Panels)
{
if (panel.Source.Title == panelName)
{
AW.RibbonItem findItem = panel.FindItem("CustomCtrl_%CustomCtrl_%"
+ tabName + "%" + panelName + "%" + itemName,
true);
if (findItem != null)
{
panel.Source.Items.Remove(findItem);
}
}
}
}
}
}
public AW.RibbonTab GetTab(string tabName)
{
AW.RibbonControl ribbon = AW.ComponentManager.Ribbon;
foreach (AW.RibbonTab tab in ribbon.Tabs)
{
if (tab.Name == tabName)
{
return tab;
}
}
return null;
}
Conclusion
I hope that through this article, I have helped you address the issue of removing Ribbon Panels and Buttons within the Revit API. If you have any questions or contributions, please feel free to leave a comment below. Wishing you a wonderful day ahead!