Filtering for Family Instances and Types by Family Name
August 17, 2017
By Jeremy Tammik
[An[ issue in the Revit API discussion forum thread on implementing a family instance filter led to a little update and new release 2018.0.134.2 of The Building Coder samples:
Question: I want to retrieve a list of all Family Instances by Type name. As the Type name is not unique (i.e. "Type 1") and can be used repeatedly by different Families, I need to also specify the family name.
For instance, there are two title block families: TB1 and TB2; both of them have a type named 60" x 30" Student.
I would like to write a single filter statement that collects only TB2:60" x 30" Student instances.
It seems counter intuitive to run a foreach over your results to check the family type.
Answer: You can achieve the filter you ask for in several ways.
The easiest and slowest is to use .NET post-processing or LINQ.
That is equivalent to running a foreach loop over the results of the filter.
Revit returns a large collection of instances, which costs time and marshalling effort, and .NET post-processes them.
A much faster alternative, but a little bit more complex, is to implement a parameter filter.
You can apply any filters you like in any order you like.
Revit may perform some optimisation by reordering them.
It is probably useful to apply all quick filters first.
Using ToList to convert the enumerable filtered element collector to a .NET List is normally not necessary and may cause a significant inefficiency.
Check out the examples provided by The Building Coder samples in the module CmdCollectorPerformance.cs
You could filter for families first, then determine their types, and then the instances referring to those.
In fact, the CmdCollectorPerformance.cs module includes code demonstrating that approach.
However, since the elements you are after in the end are the instances, it makes sense to filter for those right away, and then eliminate the ones that don't match the expected family.
Here are the two functions using LINQ post-processing that I now added to The Building Coder samples to answer this question once and for all:
#region Retrieve all family instances of specific named family and type /// <summary> /// Get instances by family name then type name /// </summary> static IEnumerable<FamilyInstance> GetFamilyInstancesByFamilyAndType( Document doc, string familyName, string typeName ) { return new FilteredElementCollector( doc ) .OfClass( typeof( FamilyInstance ) ) .Cast<FamilyInstance>() .Where( x => x.Symbol.Family.Name.Equals( familyName ) ) // family .Where( x => x.Name.Equals( typeName ) ); // family type } #endregion // Retrieve all family instances of specific named family and type #region Return first title block family symbol of specific named family and type /// <summary> /// Get title block family symbol (= definition) /// by family name then type name /// </summary> static FamilySymbol GetTitleBlockSymbolByFamilyAndType( Document doc, string familyName, string typeName ) { return new FilteredElementCollector( doc ) .OfClass( typeof( FamilySymbol ) ) .OfCategory( BuiltInCategory.OST_TitleBlocks ) .Cast<FamilySymbol>() .Where( x => x.FamilyName.Equals( familyName ) ) // family .FirstOrDefault( x => x.Name == typeName ); // family type } #endregion // Return first title block family symbol of specific named family and type
Implementing dedicated parameter filters for the family and type name would be much more efficient, and would avoid Revit having to marshal and send across to .NET all the data for the instances or types that do not match the desired criteria.
The code region to retrieve named family symbols demonstrates several different approaches using both LINQ and the more efficient parameter filter.
You can implement similar code to retrieve family instances instead of symbols.
There's more information available on the The Building Coder blog.
No comments:
Post a Comment