This is what I learned building my first Plugin for Autodesk Revit.
By default door tags in Revit contain an auto-incrementing number starting at 1. Often it would be more useful if they showed the number of the associated room instead. This is also the naming scheme followed by the Revit Architecture sample Project.
To automatically label the door tags this way we need to
- Access a door element when it is added or iterate over every door (to rename all existing doors)
- Find out the room the door leads to
- Rename the door tag
The Tool – Why F#?
While I love F# for many reasons, I found it particularly nice for building a Revit add-in for these three reasons:
- Type inference is awesome. The F# compiler is smart. Usually it deduces most variable types and function signatures without requiring you to explicitly declare them.
- Clean syntax is cool. In F# you don’t need curly braces or semicolons for delimiting code blocks. Like in python, this happens through indentation only. This eliminates much of the noise that takes up many lines of code in a C# program.
- List functions and lambdas rule. As you will see later, F# makes it easy to perform complex operations on lists and sequences.
For more information on why F# is awesome I recommend reading Scott Wlaschin’s “Why Use F#?” Series.
Need To Know: Revit Add-Ins
Before we start building our add-in it is important to get familiar with the general structure of such an add-in. We need to know that:
A Revit add-in can either be a Command or an Application. Commands (
Revit.DB.IExternalCommand) execute once when triggered. Applications (
Revit.DB.IExternalApplication) start on Revit startup and run until Revit is closed.
All the important meta-information is saved in xml files with the
.addin extension. These files are called add-in Manifests and contain the path to the add-in
dll and information like the author’s name or a unique GUID. One add-in Manifest file can contain information about multiple add-ins.
On startup Revit (2017) checks the addin folders
Users\[user]\AppData\Roaming\Autodesk\Revit\Addins\2017 for add-in manifests. It then tries to load the described add-in executables.
The Add-In: Rename Door Tags
To solve our problem we need to build two seperate add-ins:
- A command that iterates over every door in the project and changes their door tags
- An application that waits until a new doors is added and then changes its tag.
We start by building the command
When using Visual Studio, we need to add references to
RevitAPIUI.dll, both located in
C:\Program Files\Autodesk\Revit 2017\. We then start by building a simple class that implements the
namespace BuildInformed.LabelDoors open Autodesk.Revit.UI open Autodesk.Revit.DB open Autodesk.Revit.Attributes [<Transaction( TransactionMode.Manual )>] type LabelAll() = interface IExternalCommand with member x.Execute(commandData, message, elements) = // Our code goes here Result.Succeeded
<?xml version="1.0" encoding="utf-8"?> <RevitAddIns> <AddIn Type="Command"> <Name>Label all doors</Name> <Assembly>[path-to-executable]\LabelAll.dll</Assembly> <AddInId>3bff879c-291d-4673-87e1-1fdc6d6e3d57</AddInId> <FullClassName>BuildInformed.LabelDoors.LabelAll</FullClassName> <VendorId>BI</VendorId> <VendorDescription> Build Informed, www.buildinformed.com </VendorDescription> </AddIn> </RevitAddIns>
Access Revit Elements
We can now start adding functionality to our empty command. First of all, we need to get a list of all door instances in the active Revit Model. To achieve this, we use a
// retrieve document from the commandData passed to x.Execute let app = commandData.Application let doc = app.ActiveUIDocument.Document // get a list of all doors in the document let getDoors doc = use collector = (new FilteredElementCollector(doc)) .OfClass(typedefof<FamilyInstance>) .OfCategory(BuiltInCategory.OST_Doors) collector |> Seq.cast<FamilyInstance> |> Seq.toList
IEnumerable(and we already filtered for
FamilyInstances) we can cast the collector into an F# Sequence of
FamilyInstances. Thanks to F#’s smart type inference we don’t have to declare any type explicitly. The compiler knows that the
FilteredElementCollectorconstructor needs a
Documentand deduces that the argument of
getDoorshas to be a
Now all that’s left to do is change each door’s
mark property to the number of the door’s containing room.
A post about handling Events in Revit that follows up on this example will be available soon. It will released on our Blog and on our Social Media Channels.