Context Menu - Experiments and first Observations

Context Menu - Experiments and first Observations

Julian.Wandzilak
Advocate Advocate
1,351 Views
16 Replies
Message 1 of 17

Context Menu - Experiments and first Observations

Julian.Wandzilak
Advocate
Advocate

I've been recently playing a lot with context menus and managed to create some interesting problems.

Here they are:

  1. Command started from context menu doesn’t save itself in the list of recently used commands:
    1. It won't be shown on the list of recent commands.
    2. You won’t be able to repeat them with “repeat the last command”. Revit will run the previous command instead.JulianWandzilak_0-1741005716305.png

       


      It is super annoying especially that we want to be able to use these commands often and start them quickly (which is the main reason to use context menu vs normal buttons)  

  2. There is a maximum number of register commands. After hitting some magical number (I didn’t count them, but the number is probably somewhere close to 150), Revit will allow you to create more of them but will block us from using them. They will be greyed out as in the picture below - I learn about it by a fluke after spending an hour trying to find out why commands for one of my tools are grayed out. 
    One thing to keep in mind is that you share this number between all loaded add-ins.
    JulianWandzilak_1-1741005716308.png

     

 

blog: w7k.pl more about me: Linkedin Profile
My add-ins for Revit: Drafter(180+ scripts) & Leveler
0 Likes
1,352 Views
16 Replies
Replies (16)
Message 2 of 17

jeremy_tammik
Alumni
Alumni

Oh dear. That sounds a bit irritating. Thank you very much for documenting and sharing this! I guess I can understand (or imagine, at least) the Revit developers' point of view. Problem is, whenever you provide a customisation possibility, somebody will find a completely unexpected use case for it which hits limits that you never imagined would be reached or relevant. Anyway, I shared your findings with them. Let's wait and see what they say. 

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 17

Julian.Wandzilak
Advocate
Advocate

Learning by breaking... My favourite method. And now I have to figure out a way of making some settings for my users to personalize their choice of items in context menu. More learning 🙂   

To be fair, I have 240+ commands across all my tools now, so I am probably the only one who was able to hit this problem. The context menus are glorious and such a big present from devs so naturally it would be great to be somehow responsible for making them even better.

blog: w7k.pl more about me: Linkedin Profile
My add-ins for Revit: Drafter(180+ scripts) & Leveler
0 Likes
Message 4 of 17

jeremy_tammik
Alumni
Alumni

Initial discussion of this: 

   

1. Sounds like a it should be filed if there's a reproducible case.
2. 150 is a lot of content for the context menu. Please remember that it is a shared space and Revit design does not put everything you can do in the entire Ribbon/other UI into the context menu, only the commands we believe will be the most needed. That being said, I wonder if we can clarify this limitation and perhaps fail better than showing a non-usable button.

   

Not adding context menu commands to the list of recent commands is the same for internal commands. Maybe, to avoid commands that may depend on selection and therefore may be not repeatable. This doesn't work perfectly though... There's an additional complication for external context menu commands because we clear the commands before building a new menu

   

External context menu commands are assigned temporary ids.

   

There's a debug warning if too many commands are created. That needs to surface to the plugin developer.

     

BTW, our current estimate is that no more than 200 external context menu commands are possible, it seems.

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 5 of 17

Julian.Wandzilak
Advocate
Advocate

Thanks Jeremy,

 

1. Happy to provide my plugins at some point - just need some time to finish Drafter for revit 2026.

 

2. It is rather enough. As mentioned before, I'm going to code myself some "settings". It will allow user to choose which ones they are using. Still, right now it fails without style and creates confusion 😉 No way to check if it is going to happen, so it is hard to even warn a user about it.

 

3. Don't really see a good reason to keep it this way. Below the print screen with my external Command (part of my free plugin Extra). It registers nicely in the last commands and at the same time you can run it via last used command. First things which it does is to prompt user to select something (or it checks currently selected elements if there are some)

 



JulianWandzilak_0-1741554101468.jpeg

link to the animation presenting it:

Link to my web page with a short gif animation  

 

blog: w7k.pl more about me: Linkedin Profile
My add-ins for Revit: Drafter(180+ scripts) & Leveler
0 Likes
Message 6 of 17

Sean_Page
Collaborator
Collaborator

Not entirely sure, but I know there are some oddities with commands (Shortcut keys / Visibility) if the commands are not loaded on the Ribbon.

 

I'd also say that the availability component of the Context menu could probably be helpful here as I can't imagine ALL of those are useful on / in every view.

menuItem.SetAvailabilityClassName(typeof(TAvailability).FullName)
Sean Page, AIA, NCARB, LEED AP
Senionr Partner, Computational Designer, Architect
Message 7 of 17

Julian.Wandzilak
Advocate
Advocate

Thanks @Sean_Page for this suggestion, I managed to implement it.

 

A quick question, right now it grays out the command - Have you found out a way of hiding it completely? Also, in case of having all the options grayed out, we should be able to hide SubMenuItems, but I haven't seen anything allowing it.

blog: w7k.pl more about me: Linkedin Profile
My add-ins for Revit: Drafter(180+ scripts) & Leveler
Message 8 of 17

Sean_Page
Collaborator
Collaborator

Since the menus are initiated on Context (click) then you have to provide logic with each call to show (add) calls to the menu.

 

Example here is a method that check current selection and if they are of a certain category the menu option is added.

if(CheckReferences(BuiltInCategory.OST_Rooms) || CheckReferences(BuiltInCategory.OST_Areas))
                    {
                        menu.AddSubMenu("Spaces", SpacesSubMenu);
                        menu.AddSeparator();
                    }

 

Sean Page, AIA, NCARB, LEED AP
Senionr Partner, Computational Designer, Architect
Message 9 of 17

GaryOrrMBI
Advisor
Advisor

@Sean_Page wrote:

Since the menus are initiated on Context (click) then you have to provide logic with each call to show (add) calls to the menu.

 

Example here is a method that check current selection and if they are of a certain category the menu option is added.

if(CheckReferences(BuiltInCategory.OST_Rooms) || CheckReferences(BuiltInCategory.OST_Areas))
                    {
                        menu.AddSubMenu("Spaces", SpacesSubMenu);
                        menu.AddSeparator();
                    }

 


@Sean_Page, I know that this is over a year old but I'm just now trying to create a dynamic menu builder for the first time and this is one of the very few posts that I can find that talk about this subject, and you seem to have a handle on it.

 

My question to you, if you choose to accept it 😉 , is this: How are you passing the current selection set (or otherwise acquiring it) into your CheckReferences() function? Since the IContextMenuCreator Class implementation, nor the BuildContextMenu function within it contain the CommandData that I'm usually working with in an IExternalCommand Class, I'm at a complete loss as to how to acquire the selection set from within my menu creation class.

 

My apologies if this seems like a dumb question but I am not a professional developer by any means, I just try to code a few of the pieces that are missing for myself and my co-workers and I can't seem to wrap my head around this.

 

-Gary

Gary J. Orr
GaryOrrMBI (MBI Companies 2014-Current)
aka (past user names):
Gary_J_Orr (GOMO Stuff 2008-2014);
OrrG (Forum Studio 2005-2008);
Gary J. Orr (LHB Inc 2002-2005);
Orr, Gary J. (Gossen Livingston 1997-2002)
Message 10 of 17

Sean_Page
Collaborator
Collaborator

@GaryOrrMBI, probably a simpler solution than you expect, but I am just using a global reference to the UIApplication that I store.

 

From there, it just like any other selection check:

//UIApp here is my global property that is set early during app start
UIApp.ActiveUIDocument.Selection.GetReferences()
Sean Page, AIA, NCARB, LEED AP
Senionr Partner, Computational Designer, Architect
Message 11 of 17

GaryOrrMBI
Advisor
Advisor

@Sean_Page,

Thank you for the timely response.

You may be giving me too much credit. I may be over my head here.

 

in my IExternalApplication implementation I have my OnStartup function. That function has access to the UIControlledApplication Object.

In the OnStartUp function I build my Menu Panel and all the different buttons and such. This is also where I need to add the RegisterContextMenu call (I believe).

 

That calls my IContextMenuCreator Class. In the BuildContextMenu function within that Class is where I'm trying to add my conditional check to decide if a given menu item will be added. As of this point the only access that I have to Revit is the afore mentioned UIControlledApplication object which does not have access to the UIApplication object.

 

I'm lost as to how to gain access to the UIApplication object and all of the downstream objects.

 

This stuff was much more straight forward in AutoCAD...

 

I may just have to stick with creating another button or two 🙂

 

Thanks again for your assistance.

-G

Gary J. Orr
GaryOrrMBI (MBI Companies 2014-Current)
aka (past user names):
Gary_J_Orr (GOMO Stuff 2008-2014);
OrrG (Forum Studio 2005-2008);
Gary J. Orr (LHB Inc 2002-2005);
Orr, Gary J. (Gossen Livingston 1997-2002)
0 Likes
Message 12 of 17

Sean_Page
Collaborator
Collaborator

Ok, here is a rundown of how I do it:

OnStartup:

application.Idling += new EventHandler<IdlingEventArgs>(Application_Idling);
RegisterContextMenu(application)

 

Register the Context Menu, also in the App or same class as OnStartup:

private static void RegisterContextMenu(UIControlledApplication application)
{
    application.RegisterContextMenu("RDG", new CreateContextMenu());
}

 

Use an Application Idling event to get the UIApplication:

private void Application_Idling(object sender, IdlingEventArgs args)
{
    uiApp = sender as UIApplication;
    Global.UIApp = uiApp; //This is how you can store this to a app property
    AppParams.application.Idling -= Application_Idling;
}

 

Sean Page, AIA, NCARB, LEED AP
Senionr Partner, Computational Designer, Architect
Message 13 of 17

GaryOrrMBI
Advisor
Advisor

@Sean_Page,

I have been resisting event handlers (shadowy concerns over overhead and possibly setting myself up for unexpected continuous loop scenarios if not done correctly) so I hadn't looked into them too deeply, but I was just digging into that very thing, both as a possibility to solve my current dilemma as well as considering some updaters for some of my background data driven functions, when the notification arrived with your response.

 

I had simply never imagined an event handler that removed itself as soon as it got done setting up the app wide variable.

 

Thank you for that pointer and clear process example. This gets me one step closer to understanding what I'm doing 🙂

 

-G

Gary J. Orr
GaryOrrMBI (MBI Companies 2014-Current)
aka (past user names):
Gary_J_Orr (GOMO Stuff 2008-2014);
OrrG (Forum Studio 2005-2008);
Gary J. Orr (LHB Inc 2002-2005);
Orr, Gary J. (Gossen Livingston 1997-2002)
Message 14 of 17

Sean_Page
Collaborator
Collaborator

Yes, there can be real concerns with events if something within your subscription fires the event again, but there are a lot of great uses for them, and the events get raised whether you are listening or not, so not too much more overhead unless you are doing a lot of work when the event happens.

 

While the example I posted was a simplified version of how I am using that idling event, I hope it answers what you asked!


PS: You will also see in the event that you will need a Global or at least a class property for the UIControlledApplication to unsubscribe from the idling event.

Sean Page, AIA, NCARB, LEED AP
Senionr Partner, Computational Designer, Architect
Message 15 of 17

Julian.Wandzilak
Advocate
Advocate

I solved this problem in a different way. I’m not entirely sure how correct it is, but it seems to work well.

So, in OnApplicationInitialized() while registeringContextMenu Im creating my new contextMenu with UIApplication:

 

void OnApplicationInitialized(object sender, ApplicationInitializedEventArgs e)
{
    var app = sender as Autodesk.Revit.ApplicationServices.Application;
    var uiapp = new UIApplication(app);
    var rvtApp = uiapp.Application;

#if RELEASE2025 || RELEASE2026
    uiapp.RegisterContextMenu("W7K_Drafter", new W7k_DrafterContextMenu(uiapp));
#endif
}
To do it I created a simple constructor. It allows an easy access to UIApplication each time the menu is created. And from there it is easy to check selection or query active Document:  

 

internal class W7k_DrafterContextMenu : IContextMenuCreator
{
    private UIApplication UiApp;

    internal W7k_DrafterContextMenu(UIApplication uiApp)
    {
        UiApp = uiApp;
    }

    public void BuildContextMenu(ContextMenu menu)
    {
        ICollection<ElementId> elemIds =
            UiApp.ActiveUIDocument.Selection.GetElementIds();

        Document doc = UiApp.ActiveUIDocument.Document;

        ...
I hope this helps. And as always, I’m happy to be corrected if I’m doing something fundamentally wrong.

 

blog: w7k.pl more about me: Linkedin Profile
My add-ins for Revit: Drafter(180+ scripts) & Leveler
Message 16 of 17

Sean_Page
Collaborator
Collaborator

I think this is a great way of approaching it. I just use a Global because I have it scattered all over the App for different things and it just makes it consistent for me.

 

Thanks for sharing @Julian.Wandzilak!

Sean Page, AIA, NCARB, LEED AP
Senionr Partner, Computational Designer, Architect
0 Likes
Message 17 of 17

GaryOrrMBI
Advisor
Advisor

@Julian.Wandzilak,

This gives me something else to look into (as soon as I can get back to it, I had to shift back into Model Technician and do some actual chargeable work 🙂 ).

My limited knowledge says that this is happening when launching your interface, a dockable pane or some such? As of now, my tools are more limited in scope as they usually just sit there in a tool panel until a user clicks a button, then they go to work, then they shut back down and get out of the way. But it is something to look into in the future.

 

Many Thanks for the additional thoughts/direction,

-G

Gary J. Orr
GaryOrrMBI (MBI Companies 2014-Current)
aka (past user names):
Gary_J_Orr (GOMO Stuff 2008-2014);
OrrG (Forum Studio 2005-2008);
Gary J. Orr (LHB Inc 2002-2005);
Orr, Gary J. (Gossen Livingston 1997-2002)
0 Likes