Implement ICommand with actual ExternalCommandData (not null)

Implement ICommand with actual ExternalCommandData (not null)

Anonymous
Not applicable
1,237 Views
5 Replies
Message 1 of 6

Implement ICommand with actual ExternalCommandData (not null)

Anonymous
Not applicable

Hi!

I was following @jeremytammik 's post here https://thebuildingcoder.typepad.com/blog/2013/02/adding-a-button-to-existing-ribbon-panel.html as I want to add a custom panel with custom buttons to one of our existing tabs, but I can't just add it as you would always do, because that existing tab is created through pyRevit (and it can't be created via pyRevit as it needs to access a few things pyRevit cannot access).

 

I initially thought it wouldn't have worked to add custom C# panels to an existing pyRevit  tab, but I tried mostly out of curiosity and I found out it actually is possible! So I followed the steps from the post (link above) and I'm able to create the same button within my pyRevit tab.

 

The problem I have is that I don't want to open the browser but start an IExternalCommand. So I found this other link  https://thebuildingcoder.typepad.com/blog/2009/12/custom-ribbon-tab.html but then it fails because the Execute(object a) implementation sets ExternalCommandData commandData = null; so then the IExternalCommand fails to retrieve the Application.

    public class AdskCommandHandler : System.Windows.Input.ICommand
    {
        string AssemblyName
        {
            get;
            set;
        }

        string ClassName
        {
            get;
            set;
        }

        public AdskCommandHandler(
            string assemblyName,
            string className)
        {
            AssemblyName = assemblyName;
            ClassName = className;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object a)
        {
            return true;
        }

        public void Execute(object a)
        {
            Assembly assembly = Assembly.LoadFrom(AssemblyName);

            IExternalCommand command = assembly.CreateInstance(ClassName) as IExternalCommand;

            ExternalCommandData commandData = null; // Here is the issue
            string message = string.Empty;
            ElementSet elements = null;

            Result r = command.Execute(commandData,
                    ref message, elements);
        }
    }


I've read from the comments Jeremy is saying that we're not supposed to use this workaround because it's not officially supported but then he sends a couple more articles (https://thebuildingcoder.typepad.com/blog/2009/12/create-a-real-revit-ribbon-tab.html and https://thebuildingcoder.typepad.com/blog/2010/01/custom-ribbon-tab-context-switch.html) mentioning Guy Robinson managed to fix this. Now I'd really like to see how he did it, but the links in those 2 posts are not working anymore.

 

Any chance anyone has those links functioning??

 

Also curious to see if there are better ways of doing what I'm trying to do?

0 Likes
Accepted solutions (2)
1,238 Views
5 Replies
Replies (5)
Message 2 of 6

jeremytammik
Autodesk
Autodesk

Nice to see all the research you did and resources you examined. Thank you for sharing that path.

  

I didn't understand exactly what you need, but I hope I got the main gist; I also don't understand why you are having any difficulties achieving what you are after. Could you summarise your exact goal very briefly, succinctly?

   

Can't you just add the button using pure .NET, independently of the Revit API, and then use an external event to enter a valid Revit API context when needed? That would be the standard approach...

   



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

0 Likes
Message 3 of 6

Anonymous
Not applicable

Thank you for your reply Jeremy!

 

Let me try to explain better. Background: where I work we have a custom tab (called Wombat) made of custom tools and that tab is made with pyRevit. This means that I'm not controlling directly the IExternalApplication adding the panels and buttons, but pyRevit is doing it for me. All I need to do is adding folders and files and pyRevit builds the toolbar for me.

 

Now I am working on a new, more complex, tool and for that I needed access to the IExternalApplication and its OnStartup method (I need to do stuff at startup, add a couple of buttons, register a dockable panel, etc.).

At some point we decided that this new tool should live under the Wombat tab (just the location of the button, not the hosting through pyRevit), so I thought that it could have been possible to the add the button in the same way you would add a custom button to an existing tab (i.e. "Architecture", "Structure", etc.), hence I found those links I pasted in the original post

ATassera_0-1606254197978.png

 

So that's why I thought of using the example here https://thebuildingcoder.typepad.com/blog/2009/12/custom-ribbon-tab.html and implementing AdskCommandHandler, but then again, passing ExternalCommandData commandData = null; will fail the Execute method in the IExternalCommand.

Adding the buttons in the usual way with application.CreateRibbonTab("Wombat"); and then application.CreateRibbonPanel("Wombat", panelName); and then creating the PushButtonData to be added to the panel won't work of course because a tab called "Wombat" already exists

 

When you say "add the button using pure .NET" do you refer to something like WPF/WinForms buttons and then add an event handler to the .Click event? Can you add a WPF button to a panel?
That could work too as I just need to get the application from the ExternalCommandData , but I thought this would be even hack-ier than what I was already doing 😄 

0 Likes
Message 4 of 6

RPTHOMAS108
Mentor
Mentor
Accepted solution

UIApplication.CreateRibbonPanel(String, String)

 

"This method will create a custom panel appending to the specified tab."

 

If 'wombat' tab exists then don't create it, if it doesn't then create it. This is all related to timing of what add-in comes first (one from pyRevit or your separate one). If pyRevit doesn't check to see if 'wombat' exists before creating it then that is an exception waiting to happen elsewhere with pyRevit (it should try adding with try catch since it doesn't know what already exists). If the name was something more general such as 'Tools' then you can see the issue that pyRevit creators should be made aware of if you are saying it isn't checking.

 

I've never had an issue adding two different panels from two different assemblies to the same tab using standard methods. The examples related to adding to internal tabs likely hold no relevance since the tab you are aiming to add to is not internally created.

 

Your actual error is related to a member within a separate assembly not being populated with a value. Likely you would have to invoke the member passing the arguments from your external command, but sounds like not the most straightforward solution.

Message 5 of 6

AGGilliam
Collaborator
Collaborator
Accepted solution

If you're just trying to add it to a tab that already exists (the Wombat tab), couldn't you just create the panel using the name of the tab without trying to recreate the tab?

RibbonPanel newPanel = application.CreateRibbonPanel("Wombat", "panelName");
0 Likes
Message 6 of 6

Anonymous
Not applicable

Ouch I feel stupid now! 😄

 

You guys are right! The error wasn't in adding the panel to the existing tab but trying to add create another tab called Wombat with application.CreateRibbonTab("Wombat);

I just got rid of it nd I'm now simply using application.CreateRibbonPanel("Wombat, panelName); and it works correctly.

 

Excuse me for the confusion