In this post I will go over a few things:
- Implementing ISelectionFilter
- which we will use while prompting user to select things via UI
Before we even get to selection stuff let’s first make sure that we have our application and documents needed. We specified that to be the first thing to do in our pseudo code so let’s do that first:Now I will go over it line by line:
- uiApp is our Revit application. This will give us project level access to Revit database.
Next we want to prompt a user to select bunch of lines, so that we can measure their length. While user is selecting them we want to filter out any objects that are not Detail Lines. For that we can use something called ISelectionFilter like so:
Here’s the line by line breakdown:
- create a new public class that implements ISelectionFilter
- define first method that will return a boolean true for elements that we want to allow.
- define return statement. our rule is that if selected element Category Id value equals to that of BuiltInCategory.OST_Lines elements will be passed through. Basically we only allow Lines to pass through.
- define second method for allowing references.
- since we don’t want to pass references through we simply return false here.
What this method will do, is that it will allow us to select only elements that are of category OST_Lines. That’s a pretty nifty little tool that is super helpful in our case.
Let’s implement this filter in our code, and prompt a user to select something:
Here’s a line by line breakdown:
- create a new empty list of Elements that will be populated with elements selected by user.
- this is just a shortcut so that I don’t have to type out a super long line of code later.
- This defines our Selection class that will be used to prompt user to select things.
- Now that we have our filter class defined let’s create an instance of it and assign it to “selFilter” variable.
- This prompts user to “select lines” and when user is done will populate pickedRef list with selected elements.
This is what our code should look like to this point*:
*You should also notice that I added another using statement at line 10. This is just to make our code cleaner at line 42. If I didn’t add a using statement pointing directly at Selection class I would have to change line 42 to something like this:
Selection.Selection sel = uiApp.ActiveUIDocument.Selection;
That’s arguably much muddier hence the using statement. :-)
Let’s Build this project now, and replace the dll file in Revit Addins folder. You don’t have to worry about the addin file, since that hasn’t changed. This is what we should be getting now in Revit:
- This is area of my rectangle used to select things.
- Only Detail Lines present within the selection rectangle were highlighted (selected). This means that our selection filter is working just fine. :-)
Now that we have out selection filter implemented we can move on to rest of our code that will measure the length of all of the selected lines and return a value for you. First I want to iterate through my list of selected curves and return their length:Here’s the line by line breakdown.
- each length is a double (a number) so we need a new list to store them in.
- this starts a new foreach loop that will iterate through every selected detail line
- since when lines were selected they were returned to us as Element objects we need to cast them to DetailLine class. We do that because we want to be able to access DetailLine properties of each of the Elements.
- if somehow our cast failed it will return null. let’s check that we are not getting a null value here
- now let’s add the Length of the line to our list
Now we should have a list that holds bunch of number values. Each value represents a length of the detail line. All we have to do now, is sum them all up and return that information to the user. Also, I mentioned before that I am interested in returning that info not only in imperial units of length but also in metric. This will require a few extra lines of code but it’s easy enough to cover here:
Here’s the line by line breakdown:
- here we build a string that will read out total length in ft. I also add a Math.Round method to round our results to two decimal places.
- here we build a string that will read out total length in meters. Since all values returned by Revit are always in ft we have to add a * 0.3048 to convert from ft to m.
- same as above but this time we convert to mm.
- same as above but this time we convert to inch by multiplying by 12.
- we create a new instance of String Builder. String Builder is an object that can be used to quickly concatenate bunch of strings into one continues string.
- first we append “Total Length is: ” to string builder.
- then we append a new line that has our total length in ft.
- then we append a new line that has our total length in inches.
- then we append a new line that has our total length in meters
- finally we append a new line that has our total length in mm
- last step of our code is to show the results to user. We can use a very simple TaskDialog object to do that. It will show a simple message window to the user that will contain our StringBuilder object converted to string.
This should be it. Here’s all of our code for this example:
Now if we Build this and again replace the DLL file with the latest one, this should be our result in Revit:
This is it! If you stuck around with me, for this long, you were just able to build your first Revit plug-in. I am thinking about extending this into a series, and next time going over taking our External Command addin and showing you guys how to add it to a larger set of tools. I would like to show you how to create your own tab on Revit ribbon, add buttons and proper icons for your own tool-set branding. Since I already shared a small tool, called Legend Duplicator few weeks ago, it will be easy to take these two and put them together under our own tab in Revit.
Final DLL and Addin files can be downloaded from here:CurveTotalLength (811 downloads)
Thanks for following!
Just finished the first three lessons on this, and after a little stumbling around, got my line length totals working! Thanks for taking the time to put this together.
(Only real struggle was where we’d jump to a new section of code, trying to figure out where to pick up, but I think that’s with my lack of knowledge of C# structures. The ‘complete’ code at the bottom was my road map.)
Glad you got it to work. Cheers!
Really nice work with this. For me, it was really helpful to start with the C# coding. Also, I got some things that I’ll surely use in my future apps.
Have some questions:
– The process mentioned here is that after you build the app, you just copy the updated .dll file to the folder. I would like to know how to properly set the Visual Studio to re-write the updated .dll file at the %appdata% folder.
– Following the previous question, I also wanted to configure my Visual Studio to debug the code using Revit, so I tried to do the explained procedure here: https://www.youtube.com/watch?v=C0mNU2bEUSs&index=19&list=WL.
– Finally, I looked the setting from the Jeremy Tammik’s template.
When I press “Start” button in VS, I get an execution error in REVIT and have to stop the process.
As a summary, I want to know how to code in VS and test the codes I write but not having to Open-Close Revit in every iteration.
Thanks in advance for your help!
You can try this in the post build event: xcopy /Q/Y “$(TargetDir)some.dll” “%ALLUSERSPROFILE%\Autodesk\Revit\Addins\2018”. For debugging on top of revit make sure you have the Start external program setting in Debug tab set to appropriate version of Revit.exe file. Then you also have to change the Use native compatibility mode to be unchecked under the Tools>Options>Debugging. Finally you can use the External Addin Manager to re-load the DLL in question and test your plug-in without having to restart revit. For that to work though you don’t place the dlls in the addins folder, so all previous pointers are useless.
Very thanks for this lessons. It helps me a lot forward to achieve my goal.
When I write this for a detail line it works perfect.
But when I try this to do for ducts, I am very struggling.
Do you know how I can get this done for the “Length” and “Area” parameter for a collection of ducts?
Thanks in advance for your help!