Category Comparison and Model Elements Revisited

This is a follow-up to two previous posts, on category comparison and the selection of model elements.

Regarding the category comparison, we discussed the fact that categories cannot be compared directly, and one should use their element id instead. We also underlined that every category comparison should be made language independent by using the built-in category enumeration and not comparing the localised category names. The analysis of the model element selection examined how to eliminate the preview legend components from the selected elements, since they suddenly acquired geometry in Revit 2010 and also fit the other criteria applied in the selection process.

As Miroslav Schonauer pointed out, the model element selection that I suggested in that post will not work, because various model elements exist that do not have a valid level assigned to them. He suggests that the previous selection algorithm with an additional check added for the preview legend components probably remains the simplest and most reliable method.

One deplorable aspect of the code presented there with the preview legend component check added is its language dependence. It compares the element category name with the preview legend component one, which is "Legend Components". I left that in there because I thought I would use the alternative approach anyway, checking for a valid level.

So now the issue is how to compare a given element's category against a built-in one without using the category name. In previous similar situations, I achieved this by asking the document for the built-in category object and using its element id. The Revit developers guide includes some sample code which demonstrates an extremely effective alternative method. It avoids the string comparison and achieves language independence, and it also does not require the effort of querying the document settings for the element id of a built-it category.

For built-in categories, the value of the element id is exactly the same as the integer resulting from casting the built-in category enumeration value. The section 5.2.1 Category discusses details of the element category classification and explains:

An element's category is determined by the Category ID.

The Code Region 14-1: 'Distinguishing permanent dimensions from constraints' in section 14.1 'Dimensions and Constraints' shows that we can actually use the built-in enum value to compare with an element category id directly, as in the following excerpted lines:

  if ( (int) BuiltInCategory.OST_Dimensions
    == dimension.Category.Id.Value )
  {
    message += "\nDimension is a permanent dimension.";
  }

Making use of this for the model element selection, we can implement the following filter which is both language independent and efficient:

BuiltInCategory _bicPreviewLegendComponent 
  = BuiltInCategory.OST_PreviewLegendComponents;
 
public CmdResult Execute(
  ExternalCommandData commandData,
  ref string message,
  ElementSet elements )
{
  Application app = commandData.Application;
  Document doc = app.ActiveDocument;
 
  ElementIterator it = doc.Elements;
  string s = string.Empty;
  int count = 0;
 
  Geo.Options opt = app.Create.NewGeometryOptions();
 
  int iBic = (int) _bicPreviewLegendComponent;
 
  while( it.MoveNext() )
  {
    Element e = it.Current as Element;
 
    if ( !(e is Symbol)
      && !(e is FamilyBase)
      && (null != e.Category)
      && ( iBic != e.Category.Id.Value )
      && (null != e.get_Geometry( opt )) )
    {
      ++count;
      s += string.Format( 
        "\r\n  Category={0}; Name={1}; Id={2}",
        e.Category.Name, e.Name, 
        e.Id.Value.ToString() );
    }
  }
 
  s = "There are " + count.ToString() 
    + " model elements:" + s;
 
  LabUtils.InfoMsg( s );
 
  return CmdResult.Failed;
}

Here is a snapshot of an updated version of the Revit API introduction labs, including the updated version of Lab 2-2 to select all model elements using this criterion and other on-going improvements we have made. In particular, it includes two completely new modules, Labs6 dealing with external application and event issues, and Labs7 demonstrating the form creation API. We are still continuing the process of updating and enhancing them for the Revit 2010 API and its new features. All of the labs are provided in two versions, one each for C# and VB. This version also includes a set of HTML instructions for self-learning and AdnSamples.txt, an include file for RvtSamples that I use to load all my external commands, updated to the 2010 RvtSamples format.

Revit Inside

By the way, if you are interested in seeing who else is using Revit, there is a Revit Inside website which tells you just that. The people running the list are always interested in new entries as well, so don't hesitate to check it out and sign up if it applies to you.