This is in response to a query from Berria on iterating over the railing types in the model. It is also once again about teaching how to fish, and not just feeding people, at least I hope so. If I want to list all the railing types, the best way to go is probably to define an API filter which retrieves them from the model for me. How do I design that filter? I start by opening a new model, inserting a railing, and examining it with RvtMgdDbg. There I can easily determine that its built-in category is OST_StairsRailing. Therefore, I first thought that an API filter selecting all family symbols of this category should suffice. The project browser shows me the following stair and railing symbols in my current model:
I implemented a new external command CmdListRailingTypes to test this approach, with the following code:
Application app = commandData.Application; Document doc = app.ActiveDocument; CreationFilter cf = app.Create.Filter; List<Element> symbols = new List<Element>(); BuiltInCategory bic = BuiltInCategory.OST_StairsRailing; Filter f1 = cf.NewCategoryFilter( bic ); Filter f2 = cf.NewTypeFilter( typeof( FamilySymbol ) ); doc.get_Elements( f, symbols ); foreach( FamilySymbol s in symbols ) { Debug.Print( "Family name={0}, symbol name={1}", s.Family.Name, s.Name ); } return CmdResult.Failed;
It makes use of a new alias for CreationFilter:
using CreationFilter = Autodesk.Revit.Creation.Filter;
To my initial surprise, no symbols were found. I thereupon took a closer look at the symbols within the railings family in RvtMgdDbg and discovered that the category of the M_Baluster symbols is not OST_StairsRailing but OST_StairsRailingBaluster. Changing the value of the 'bic' variable to that built-in category returns some valid results:
Family name=M_Baluster - Square, symbol name=25mm Family name=M_Baluster - Square, symbol name=20mm Family name=M_Baluster - Round, symbol name=25mm Family name=M_Baluster - Round, symbol name=20mm
This does still not include all the railing symbols listed in the browser, so I went back into RvtMgdDbg and searched for those as well. They were not listed in the FamilySymbol collection, but under Symbols, this time indeed with the category OST_StairsRailing.
Making use of this info, I implemented a second filter and iteration to retrieve and list Symbol objects like this:
bic = BuiltInCategory.OST_StairsRailing; f1 = cf.NewCategoryFilter( bic ); f2 = cf.NewTypeFilter( typeof( Symbol ) ); f = cf.NewLogicAndFilter( f1, f2 ); doc.get_Elements( f, symbols ); n = symbols.Count; Debug.Print( "{0}" + " OST_StairsRailing symbol{1}:", n, Util.PluralSuffix( n ) ); foreach( Symbol s in symbols ) { FamilySymbol fs = s as FamilySymbol; Debug.Print( "Family name={0}, symbol name={1}", null == fs ? "<none>" : fs.Family.Name, s.Name ); }
The result of this code is somewhat surprising too. The non-family symbols that we are looking for are retrieved now, just like we hoped, and we also retrieve the family symbols that we already saw above:
6 OST_StairsRailing symbols: Family name=M_Baluster - Square, symbol name=25mm Family name=M_Baluster - Square, symbol name=20mm Family name=M_Baluster - Round, symbol name=25mm Family name=M_Baluster - Round, symbol name=20mm Family name=<none>, symbol name=900mm Pipe Family name=<none>, symbol name=900mm Rectangular
This goes to show several things:
- You need to explore the model.
- You may get unexpected results.
I'll just leave it at that for now.
Here is version 1.0.0.25 of the complete Visual Studio solution with the new CmdListRailingTypes command implementation.