Pass info from ribbon button to command

Pass info from ribbon button to command

Kevin.Bell
Advisor Advisor
2,100 Views
7 Replies
Message 1 of 8

Pass info from ribbon button to command

Kevin.Bell
Advisor
Advisor

Hi,

 

I've created an addon that adds buttons to my Revit ribbon based on the contents of an XML file.

This way buttons can easily be added  and removed by editing the XML file.

The XML file is read at startup and buttons added to the ribbon accordingly.

 

The buttons open PDF files in the users default reader - the PDFs are my office standards.

 

The problem I have is that I need to work out which of the my buttons were pressed so the command can open the correct PDF file.

 

All buttons run the same external command, which then uses the XML file to read the path of the PDF file to open.

 

So if button 3 was pressed, it would look up the path of the PDF file in the XML document for button 3.

 

The issue is that I cannot detect if button 3 or any other button was pressed, is there any way I can pass data from the button to the command so I it can determine which was pressed.

 

I know its possible to subscribe to a button pressed event, but I'm hoping to avoid that as I've never set up an event subscription before and I'm worried about causing too much lag to Revit by detecting all button presses...

 

Thanks.

0 Likes
2,101 Views
7 Replies
Replies (7)
Message 2 of 8

jeremytammik
Autodesk
Autodesk

The Revit API does not provide any method to determine which button was used to launch your command.

 

You could certainly determine that by using the .NET Automation library, as demonstrated in various discussions by The Building Coder:

 

https://thebuildingcoder.typepad.com/blog/automation

 

However, I would suggest using another simple approach that _is_ officially supported by the Revit API:

 

Implement a separate external command for each one of your buttons. In the external command, make a note of which command was called and thus which button was pushed, and then call a common method that handles all your commands with that information in hand.

 



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

0 Likes
Message 3 of 8

MarryTookMyCoffe
Collaborator
Collaborator

I personally use

Autodesk.Windows.ComponentManager.PreviewExecute += ComponentManager_PreviewExecute;

just to know what button was last pushed and I had no problem with lag.

but If you really don't what to use event maybe we can be tricky. We know that ExternalCommand is not static class that inherit from IExternalComand. I thing revit implement button in order made in App.cs. So what if we gonna made some id creation in creator of class, like:

    class NotTested : IExternalCommand
    {
        static int? id_Counter;
        int id;

        public NotTested() => id = Id_Counter++;

        public static int Id_Counter { get
            {
                if (id_Counter is null)
                    id_Counter = 1;
                return (int)id_Counter;
            }
            set => id_Counter = value; }


        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {            
            //go nuts with id 
            return Result.Succeeded;
        }
    }

If every thing would go with plan first button should be 1. Of course I am making a lot of assumptions, like:

  • revit make only one instance for every button(I thing that this is how it work, but never tested it)
  • revit made button when it read IExternalApplication(this is big unknown)
  • revit don't sort buttons to create.

 

-------------------------------------------------------------
--------------------------------|\/\/|------------------------
do not worry it only gonna take Autodesk 5 years to fix bug
0 Likes
Message 4 of 8

Kevin.Bell
Advisor
Advisor

Jeremy - I can't do your suggestion of a separate external command for each button. The idea of the addon is that buttons can be added easily by editing the XML file, I couldn't make an external command for each button as I've no idea of what buttons the user would add to the XML file.

 

 MarryTookMyCoffe - not too sure I follow your code, I'll have to read it a bit more!

 

Thanks.

0 Likes
Message 5 of 8

jeremytammik
Autodesk
Autodesk

Aha. In that case, MarryTookMyCoffe points the way. Good luck! Please let us know the solution.

 



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

0 Likes
Message 6 of 8

stever66
Advisor
Advisor

When it comes to programming, I still consider myself a beginner, so these suggestions might be way off track, but;

 

Would it help to use an external application instead of an external command to read the xml file and load in the buttons?   That seems like it would make it easier to respond to a button that was pressed since the application is already running in memory. 

 

Or maybe even REX would have a way to do this?  I'm not exactly sure what REX is, but the Getting started guide says REX includes:  "Built-in command-based architecture to make menu and toolbar development easier."

 

Or, is it possible to add a string variable to each button to hold the PDF path, and then read that when the button is pushed?  Maybe its even possible to define a new class that inherits the button class and adds get and set methods for a string variable?

 

 

0 Likes
Message 7 of 8

RPTHOMAS108
Mentor
Mentor

I've created similar in the past. The best option is to use a Autodesk.Revit.UI.ComboBox and then add event handers for:

 

CurrentChanged

DropDownOpened

DropDownClosed

 

The CurrentChanged event args have members for 'NewValue' and 'OldValue'

Step 1: during OnStartup of IExternalApplication read your XML into a dictionary of string, string (your key is the text displayed on the ComboBox item and the dictionary item value is the link).

Step 2: when CurrentChanged occurs your read the value of the NewValue (ComboBoxMember.ItemText) and find the link related to this in the dictionary created in Step 1.

Step 3: is simply to retrieve link from dictionary and open it.

 

The UI name space contains more than just split buttons and push buttons.

 

A more exotic approach using class inheritance is given below. With this you also have to decide how many items you will have as a maximum (for the example below it is ten).

 

Another idea is based on the fact the current journal location can be found from within an IExternalCommand and all command actions are recorded in a journal. However I'm not sure if the sequence involved (journal entries made vs read) would always capture this correctly. You also don't want to parse a large journal file to read a single entry.

 

Imports Autodesk.Revit.DB
Imports Autodesk.Revit.UI

<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EntryClass
    Implements IExternalApplication

    Public Shared LinkDictionary As New Dictionary(Of String, String)
    Public Function OnStartup(application As UIControlledApplication) As Result Implements IExternalApplication.OnStartup

        Dim SampleList As String(,) = New String(2, 1) _
            {{"www.google.com", "Google"},
            {"www.autodesk.com", "AutoDesk"},
            {"https://en.wikipedia.org/wiki/Main_Page", "Wikipedia"}}
        'Above list would come from elsewhere

        Dim TxStr As String = Me.GetType.Name
        Dim RP As List(Of RibbonPanel) = application.GetRibbonPanels(Tab.AddIns)
        Dim RPi As RibbonPanel = RP.Find(Function(j) j.Name = "Test")
        If RPi Is Nothing Then
            RPi = application.CreateRibbonPanel(Tab.AddIns, "Test")
        End If

        Dim PDBd As New PulldownButtonData("RPT_Test_PDB", "Links")
        Dim PDB As PulldownButton = RPi.AddItem(PDBd)

        For i = 0 To SampleList.GetUpperBound(0)
            If i > 10 Then Exit For Else
            'For this example can have a maximum of ten items

            Dim TPlus As String = "RPT_RvtTests.EP" & CStr(i)
            If LinkDictionary.ContainsKey(TPlus) Then Continue For Else
            LinkDictionary.Add(TPlus, SampleList(i, 0))

            Dim PBd As New PushButtonData("RPT_Test_PB_EP" & CStr(i), SampleList(i, 1), MyAssemblyPath, TPlus)
            PDB.AddPushButton(PBd)
        Next

        Return Result.Succeeded
    End Function
    Private Shared Function MyAssemblyPath(Optional FileName As String = "") As String
        Dim ASBPath As String = System.IO.Path.GetDirectoryName(System.Reflection.Assembly _
                                                          .GetExecutingAssembly().GetName.CodeBase)
        ASBPath = Strings.Replace(ASBPath, "file:\", "")
        Dim ASBName As String = System.IO.Path.GetFileName(System.Reflection.Assembly _
                                                                 .GetExecutingAssembly().GetName.CodeBase)
        Dim out As String = ASBPath & "\" & ASBName ' & ".dll"

        Return out
    End Function
    Public Function OnShutdown(application As UIControlledApplication) As Result Implements IExternalApplication.OnShutdown
        Return Result.Succeeded
    End Function
End Class

<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EntryPoint_Base
    Implements IExternalCommand
    Public ReadOnly Property Typ As String = "RPT_RvtTests." & Me.GetType.Name
    Public Function Execute(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result Implements IExternalCommand.Execute
        If EntryClass.LinkDictionary.ContainsKey(Typ) Then
            TaskDialog.Show("Empty", EntryClass.LinkDictionary(Typ))
        End If
        Return Result.Succeeded
    End Function
End Class

<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP0
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP1
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP2
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP3
    Inherits EntryPoint_Base
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP4
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP5
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP6
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP7
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP8
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP9
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class
<Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)>
<Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)>
Public Class EP10
    Inherits EntryPoint_Base
    Implements IExternalCommand
End Class

 

 

 

0 Likes
Message 8 of 8

Kevin.Bell
Advisor
Advisor

Thanks for all the replies, a lot to digest there. The combo box idea sounds interested, I'll have to do some tests.

0 Likes