We have looked at many aspects of categories in the past, such as

category comparison
,

family categories
,

categories and parameter bindings
, and

system versus user defined categories
.

Here is very basic question that just came in and may be of general interest:

Question: Is it possible to obtain a complete list of Revit family categories?

Answer: Yes, sure it is.
You can create it yourself.
There are two sets of categories in a Revit project document: the ones defined in the document itself, and the pre-defined built-in ones.

The top-level document ones are available from the document settings categories property, and the built-in ones from the BuiltInCategory enumeration.

I implemented a new Building Coder sample command CmdCategories to list the contents of these two collections.

This is the code to read the document categories.
The Categories object is a map that contains all the top-level Category objects within the Document:


  UIApplication app = commandData.Application;
  Document doc = app.ActiveUIDocument.Document;
  Categories categories = doc.Settings.Categories;
 
  int n = categories.Size;
 
  Debug.Print( "{0} categories and their parents:", n );
 
  foreach( Category c in categories )
  {
    Category p = c.Parent;
 
    Debug.Print( "  {0} ({1}), parent {2}",
      c.Name, c.Id.IntegerValue,
      (null == p ? "<none>" : p.Name) );
  }

This lists 219 categories in the sample project I tested it in, starting like this:


219 categories and their parents:
Plumbing Fixture Tags (-2005010), parent <none>
Switch System (-2008101), parent <none>
Structural Framing Tags (-2005015), parent <none>
Room Tags (-2000480), parent <none>
Raster Images (-2000560), parent <none>
Security Devices (-2008079), parent <none>
Structural Beam System Tags (-2005130), parent <none>

Since all of these are top-level categories, the Parent property is always null.
You can still ask the map for a sub-category by name or id and it should return it to you.
You can also extract the sub-categories of each parent category you iterate.

Here is the code iterating over the built-in categories listed in the BuiltInCategory enumeration:


  Array bics = Enum.GetValues(
    typeof( BuiltInCategory ) );
 
  n = bics.Length;
 
  Debug.Print( "{0} built-in categories and the "
    + "corresponding document ones:", n );
 
  Category cat;
  string s;
 
  foreach( BuiltInCategory bic in bics )
  {
    try
    {
      cat = categories.get_Item( bic );
 
      s = (null == cat)
        ? "<none>"
        : string.Format( "--> {0} ({1}",
          cat.Name, cat.Id.IntegerValue );
    }
    catch( Exception ex )
    {
      s = ex.GetType().Name + " " + ex.Message;
    }
    Debug.Print( "  {0} {1}", bic.ToString(), s );
  }

This lists 747 built-in categories and their corresponding document ones, if found.

I had to add an exception handler around the call to the Categories collection Item property taking a BuiltInCategory argument, since I discovered that it throws exceptions when called with certain built-in categories:

  • A InvalidOperationException with an empty message on the built-in categories
    • OST_FurnitureHiddenLines
    • OST_HostTemplate InvalidOperationException
    • OST_MassFaceSplitter InvalidOperationException
    • OST_MassCutter InvalidOperationException
    • OST_ZoningEnvelope InvalidOperationException
  • A NullReferenceException with a message saying “Object reference not set to an instance of an object” on
    • OST_InstanceDrivenLineStyle
    • OST_CurtainGridsSystem
    • OST_IOSNotSilhouette
    • OST_InvisibleLines

This is due to the fact that a few categories can’t be stored in the map correctly because their names are the same as other categories, and the name is used as a key.
A workaround might be: obtain the category from a new Categories object (from Settings.Categories), get the category directly by BuiltInCategory from that object.

Here is a
text file
including the output of running this command in my simple sample project.

Here is
version 2011.0.67.0
of The Building Coder sample source code and Visual Studio solution including the new command.

The Revit 2010 version of the Revit API introduction labs also included a command Lab2_5_Categories which performed some further analysis on the document and built-in categories and their relationships with each other.
The last version we published was in the discussion on

selecting model elements
.


Comments

10 responses to “Categories”

  1. Rajeswara Rao Avatar
    Rajeswara Rao

    Hello Jeremy Tammik,
    I am unable get the Category object of the BuiltInCategory.OST_SWallRectOpening using Document.Settings.Categories.get_Item(BuiltInCategory.OST_SWallRectOpening). Any idea?
    Thanks,
    Rajeswara Rao

  2. Dear Rajeswara,
    There is no guaranteed match between the list of built-in categories and the entries in the document settings Categories collection. In fact, they will never completely match.
    Some built-in categories will not have a corresponding instance in the document Categories collection, and also vice versa, there may be instances in the Categories collection that have no corresponding built-in category.
    For instance, when you insert a DWG file into the project, a new category is created for it which obviously lacks a matching built-in category:
    http://thebuildingcoder.typepad.com/blog/2008/11/adding-a-shared-parameter-to-a-dwg-file.html
    Your situation with a built-in category lacking a corresponding entry in the document categories may possibly change if you add some specific element to the model.
    What problem do you have with that?
    Cheers, Jeremy.

  3. Rajeswara Rao Avatar
    Rajeswara Rao

    Dear Jeremy,
    Thanks.
    I am trying to add/bind some Parameters to the BuiltInCategory.OST_SWallRectOpening category. This may nod be possible because this category will not allow to bound parameters.
    Thanks,
    Rajeswara Rao

  4. Weird question: Where does the “Multi-category” category fit in the equation? Is it a real top-level category, or some hybrid category? Is it possible to change, for example, an annotation tag from “furniture” category to a “multi-category”?
    Thanks for the brains

  5. Dear NearlyNil,
    Thank you for your weird question, and thank God for the brains!
    I have no idea, I never heard of multi-category. It may be that I just never encountered it, or that it does not exist in the API, and is just a user interface artifact. Where do you observe such a thing? I would suggest exploring the issue yourself using RevitLookup.
    Cheers, Jeremy.

  6. built-in category storage
    Hi Jeremy,
    I want to add specific built-in categories to a list and store these as a schema in the projectinformation category. I have been able to add strings using the stringlist.add() method however Revit is reporting an error when I used “builtincategory as the type.”
    So my question is if I cant use that type how can i add built-in cats to revit data storage? What is the easiest method if there is one to store and retrieve the builtincategories such as walls, floors etc.?
    Your blog has been quite helpful btw!
    cheers,
    alex
    public void StoreDataInProjectInfo(Document doc, Element projectInfoElement)
    {
    Schema schema = Schema.Lookup(SchemaGuid);
    List stringList = new List();
    stringList.Add(BuiltInCategory.OST_Walls);
    stringList.Add(BuiltInCategory.OST_Floors);
    using (Transaction transEstorage = new Transaction(doc))
    {
    transEstorage.Start(“add estorage list”);
    if (null == schema)
    {
    SchemaBuilder sb = new SchemaBuilder(new Guid(“AF5E4C3E-C2E2-493B-8236-BA0F5E323887”));
    //public accesibility
    sb.SetReadAccessLevel(AccessLevel.Public);
    sb.SetWriteAccessLevel(AccessLevel.Public);
    //Storage Filled for Cat List
    FieldBuilder fb = sb.AddArrayField(“UserCategoryList”, typeof(BuiltInCategory));
    fb.SetDocumentation(“A Set Of Categories to be worksetted”);

  7. Dear Alex,
    All enumerations are basically integers, so you could convert them to that.
    There is also a certain amount of exchangeability between built-in categories and non-built-in categories defined in the project file. The latter have element ids. Therefore, you can also consider the built-in category integer value as an element id, albeit with a negative value.
    So you might use either integer or ElementId data type.
    Please let us know how you fare.
    Thank you and good luck!
    Cheers, Jeremy.

  8. Hi Jeremy,
    I am trying to write a little macro (pasted below) that will loop through the view templates and list out the various graphic overrides (model) that may exist. I get the views ok, and list the name, scale and type. I then was thinking (based on looking at the members of View) that by using GetCategoryOverrides I can list out how the various categories are modified by the template. This apparently is not the case, or the way I am going about it is misguided. My attempt to get a filtered element collection of Categories fails at runtime. here is the code and thanks for any insight you can provide:
    public void ViewTemplateList()
    {
    Document doc = this.ActiveUIDocument.Document;
    FilteredElementCollector views = new FilteredElementCollector(doc).OfClass(typeof(Autodesk.Revit.DB.View));
    SaveFileDialog saveFile = new SaveFileDialog();
    saveFile.FileName = “Templates.txt”;
    saveFile.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
    if(saveFile.ShowDialog() == DialogResult.OK)
    {
    StreamWriter sw = new StreamWriter(saveFile.FileName);
    try
    {
    foreach (Autodesk.Revit.DB.View v in views)
    {
    string line = “”;
    if (v.IsTemplate)
    {
    line += v.Name;
    line += “\t”;
    line += v.Scale.ToString();
    line += “\t”;
    line += v.ViewType.ToString();
    FilteredElementCollector cCats = new FilteredElementCollector(doc).OfClass(typeof(Categories));
    foreach (Element c in cCats)
    {
    OverrideGraphicSettings ogs = v.GetCategoryOverrides(c.Id);
    line += “\t”;
    line += c.Name;
    line += “\t”;
    line += ogs.CutFillColor;
    }
    sw.WriteLine(line);
    }
    }
    sw.Close();
    }
    catch(Exception ex)
    {
    MessageBox.Show(ex.Message);
    sw.Close();
    }
    }
    }

  9. Oops . . . I notice that I should not be using Categories as elements, and am now setting cCats as you have above: Categories cCats = doc.Settings.Categories;
    so my new inner foreach loop looks like this:
    foreach (Category c in cCats)
    {
    if(c.get_AllowsVisibilityControl(v))
    {
    OverrideGraphicSettings ogs = v.GetCategoryOverrides(c.Id);
    line += “\t”;
    line += c.Name;
    line += “\t”;
    line += ogs.CutFillColor;
    }
    }
    Now I get all categories, but I really just need the ones that are overridden . . . I don’t see a way to filter or test on that. Any ideas?
    Thanks, Paul

  10. Dear Paul,
    I checked with the development team for you, and they reply:
    I don’t think we offer a shortcut to do this. GetCategoryOverrides() returns a default overrides object if it’s not overridden.
    I hope this helps.
    Cheers, Jeremy.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading