Wednesday, May 25, 2011

Using LINQ with the Revit API

LINQ (or Language Integrated Query) can be used to filter collections retrieved using the Revit API.

For example, say you wanted to retrieve a list of category names of all elements that exist in the active view. To achieve this without LINQ you would:
  1. Select all elements in the active view (using a FilteredElementCollector).
  2. Loop through each element and:
    • Check that category is not null.
    • Check that you haven't already stored this category name (you don't want to see the Doors category 100 times).
    • Store the category in a list.
  3. Sort the list so that it is in alphabetical order.

Using LINQ this can be reduced to:
  1. Select all elements in the active view (using a FilteredElementCollector).
  2. Select all category names from the elements.
  3. Ensure the list of category names contains only distinct items and is in alphabetical order.

using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace KRSP.DistinctCategories
{
    [Transaction(TransactionMode.Automatic)]
    public class Command : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            UIApplication app = commandData.Application;
            UIDocument doc = app.ActiveUIDocument;
            View view = doc.ActiveView;

            //select all elements in the active view
            FilteredElementCollector allInView = new FilteredElementCollector(doc.Document, view.Id);
            allInView.WhereElementIsNotElementType();

            //use LINQ to collect the category names of all elements whilst ensuring that category is not null
            var categoryNames = from elem in allInView
                             where elem.Category != null
                             select elem.Category.Name;

            //strip out duplicates and order the list of category names
            categoryNames = categoryNames.Distinct().OrderBy(catName => catName);

            //construct a string list of the collected category names
            string catList = "Categories:";
            foreach (var catName in categoryNames)
            {
                catList += "\n" + catName;
            }

            //display a dialog showing the results.
            TaskDialog td = new TaskDialog("INFO");
            td.MainInstruction = string.Format("{0} Categor{1} Found.", categoryNames.Count(), categoryNames.Count() == 1 ? "y" : "ies");
            td.MainContent = catList;
            td.Show();

            return Result.Succeeded;
        }
    }
}

Lets examine the LINQ statement above:
//use LINQ to collect the category names of all elements whilst ensuring that category is not null
            var categoryNames = from elem in allInView
                             where elem.Category != null
                             select elem.Category.Name;
There are three parts to the query:
  1. The "from/in" clause; this is where you define which collection you are querying. In this example we are querying the "elem" items inside the "allInView" FilteredElementCollector.
  2. The "where" clause; this clause is optional and allows us to conditionally filter the selection. In this example we are only selecting those elements whose categories are not null.
  3. The "select" clause; this is where we state what we want to retrieve from the collection. In this example we are retrieving the "Category.Name" of each "elem" item.
The results of running the above code on a simple model are shown below:

1 comment: