next previous home

3. Families and Types

3-1 List all loaded families and types

In this section, we list all loaded families and family instances in the current document. Each family defines a set of types, also called symbols. To list them, we retrieve all the objects wrapping standard families and types loaded and available in the current model. Let's first see how we can get all family objects and try to determine their category. Add a new module named Labs3 to the project and a standard command class implementation Lab3_1_StandardFamiliesAndTypes to it:

using System;
using System.Collections;
using WinForms = System.Windows.Forms;
using Autodesk.Revit;
using Autodesk.Revit.Elements;
using Autodesk.Revit.Parameters;
using Autodesk.Revit.Symbols;

namespace Labs
{
  public class Lab3_1_StandardFamiliesAndTypes : IExternalCommand
  {
    public IExternalCommand.Result Execute(
      ExternalCommandData commandData,
      ref string message,
      ElementSet elements )
    {
      return IExternalCommand.Result.Succeeded;
    }
  }
}
Public Class Lab3_1_StandardFamiliesAndTypes
    Implements IExternalCommand

    Public Function Execute( _
        ByVal commandData As ExternalCommandData, _
        ByRef message As String, _
        ByVal elements As ElementSet) _
    As IExternalCommand.Result _
    Implements Autodesk.Revit.IExternalCommand.Execute

        Return IExternalCommand.Result.Succeeded

    End Function
End Class

Unlike previous versions of Revit where we had to iterate through all the elements in Revit and create a list of family elements, from Revit 2009 onwards, we can use the element filtering mechanism to filter out all the family elements. This not only reduces the number of lines of code required to achieve the same result, but also has tremendous performance advantage as compared to previous releases. Insert the following lines of code to the class implementation to create a list of family elements using the filtering mechanism.

    Application app = commandData.Application;
    Document doc = app.ActiveDocument;
    //
    // get all family elements in current document:
    //
    List<Element> families = new List<Element>();
    Filter filterFamily = app.Create.Filter.NewTypeFilter( typeof( Family ) );
    doc.get_Elements( filterFamily, families );
    string sMsg = "Standard families already loaded in this model:";
    foreach( Family f in families )
    {
      // Get its category name; notice that the Category property is not
      // implemented for the Family class; use FamilyCategory instead;
      // notice that that is also not always implemented; in that case,
      // use the workaround demonstrated below, looking at the contained
      // family symbols' category:
      sMsg += "\r\n  Name=" + f.Name
        + "; Category=" + ( ( null == f.Category ) ? "?" : f.Category.Name )
        + "; FamilyCategory=" + ( ( null == f.FamilyCategory ) ? "?" : f.FamilyCategory.Name );
    }
    LabUtils.InfoMsg( sMsg );
    ' Element iteration done with element filtering functionality in Revit 2009
    Dim doc As Revit.Document = commandData.Application.ActiveDocument
    Dim elementList As New List(Of Revit.Element)
    Dim filterType As Revit.Filter = commandData.Application.Create.Filter.NewTypeFilter(GetType(Family))
    Dim nRetVal As Integer = doc.Elements(filterType, elementList)
    Dim sMsg As String = "Standard Families already loaded in this model are:"
    Dim f As Family
    For Each f In elementList
        ' get its category name; notice that the category property is
        ' not implemented for the Family class. use FamilyCategory
        ' instead, which is also not always implemented:

        Dim catName As String
        If f.Category Is Nothing Then
            catName = "?"
        Else
            catName = f.Category.Name
        End If

        Dim famCatName As String
        If f.FamilyCategory Is Nothing Then
            famCatName = "?"
        Else
            famCatName = f.FamilyCategory.Name
        End If

        sMsg += vbCrLf & "  Name=" & f.Name _
            & "; Category=" & catName _
            & "; FamilyCategory=" & famCatName
    Next
    MsgBox(sMsg)

Compile the project, adjust the ini file and run the command. As we can see from the message box, all the categories are listed as "?", showing that this property is not implemented for family class.

Because the Category property is not implemented for family objects, and it is often useful to determine a family instance's category, the Revit API has defined an additional property FamilyCategory to query it. Unfortunately, the value of this property is defined in the content, and some legacy content does not specify a value for it, so even this property does not always reliably return the value you might expect.

In order to reliably determine a family's category, we can retrieve the contained symbols or types and determine their category. The following code demonstrates this and also reports all types available in each family, displaying one message box per family. Add the following between the previous loop and the return statement:

    // Loop through the collection of families, and now look at
    // the child symbols (types) as well. These symbols can be
    // used to determine the family category.
    foreach( Family f in families )
    {
      string catName;
      bool first = true;
      // Loop all contained symbols (types)
      foreach( FamilySymbol symb in f.Symbols )
      {
        // you can determine the family category from its first symbol.
        if( first )
        {
          first = false;
          catName = symb.Category.Name;
          sMsg = "Family: Name=" + f.Name
            + "; Id=" + f.Id.Value.ToString()
            + "; Category=" + catName
            + "\r\nContains Types:";
        }
        sMsg += "\r\n    " + symb.Name + "; Id=" + symb.Id.Value.ToString();
      }
      // Show the symbols for this family and allow user to proceed
      // to the next family (OK) or cancel (Cancel)
      sMsg += "\r\nContinue?";
      if( !LabUtils.QuestionMsg( sMsg ) )
      {
        break;
      }
    }
    ' Let's do a similar loop, but now get all the child symbols (types) as well.
    ' These symbols can also be used to determine the category:

    For Each f In elementList
        Dim catName As String
        Dim first As Boolean = True
        Dim symb As FamilySymbol
        'Loop all contained symbols (types)
        For Each symb In f.Symbols
            ' Determine the category via first symbol
            If first Then
                first = False
                If (symb.Category Is Nothing) Then
                    catName = "?"  ' Still happens for *some* Symbols (Profiles?)
                Else
                    catName = symb.Category.Name
                End If

                sMsg = "Family: Name=" & f.Name & "; Id=" & f.Id.Value.ToString & "; Category=" & catName & vbCrLf & "Contains Types:"
            End If
                sMsg += vbCrLf & "    " & symb.Name & "; Id=" & symb.Id.Value.ToString
        Next

        ' Show the symbols for this family and allow user to procede to the next family (OK) or cancel (Cancel)
        If MsgBox(sMsg, MsgBoxStyle.OkCancel) = MsgBoxResult.Cancel Then
            Exit For
        End If
    Next

Now a valid category name is reported for all families. The looping continues displaying all types contained in each family in one message box each as long as you click 'OK'. Click 'Cancel' to terminate.

next previous home copyright © 2007-2008 jeremy tammik, autodesk inc. all rights reserved.