Announcements
Attention for Customers without Multi-Factor Authentication or Single Sign-On - OTP Verification rolls out April 2025. Read all about it here.

RibbonItem Combo Box of scopBoxes at Runtime

SONA-ARCHITECTURE
Advocate

RibbonItem Combo Box of scopBoxes at Runtime

SONA-ARCHITECTURE
Advocate
Advocate

I'd like to create a RibbonItem with a pulldown button containing the list of all scop Boxes of the active document.

Is it possible to add items im the pulldown button at Runtime each time the user add a new scopBox? Or to delete an item when the user detele a scop box?

Like Enscape do with the Active 3D Views?

 

 

Pierre NAVARRA
SONA-Architecture.
http://www.sona-architecture.com
https://fr.linkedin.com/in/pierre-navarra-62032a107
0 Likes
Reply
Accepted solutions (1)
2,109 Views
10 Replies
Replies (10)

jeremytammik
Autodesk
Autodesk

Might be easier to implement this independently of the Revit API, modeless, using Windows API or .NET libraries...

 



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

0 Likes

SONA-ARCHITECTURE
Advocate
Advocate

Hi Jeremy.

Thx for answering.

Do you have a lead or an exemple?

It seems that the Enscape plugin show the list of 3D Views with a modeless form. What do you think about that?

But I'd like to know how which event the plugin catch to refresh the combo list with the 3d Views. Do you have an idea?

 

2020-10-29_12h02_21_cut_001 (1).gif

 

Pierre NAVARRA
SONA-Architecture.
http://www.sona-architecture.com
https://fr.linkedin.com/in/pierre-navarra-62032a107
0 Likes

RPTHOMAS108
Mentor
Mentor

The difficulty isn't responding to an event or even adding items to a combo box on the tab at various points. The difficulty is removing those items. Workarounds have been suggested whereby you add non-visible place holders.

 

What you present isn't typical of how a Revit combo box looks once opened i.e. it contains one item of fixed screen size and that one item appears to contain a collection (listview). I have my suspicions that it looks like a case of adding a combo box with one item and then using the dark arts of p/invoke to subclass/hook that item giving it an entirely different appearance...

 

Have you investigated this mysterious occurance with Spy++? i.e. what you oridinarily get and what you get here?

0 Likes

SONA-ARCHITECTURE
Advocate
Advocate

Hi RPTHOMAS108

Thx for answering me.


@RPTHOMAS108 wrote:

The difficulty isn't responding to an event or even adding items to a combo box on the tab at various points. The difficulty is removing those items. Workarounds have been suggested whereby you add non-visible place holders.

I thought to add on item to my ComboBox on StartUp

Beside, catching an event of document (which one?) and changing the text of the item of the comboBox and replacing by the ScopBox selected into modelss frame. What do you thing about that?

 

At the moment, my panel looks like this :

screenshot_440.png

Pierre NAVARRA
SONA-Architecture.
http://www.sona-architecture.com
https://fr.linkedin.com/in/pierre-navarra-62032a107
0 Likes

RPTHOMAS108
Mentor
Mentor

At any time you can use UIApplication.GetRibbonPanels("Your tab name") to get the panels each of which you know the name of.

 

Then you can get the ComboBox RibbonItem.GetItems

 

You can then add items with ComboBox.AddItem, get an item ComboBox.GetItems and then change it's text ComboBoxMember.ItemText. You can't however remove items.

 

In terms of events it depends on the exact purpose, DocumentChanged for example looking for scopebox (volumes of interest). You also have to maintain the state between documents (this is where it all falls down). The scope boxes in one document will not exist in another. You then may have to use IdlingEvent to get the active document and repopulate the scope box list. These things typically relying on subscribing to more than one event.

 

With place holder items you could add empty items at start up for the purpose of renaming but not hundreds of them.

 

There are other options but not API approaches. Ideally you would be able to remove members (of a scope box at least). or allow it to bind to a list of something.

0 Likes

BenoitE&A
Collaborator
Collaborator

Hey,

Sorry for the stupid question, but is it that crucial not to have a button in your ribbon? 

Looks like what you're trying to do is complicated, it depends on the view, has to be updated every time you create a ScopeBox or change your view, which means create listeners, etc...

So why not a simpler way, let's say a good old button? So that you compute it once everytime the user clicks... I guess you even can create a shortcut (I haven't but I guess you can).


Benoit FAVRE
CEO of etudes & automates
www.etudesetautomates.com/

RPTHOMAS108
Mentor
Mentor

I did figure this one out to be honest but the code ended up quite convoluted. It involved no dark arts just referencing: AdWindows and UIFramework.

 

You find the ComboBox after it has been created UIFramework.RvtRibbonCombo.

Change it's Binding and Template properties:

.SelectionBoxItemTemplate (The template used to display the selected item, keep it simple a single TextBlock)

.ItemTemplate (The template of the items within the control)

.ItemsSourceBinding 

 

For .ItemSourceBinding you can create a binding to a collection of a single item. This single item then itself can have other content such as a list or something else templated however you like.

 

Most of the convolution involves ensuring that your single item is current when the box closes (to ensure Revit ComboBox shows a value). Also handling the events of your single item control e.g. how you pass the selected item event out from the Listbox contained in your template. I did it by creating an xaml resource dictionary containing the templates above with events handled in the code behind of the resource dictionary, then passed out from that. 

 

Behold the amazing results of my ListBox within a ComboBox:

RPTHOMAS108_0-1604276402809.png

 

I have to look at the stability of the above long term and to be honest I would probably use a DockablePane i.e. why create something so complex on a hidden tab when a DockablePane can be ever present in a modeless form?

0 Likes

SONA-ARCHITECTURE
Advocate
Advocate

Hi and Thx a lot for your answer 😀

I do not understand your last sentence :


@RPTHOMAS108 wrote:

I have to look at the stability of the above long term and to be honest I would probably use a DockablePane i.e. why create something so complex on a hidden tab when a DockablePane can be ever present in a modeless form?


What do you mean with DockablePlane?

 

Besides, I saw this solution here but I don't understand also :  https://forums.autodesk.com/t5/revit-api-forum/ribbon-combobox-clear-change-data/m-p/9559732/highlig...

 

Many Thx

Pierre NAVARRA
SONA-Architecture.
http://www.sona-architecture.com
https://fr.linkedin.com/in/pierre-navarra-62032a107
0 Likes

RPTHOMAS108
Mentor
Mentor

Most planes dock at the airport don't they.

 

A DockablePane is the official API way of adding a modeless window pane to Revit. This pane can be docked such as adjacent to the project browser of floating, refer to:

UI.DockablePane

UI.DockablePaneProviderData

 

When you do things a way that isn't supported then you run the risk of bypassing control measures that may cause instability of the application due to a state arising that isn't accounted for by the developers. For example I imagine you may put something other than RibbonItem in a ComboBox and then Revit internally looks for a member of RibbonItem that isn't available on the object (since it isn't a RibbonItem).  Also the API events are designed to occur at certain stages with the application state known. Non-api events from AdWindows etc. may not check or give any indication of such a state. Presumably they are usually raised and subscribed to in an set way for non-API purposes.

0 Likes

SONA-ARCHITECTURE
Advocate
Advocate
Accepted solution

 

Hi, I'm done with my problem.

Here is the code, the result in Gif just bellow

 

The code to create PushButon disabled for showing a tag and the ComboBox Both stacked
Juste after the listener for the Event

 

// PushButton for the Label : ScopBox Selector
            //-----------------------------
            PushButtonData _pbDataScopBoxSelectorLabel = new PushButtonData("Scop Box Label", "Scope box selected", AddInPath, "Sona_Ribbon.ExternalCommandDoNothing");            
            //-----------------------------

            // ComboBoxData for ScopBox Selector
            //-----------------------------
            ComboBoxData _cbDataScopBoxSelector = new ComboBoxData("ScopBoxSelector");
            _cbDataScopBoxSelector.ToolTip = "Select a scope box";
            
            IList<RibbonItem> _lstScopBoxesItemsStacked = _rbPanelPointCloud.AddStackedItems(_pbDataScopBoxSelectorLabel,_cbDataScopBoxSelector);
            ((PushButton)_lstScopBoxesItemsStacked[0]).Enabled = false;
            
            ComboBox _cbScopBoxSelector = _lstScopBoxesItemsStacked[1] as ComboBox;
            // add first element
            ComboBoxMemberData comboBoxMemberData = new ComboBoxMemberData("0@Empty", "Please select a ScopeBox");
            ComboBoxMember comboboxMember = _cbScopBoxSelector.AddItem(comboBoxMemberData);   
            //Catch to listen event
            _cbScopBoxSelector.DropDownOpened += new EventHandler<Autodesk.Revit.UI.Events.ComboBoxDropDownOpenedEventArgs>(AddNewScopBoxes);
            
            //-----------------------------

 


Here is the code calling in the event for filling the comboBox with ScopeBoxes of the document

 

 

       private void FillComboBoxSelector(ComboBox _givenComboBox)
        {
            // Get all ScopBoxes of Document
            ElementCategoryFilter _cFilter = new ElementCategoryFilter(BuiltInCategory.OST_VolumeOfInterest);            
            FilteredElementCollector _feCollector = new FilteredElementCollector(_uiApplication.ActiveUIDocument.Document);
            IList<Element> _elements = _feCollector.OfCategory(BuiltInCategory.OST_VolumeOfInterest).ToElements();

            // add the first item if combo is empty
            if (_givenComboBox.GetItems().Count()==0)
            {
                ComboBoxMemberData comboBoxMemberData = new ComboBoxMemberData("0@Empty", "Please select a ScopeBox");
                ComboBoxMember comboboxMember = _givenComboBox.AddItem(comboBoxMemberData);
            }

            // put all existing items unvisible
            foreach (ComboBoxMember comboboxMember in _givenComboBox.GetItems())
            {
                
                comboboxMember.Visible = false;
            }

            // For each scoBoxes in Document
            foreach (Element elem in _elements)
            {
                bool alreadyContained = false;
                string str_name = elem.UniqueId.ToString() + "@" + elem.Name;

                foreach (ComboBoxMember comboboxMember in _givenComboBox.GetItems())
                {   
                    // If already exists, so put to invisible, delete is impossible
                    if (comboboxMember.Name == str_name)
                    {   
                        alreadyContained = true;
                        comboboxMember.Visible = true;
                        break;
                    }
                }
                // Create the item if not exists, so if it's new or renamed
                if (!alreadyContained)
                {
                    ComboBoxMemberData comboBoxMemberData = new ComboBoxMemberData(str_name, elem.Name);
                    ComboBoxMember comboboxMember = _givenComboBox.AddItem(comboBoxMemberData);
                    comboboxMember.Visible = true;
                }
            }
                                    
            _givenComboBox.GetItems()[0].Visible = true;
            ////////////////////////////
        }

 

 

2020-11-04_18h36_55.gif

 

 

Pierre NAVARRA
SONA-Architecture.
http://www.sona-architecture.com
https://fr.linkedin.com/in/pierre-navarra-62032a107
0 Likes