Getting filtered elements or element ids

Getting filtered elements or element ids

Once one or more filters have been applied to the FilteredElementCollector, the filtered set of elements can be retrieved in one of three ways:

  1. Obtain a collection of Elements or ElementIds.
    • ToElements() - returns all elements that pass all applied filters
    • ToElementIds() - returns ElementIds of all elements which pass all applied filters
  2. Obtain the first Element or ElementId that matches the filter.
    • FirstElement() - returns first element to pass all applied filters
    • FirstElementId() - returns id of first element to pass all applied filters
  3. Obtain an ElementId or Element iterator.
    • GetElementIdIterator() - returns FilteredElementIdIterator to the element ids passing the filters
    • GetElementIterator() - returns FilteredElementIterator to the elements passing the filters
    • GetEnumerator() - returns an IEnumerator<Element> that iterates through collection of passing elements

You should only use one of the methods from these groups at a time; the collector will reset if you call another method to extract elements. Thus, if you have previously obtained an iterator, it will be stopped and traverse no more elements if you call another method to extract elements.

Which method is best depends on the application. If just one matching element is required, FirstElement() or FirstElementId() is the best choice. If all the matching elements are required, use ToElements(). If a variable number are needed, use an iterator.

If the application will be deleting elements or making significant changes to elements in the filtered list, ToElementIds() or an element id iterator are the best options. This is because deleting elements or making significant changes to an element can invalidate an element handle. With element ids, the call to Document.GetElement() with the ElementId will always return a valid Element (or a null reference if the element has been deleted).

Using the ToElements() method to get the filter results as a collection of elements allows for the use of foreach to examine each element in the set, as is shown below:

Code Region 6-9: Using ToElements() to get filter results

// Use ElementClassFilter to find all loads in the document
// Using typeof(LoadBase) will yield all AreaLoad, LineLoad and PointLoad
ElementClassFilter filter = new ElementClassFilter(typeof(LoadBase));

// Apply the filter to the elements in the active document
FilteredElementCollector collector = new FilteredElementCollector(document);
collector.WherePasses(filter);
ICollection<Element> allLoads = collector.ToElements();

String prompt = "The loads in the current document are:\n";
foreach (Element loadElem in allLoads)
{
        LoadBase load = loadElem as LoadBase;
        prompt += load.GetType().Name +  ": " + 
                        load.Name + "\n";
}

TaskDialog.Show("Revit", prompt);

When just one passing element is needed, use FirstElement():

Code Region 6-10: Get the first passing element

// Create a filter to find all columns
StructuralInstanceUsageFilter columnFilter = 
        new StructuralInstanceUsageFilter(StructuralInstanceUsage.Column);

// Apply the filter to the elements in the active document
FilteredElementCollector collector = new FilteredElementCollector(document);
collector.WherePasses(columnFilter);

// Get the first column from the filtered results
// Element will be a FamilyInstance
FamilyInstance column = collector.FirstElement() as FamilyInstance;

In some cases, FirstElement() is not sufficient. This next example shows how to use extension methods to get the first non-template 3D view (which is useful for input to the ReferenceIntersector constructors).

Code Region 6-11: Get first passing element using extension methods

// Use filter to find a non-template 3D view
// This example does not use FirstElement() since first filterd view3D might be a template
FilteredElementCollector collector = new FilteredElementCollector(document);
Func<View3D, bool> isNotTemplate = v3 => !(v3.IsTemplate);

// apply ElementClassFilter
collector.OfClass(typeof(View3D));

// use extension methods to get first non-template View3D
View3D view3D = collector.Cast<View3D>().First<View3D>(isNotTemplate);

The following example demonstrates the use of the FirstElementId() method to get one passing element (a 3d view in this case) and the use of ToElementIds() to get the filter results as a collection of element ids (in order to delete a set of elements in this case).

Code Region 6-12: Using Getting filter results as element ids

FilteredElementCollector collector = new FilteredElementCollector(document);

// Use shortcut OfClass to get View elements
collector.OfClass(typeof(View3D));

// Get the Id of the first view
ElementId viewId = collector.FirstElementId();

// Test if the view is valid for element filtering
if (FilteredElementCollector.IsViewValidForElementIteration(document, viewId))
{
        FilteredElementCollector viewCollector = new FilteredElementCollector(document, viewId);

        // Get all FamilyInstance items in the view
        viewCollector.OfClass(typeof(FamilyInstance));
        ICollection<ElementId> familyInstanceIds = viewCollector.ToElementIds();

        document.Delete(familyInstanceIds);
}

The GetElementIterator() method is used in the following example that iterates through the filtered elements to check the flow state of some pipes.

Code Region 6-13: Getting the results as an element iterator

FilteredElementCollector collector = new FilteredElementCollector(document);

// Apply a filter to get all pipes in the document
collector.OfClass(typeof(Autodesk.Revit.DB.Plumbing.Pipe));

// Get results as an element iterator and look for a pipe with
// a specific flow state
FilteredElementIterator elemItr = collector.GetElementIterator();
elemItr.Reset();
while (elemItr.MoveNext())
{
        Pipe pipe = elemItr.Current as Pipe;
        if (pipe.FlowState == PipeFlowState.LaminarState)
        {
                TaskDialog.Show("Revit", "Model has at least one pipe with Laminar flow state.");
                break;
        }
}

Alternatively, the filter results can be returned as an element id iterator:

Code Region 6-14: Getting the results as an element id iterator

// Use a RoomFilter to find all room elements in the document. 
RoomFilter filter = new RoomFilter();

// Apply the filter to the elements in the active document
FilteredElementCollector collector = new FilteredElementCollector(document);
collector.WherePasses(filter);

// Get results as ElementId iterator
FilteredElementIdIterator roomIdItr = collector.GetElementIdIterator();
roomIdItr.Reset();
while (roomIdItr.MoveNext())
{
        ElementId roomId = roomIdItr.Current;
        // Warn rooms smaller than 50 SF
        Room room = document.GetElement(roomId) as Room;
        if (room.Area < 50.0)
        {
                String prompt = "Room is too small: id = " + roomId.ToString();
                TaskDialog.Show("Revit", prompt);
                break;
        }
}
In some cases, it may be useful to test a single element against a given filter, rather than getting all elements that pass the filter. There are two overloads for ElementFilter.PassesFilter() that test a given Element, or ElementId, against the filter, returning true if the element passes the filter.