Author Topic: Revit Select Event  (Read 8488 times)

0 Members and 1 Guest are viewing this topic.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Revit Select Event
« on: October 16, 2012, 10:36:14 AM »
I've got  scenario where I've got a modeless dialog open. But want to turn on the option of picking elements when a button is pressed on the dialog, then turn it off when the same button is pressed again. Unfortunately I can't seem to find any event to turn on so I can capture the selection as an event, and simply doing a while (true) with a uidoc.Selection.PickObject call seems impossible to turn off.

Anyone got any ideas? I mean, this is a bit silly isn't it?
Code - C#: [Select]
  1.         private void btnRun_Click(object sender, EventArgs e) {
  2.             UIDocument doc = command.CommandData.Application.ActiveUIDocument;
  3.             Transaction tran = new Transaction(doc.Document, "Renumber elements");
  4.             try{
  5.                 tran.Start();
  6.                 state = 1;
  7.                 GiveFocusToRevit();
  8.                 while (true) {
  9.                     String val = GetCalculatedString();
  10.                     Reference sel = doc.Selection.PickObject(ObjectType.Element, new BoudParameterFilter(),
  11.                         "Pick the element to set new value of [" + val + "], press ESC to stop");
  12.                     if (sel != null) {
  13.                         Element elem = doc.Document.GetElement(sel);
  14. // Continued
  15.  
I've attempted to use the state variable as a static int, but the button's "event" doesn't fire while it's already running. Am I supposed to start some sort of thread instead? I though Adesk's API's don't work well with threads. And the press ESC causes other issues.
« Last Edit: October 16, 2012, 10:39:27 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

BlackBox

  • King Gator
  • Posts: 3770
Re: Revit Select Event
« Reply #1 on: October 16, 2012, 12:23:36 PM »
Perhaps this is above my head (I do not do much formwork, althought I would like to), but if the user is going to click on a button in your modeless form to initiate a selection until the button is deselected, why not simply have two buttons?

By default, the ON button's Visible Property set to True, and the OFF button (stacked beneath) is set to False.

The code behind for the ON button's Click event could set the ON Button's Visible Property to False, and the OFF Button's to True, followed by the prompt for selection.

In turn, the OFF Button's code behind for the Click event could restore the default configuration.



You could always simply modify the appropriate Button Properties to use one Button for same, you'd just need to test for the Button's Text Property (presuming the ON / OFF is visible to indicate 'mode') to conditionally perform the appropriate Click event action(s). Either way.

HTH
« Last Edit: October 16, 2012, 12:33:15 PM by RenderMan »
"How we think determines what we do, and what we do determines what we get."

BlackBox

  • King Gator
  • Posts: 3770
Re: Revit Select Event
« Reply #2 on: October 16, 2012, 12:34:59 PM »
You could always simply modify the appropriate Button Properties to use one Button for same, you'd just need to test for the Button's Text Property (presuming the ON / OFF is visible to indicate 'mode') to conditionally perform the appropriate Click event action(s). Either way.

Simple example:

Code - C#: [Select]
  1.         private void btnRun_Click(object sender, EventArgs e)
  2.         {
  3.             string run = "Run";
  4.             string done = "Done";
  5.  
  6.             if (btnRun.Text == run)
  7.             {
  8.                 btnRun.Text = done;
  9.  
  10.                 // depending on your coding style, either call
  11.                 // a separate, pseudo btrnRun_Start() Method here,
  12.                 // or prompt user for selection, etc..
  13.  
  14.                 return;
  15.             }
  16.  
  17.             btnRun.Text = run;
  18.  
  19.             // ditto for when the user selects "Done," etc..
  20.  
  21.         }
  22.  
"How we think determines what we do, and what we do determines what we get."

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Revit Select Event
« Reply #3 on: October 18, 2012, 01:03:14 AM »
That is the principle I was going for originally. The issue is that the "ON" action defines an infinite loop. BTW, I used this as inspiration for the plugin. That code moves the while loop into the command Execute method, which opens the dialog and imediately starts the selection process. My idea however was to enable a more comprehensive renumbering app:

The original basically hard-coded the selection filters to only Rooms, Doors, Windows & Spaces (through a drop-down box and delegate functions [read similar to lambda's in lisp]). But my idea was to let the user pick an entity type by selecting an entity with bound parameters (i.e. I don't need to code for any new entity types at all), then read all the paramaters of text type and list them in a drop-down. That way the user can renumber anything - not just room numbers or door/window marks, but even view reference numbers placed on sheets, or comments for walls (whatever he/she likes) ... without coding anything extra for each scenario (as Adam Nagy's code needs to do).

So my form has another button on it to select the element type. This already works fine since it only calls the PickElement member, filters only elements which have bound text parameters (i.e. only those can get highlighted) and then polulates the paramater drop-down with their names. It also reads from a dictionary saved to the project settings as a string list to check which property was last used for that element type - a form of multiple defaults.

But due to my method, I can't have the form as for the actual click-and-increment all the time as Adam's is doing. Thus I've got a Run button. But the method of PickElement means it loops infinitely (i.e. while (true)), then jumps out of that loop through a try-catch on an ESC key press. The problem here is that such closes the dialog as well - which is generally not what the user wants to do (not in this case that is).

And having a 2-button / Run/Stop button doesn't stop the current iteration of the loop itself. Even with the state variable the PickElement method call is already waiting. So in this case the user needs to press the Stop button, then also pick something in the view (even if it's a blank spot) - simply to have the PickElement return so the loop can continue and see that the state has changed then break from the loop.

I was hoping these would be some sort of event I could turn on. Perhaps on the document. Something like
Code - C#: [Select]
  1. doc.OnPickElements += MyPickElementEventHandler;
That way the run action woult += such event, while the stop action would -= it. And the event handler would perform the actual action.

Though I can also see a problem with that: how to instantiate the filter object in such a case? Perhaps this is why ADesk didn't include such an event.
 
Edit: Sorry for all the typo's - new keyboard, new pc, new job, need to get used to it I guess.
« Last Edit: October 18, 2012, 01:16:33 AM by irneb »
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

BlackBox

  • King Gator
  • Posts: 3770
Re: Revit Select Event
« Reply #4 on: October 18, 2012, 08:39:15 AM »
Unfortunately, I'm not even sure what features the Revit API exposes to be of any help to you there.

... new job, need to get used to it I guess.

Congrats on the new job (I hope?); are you still in ZA?
"How we think determines what we do, and what we do determines what we get."

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Revit Select Event
« Reply #5 on: October 18, 2012, 01:23:41 PM »
Is there a ToggleButton in WinForms?  I use WPF and would use the ToggleButton events IsChecked to start creating a list of entities and IsUnchecked to stop collecting entities.
Revit 2019, AMEP 2019 64bit Win 10

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Revit Select Event
« Reply #6 on: October 19, 2012, 01:02:03 PM »
Congrats on the new job (I hope?); are you still in ZA?
Thanks, yes ... still here. I'm now working for a much smaller local firm doing only commercial & office buildings. Much less pressure, and more time for my "hobbies" ... I hope. Also they're using Revit near-exclusively, which is why I'm now focussing on this for a start.

Is there a ToggleButton in WinForms?  I use WPF and would use the ToggleButton events IsChecked to start creating a list of entities and IsUnchecked to stop collecting entities.
Well, yes that is what I'm attempting. Though, it's not simply collecting entities: Each time the user picks an entity, it's data must be updated according to the increment value. Then the loop starts again.

If I simply check the "toggle" button's status, there's still an issue. Once the "toggle" (or in my case state + IconIndex) is changed to "off", the lopp is still running and waiting for the last call to PickElement. Thus the user has to pick again to actually have the loop exit.

It's not a big deal, since the dialog does respond to other events while the pick loop is running. Though it works a bit strange, e.g.: Say the following is happening:
  • The user was busy renumbering room tags.
  • He notices that some door tag is out of sync as well, so he changes the element type to door by clicking on the "Select element type" button.
  • But seeing as the previous loop is still waiting for a "Select room", he's incapable of picking the door.
  • He needs to pick something, even nothing so the loop can finish and complete.
Point 3 & 4 is why I wanted this "toggle" idea. So he could turn off the increment selection in order to change settings. Else some strange things happen - exactly as does with Adam's code.


Actually after some thought I'm thinking of going about it a different way: Instead of toggling this on/off I need to have it run continuously (as Adam's does). But inside the loop I need to check what needs to happen after the PickElement returns (i.e. do the increment or select the type). Also the filter object needs to read the form's values live - thus each time it's called when hovering over an element it checks against the current type when in increment mode / checks for any type with parameters if in select type mode.


I'll have to investigate if this can work, though I'm feeling it might make the selection filter a lot less efficient. And I'm not too happy with the infinite loop idea.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

cshha

  • Guest
Re: Revit Select Event
« Reply #7 on: January 08, 2013, 10:42:48 AM »
Hopefully you've found a solution to this already, but if not, you'll definitely want to check out this post:

http://thebuildingcoder.typepad.com/blog/2010/09/selection-watcher-using-idling-event.html

The Idling event is the best (and only supported) way to interact with modeless dialogs. 
In Revit 2013, the Idling event has also been supplemented with a few other events that allow Revit to be driven from another program without constantly checking to see if there's any code to be run.

I believe that there's some sort of toggle button in WinForms - if not then you could use a checkmark or something.  You would then place code the Idling event that only runs when the toggle/check has been enabled.  The code could monitor the selection similarly to the post I linked to.

If you're using Revit 2012 or newer, you'll need to change the following code in the Idling event from:
Code: [Select]
Application app = sender as Application;
    UIApplication uiApplication = new UIApplication( app );
to
Code: [Select]
UIApplication uiApplication = sender as UIApplication;
« Last Edit: January 08, 2013, 10:45:53 AM by cshha »

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Revit Select Event
« Reply #8 on: January 08, 2013, 02:28:18 PM »
Thanks. Will check that out.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

TheMaster

  • Guest
Re: Revit Select Event
« Reply #9 on: January 09, 2013, 03:42:11 AM »
Thanks. Will check that out.

And if you're going to use the code that cshha referred you to, you should probably know that it has some pretty scary aspects, like this:

Code - Text: [Select]
  1.  
  2. if (m_Selection.Count != selected.Size)
  3. {
  4.    HandleSelectionChange(selected);
  5. }
  6. else //--count is the same... compare
  7. {    // UID to see if selection has changed
  8.  
  9.    string uid = "";                        
  10.    foreach (Element elem in selected)
  11.    {                            
  12.        uid += "_" + elem.Id;
  13.    }
  14.    if (uid != selectionUID)
  15.    {
  16.        HandleSelectionChange(selected);
  17.    }
  18. }
  19.  
  20.  

Aside from not using best-practices like the StringBuilder, that's not a good way to compare two sets of objects.  You can use this API (in .NET 4.0 or later) from the StructuralComparisons namespace, to compare two sets of objects for equality:

   StructuralComparisons.StructuralEqualityComparer.Equals();

That API can be used with two sequences or arrays, and will compare both for equality (which means that they both have the same number of elements, and each corresponding pair of elements when passed to Object.Equals() returns true), without the ball-and-chain approach used in the above quoted code.

If you can't keep both sets of objects around to compare them, you can instead use StructuralEqualityComparer.GetHashCode() on each sequence, while it is available, and just cache the result and compare that to the result returned by calling GetHashCode() on the next sequence. In this case, that's what you would do, although I'm puzzled to understand why Revit would fire an event signaling the selection has changed, if the selection contained the same contents as the previous selection had. I'm just guessing, but it looks like that code will result in some very noticeable lag when a large selection is made.


irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Revit Select Event
« Reply #10 on: January 09, 2013, 04:52:59 AM »
Thanks, that's a good tip indeed!

Fortunately for this scenario I'm only interested in a selection of one item at a time. So I could probably disregard anything as such:
Code - C#: [Select]
  1. if (selected.size > 1)
What I'm worried about though is setting a selection filter. Sorry, I'm really a newbie to Revit's API - still using google a lot to find anything as and when I get stuck.

Off topic: Blind hell! I'm getting fed up with this keyboard at the office. I'm used to the mechanical keyboard at home, so I can touch type. Keep missing keys since I'm not hammering it down to the bottom on this "normal" electronic keyboard.
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.

TheMaster

  • Guest
Re: Revit Select Event
« Reply #11 on: January 09, 2013, 10:22:31 AM »
Thanks, that's a good tip indeed!

Fortunately for this scenario I'm only interested in a selection of one item at a time. So I could probably disregard anything as such:
Code - C#: [Select]
  1. if (selected.size > 1)
What I'm worried about though is setting a selection filter. Sorry, I'm really a newbie to Revit's API - still using google a lot to find anything as and when I get stuck.


Your original problem, of stopping selection when the user press a button is actually easy to solve in AutoCAD using a Jig. You can use a Jig to do object selection too, and because it gets control every time the mouse moves, it's easy to cancel out of the input request whenever some condition requires it.

So, if Revit's API has something that behaves like the Jig class, that's what I'd look into.

cshha

  • Guest
Re: Revit Select Event
« Reply #12 on: January 09, 2013, 04:14:00 PM »
irneb, what version of Revit are you using?  The sample code I'm including is for Revit 2012.  The changes are generally minor between versions so it shouldn't be tough to modify as needed.

If you need to just select one object at a time, you need to do something like this:

Code: [Select]

[...]
            UIDocument uidoc = commandData.Application.ActiveUIDocument; //I'll assume you're using a macro. If you're coding an add-in, retrieving the active document is a slightly different process

            Document doc = uidoc.Document;

            Autodesk.Revit.UI.Selection.Selection sel = uidoc.Selection;

            Reference ref = sel.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, new ElementSelectionFilter(), "Select an Element");

            Element el = doc.get_Element(ref.ElementId);

[...]


public class ElementSelectionFilter : ISelectionFilter
{
            public bool AllowElement(Element elem)
            {
                if (elem.Category.Id.IntegerValue == (int)BuiltInCategory.OST_WhatEverCategoryYouWantToFilterFor) return true;

                //Put any other kind of check here, return true to allow the element to be selected, otherwise return false

                return false;
            }

            public bool AllowReference(Reference refer, XYZ pos)
            {
                return false;
            }
}

If you want to select multiple objects, replace
Code: [Select]
Reference ref = sel.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, new ElementSelectionFilter(), "Select an Element");with
Code: [Select]
IList<Reference> referenceList = sel.PickObjects(Autodesk.Revit.UI.Selection.ObjectType.Element, new ElementSelectionFilter(), "Select desired elements. Press Finish when done.");
A small toolbar with the finish button will appear just under the ribbon if you're selecting multiple items.

If you don't need your modeless form to update until after all of the elements have been selected, then using the Selection.PickObjects function is probably the best bet. 

As far as the selection filter goes, you can put any kind of verification code in the AllowElement part of the ISelectionFilter.  What kind of filter are you trying to set?


Back to your original post, you may be able to use a while loop with the Selection.PickObject by doing something like this:
Code: [Select]
bool continueLoop = true;

while (continueLoop) {
   String val = GetCalculatedString();
try {
   Reference sel = doc.Selection.PickObject(ObjectType.Element, new BoudParameterFilter(),
   "Pick the element to set new value of [" + val + "], press ESC to stop");
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException)
{
//Pressing escape to cancel the pick object operation throws the above exception
continueLoop = false;
}
[...]
}
               

It's also a good idea to always wrap your transactions in a using block like so:
Code: [Select]
using (Transaction trans = new Transaction(doc))
{
trans.Start("Doing something");
//Operation
trans.Commit();
}

If you need some examples, take a look at the samples provided in the Revit SDK.  There are tons of examples in there, and chances are there's something related to what you're trying to do.

irneb

  • Water Moccasin
  • Posts: 1794
  • ACad R9-2016, Revit Arch 6-2016
Re: Revit Select Event
« Reply #13 on: January 09, 2013, 11:17:11 PM »
I'm using 2013 (though I've still got 2012 installed on one PC, so I might try it there too. Thanks for the samples, I'll try it out this weekend - just attempting to finish off a set of construction details for a warehouse / office block at the moment.  ::)
Common sense - the curse in disguise. Because if you have it, you have to live with those that don't.