Select Model Elements 2

We already had several stabs at the topic of selecting all model elements, i.e. all elements in a Revit BIM which contribute to the building geometry. Their main characteristic is that they have a non-null geometry which is visible on the graphics screen and really contributes to the physical building model. Unfortunately, some elements which are not part of the building itself also have valid geometry and need to be eliminated.

The latest version of the code that we presented still made use of a full iteration over all document elements, i.e. something like this:

  Application app = commandData.Application;
  Document doc = app.ActiveDocument;
 
  ElementIterator it = doc.Elements;
 
  while( it.MoveNext() )
  {
    Element e = it.Current as Element;
    // . . .
  }

As we all know by now, this kind of full iteration over all document elements is unnecessarily time consuming and seldom useful, because an application is almost always only interested in a small subset of them. We will convert the model element selection to use element filtering, similarly to the conversion to use filters we described for the Revit SDK FrameBuilder sample.

We can add both a type filter to eliminate a list of classes that we are not interested in, and also a built-in category filter to eliminate unwanted categories. Here is the list of unwanted types, i.e. classes of elements which are not model elements:

static Type[] _types_to_skip = new Type[] {
  typeof( BasePoint ),
  typeof( DetailLine ),
  typeof( Dimension ),
  typeof( Family ),
  typeof( FamilyBase ),
  typeof( FillPattern ),
  typeof( gbXMLParamElem ),
  typeof( GraphicsStyle ),
  typeof( Level ),
  typeof( LinePattern ),
  typeof( MaterialOther ),
  typeof( ModelLine ),
  typeof( Phase ),
  typeof( PrintSetting ),
  typeof( ProjectInfo ),
  typeof( ProjectUnit ),
  typeof( ReferencePlane ),
  typeof( Room ),
  typeof( RoomTag ),
  typeof( Sketch ),
  typeof( SketchPlane ),
  typeof( Symbol ),
  typeof( TextNote ),
  typeof( ViewDrafting ),
  typeof( ViewPlan ),
  typeof( ViewSheet ),
  typeof( WallType ),
};

If you run into some additional unwanted element in your model that is not listed here, you can easily add it. You might want to let us know as well by submitting a comment on it.

We define the following helper method to more succinctly create a type filter which eliminates all of these classes. Given an existing type filter 'f', it adds another type 't' to it and returns the new expanded filter:

static Filter OrType(
  Filter f,
  Type t,
  Autodesk.Revit.Creation.Filter cf )
{
  Filter f2 = cf.NewTypeFilter( t );
  return cf.NewLogicOrFilter( f, f2 );
}

We can make use of it to implement the first step of our external command Execute method like this:

Application app = commandData.Application;
Document doc = app.ActiveDocument;
 
Autodesk.Revit.Creation.Filter cf
  = app.Create.Filter;
 
List<Element> els = new List<Element>();
Filter f = cf.NewTypeFilter( typeof( Symbol ) );
foreach( Type t in _types_to_skip )
{
  f = OrType( f, t, cf );
}
f = cf.NewLogicNotFilter( f );
int n = doc.get_Elements( f, els );

In addition to the unwanted types, we know some built-in categories whose elements also do not contribute to the building geometry and should be eliminated, for instance:

static int[] _bics_to_skip = new int[] {
  ( int ) BuiltInCategory.OST_PreviewLegendComponents,
  ( int ) BuiltInCategory.OST_IOSSketchGrid,
  ( int ) BuiltInCategory.OST_Cameras,
};

We have converted them to integer values to make use of the language independent category comparison. The following helper method encapsulates the code to determine whether a given built-in category integer value is included in this list and the associated element thus needs to be skipped. It makes use of the generic Array.Exists method:

static bool SkipThisBic( int bic )
{
  return Array.Exists<int>(
    _bics_to_skip,
    delegate( int i )
    {
      return i == bic;
    } );
}

With these preparations in place, we can iterate over and list the model elements like this in the rest of the implementation of the external command Execute method:

string s = string.Empty;
n = 0;
 
Geo.Options opt = app.Create.NewGeometryOptions();
 
foreach( Element e in els )
{
  if( (null != e.Category)
    && !SkipThisBic( e.Category.Id.Value )
    && (null != e.get_Geometry( opt )) )
  {
    ++n;
    s += string.Format( 
      "\r\n  Category={0}; Name={1}; Id={2}",
      e.Category.Name, e.Name, e.Id.Value );
  }
}
 
s = string.Format(
  "Project contains {0} model element{1}{2}",
  n,
  ( 1 == n ? "" : "s" ),
  ( 0 == n ? "." : ":" ) )
+ s;
 
LabUtils.InfoMsg( s );
 
return CmdResult.Failed;

Here is today's snapshot of the updated version of the Revit API introduction labs, including the version of Lab 2-2 to select all model elements as described above.