Найти тему

Класс FilteredElementCollector. Получение элементов для дальнейшей работы

В сегодняшней статье рассмотрим очень важный класс — FilteredElementCollector. Этот класс позволяет нам взять некоторые (или все) элементы из документа, чтобы потом делать с ними определённые действия.

При работе в Dynamo его заменяют ноды "All Elements Of Category", "All Elements Of Type" и некоторые другие. В C# же их всех заменяет FilteredElementCollector.

Для начала внимательно изучим описание класса на revitapidocs.com. Чтобы начать работать с ним, нам надо создать его экземпляр с помощью одного из 3 конструкторов:

Конструкторы класса FilteredElementCollector
Конструкторы класса FilteredElementCollector

Первый принимает документ (сбор элементов по всему документу), второй — документ и ElementId вида (сбор элементов из документа на данном виде), третий — документ и коллекцию ElementId (фильтрация предварительно собранных элементов).

Как передать документ в нашу команду — вспомним из статьи о создании плагина. Далее мы можем создать экземпляр FilteredElementCollector:

Создаём FilteredElementCollector в строке 21
Создаём FilteredElementCollector в строке 21

В данном случае вместо явного указания типа я использую ключевое слово var. Это означает, что компилятор сам определит тип переменной. В данном случае я делаю так, потому что не знаю конечный тип переменной: будет ли это List, ICollection или что-то ещё, будет ли он состоять из Element, ElementId или я выполню приведение к конкретному типу (например, к Wall). Но иногда это ключевое слово используют для сокращения кода:

Строка var collector = new FilteredElementCollector(doc); гораздо короче, чем FilteredElementCollector collector = new FilteredElementCollector(doc);, при этом в обоих случаях тип переменной понятен (до вызова дополнительных методов).

Теперь рассмотрим методы этого класса из справки:

Раскроем список Methods, выделим любой и ознакомимся с его описание
Раскроем список Methods, выделим любой и ознакомимся с его описание

Данные методы помогают нам применять фильтры к коллектору, объединять отфильтрованные элементы с другими, переводить коллектор в список элементов или их Id, проходить по коллектору в цикле foreach. Рассмотрим наиболее важные из них.

1. public FilteredElementCollector OfCategory(
BuiltInCategory category )

Метод OfCategory принимает значение из enumeration (перечисление) BuiltInCategory и возвращает нам список всех элементов этой категории. То есть мы передаём ему не имя категории, а встроенное имя категории в OST_-формате (независимое от языка Revit). Как же его узнать?

1. Зная название категории на английском, перейти на страницу BuiltInCategory, нажать Ctrl-F, написать его и найти категорию:

Нашли стены (OST_Walls)
Нашли стены (OST_Walls)

2. Выделить элемент и посмотреть в Revit Lookup.

Нашли двери (OST_Doors)
Нашли двери (OST_Doors)

Далее синтаксис такой:

Строка 22
Строка 22

Передаём в метод название перечисления BuiltInCategory, затем через точку — элемент перечисления.

2. public FilteredElementCollector OfCategoryId(
ElementId categoryId )

Метод возвращает тоже самое — все элементы категории, но принимает Id категории (будьте внимательны, он всегда со знаком минус, выделить Категорию внутри Ревита нельзя).

Узнаём через Lookup:

Id отрицательный
Id отрицательный

Синтаксис такой:

Строка 22
Строка 22

Вообще, в конструктор для ElementId можно передать и BuiltInCategory — VisualStudio предложит вам такой вариант. И да, он сработает:

И снова строка 22
И снова строка 22

На самом деле, OST_Doors имеет внутри этот самый шифр -2000023. Как это устроено — читайте здесь.

3. public FilteredElementCollector OfClass(
Type
type )

Собирает элементы класса. В чём разница c двумя предыдущими? Объясню на примерах:

  • Есть категория Стены и класс Wall. Но первые 2 метода вернут нам и экземпляры, и типы стен в одном списке (тип элемента — тоже элемент), и, например, когда мы попытаемся во все элементы этого списка в параметр "Комментарии" написать значение, то получим ошибку, ведь у типа такого параметра нет.
  • Класс может охватывать несколько категорий. MEPCurve — это трубы, лотки и воздуховоды. Так мы можем взять их все в одно действие.
  • Элемент может не иметь категории (например, общий параметр — SharedParameterElement)

В общем, класс — это совсем не категория. Синтаксис метода такой:

Наша любимая строка 22
Наша любимая строка 22

Обязательно пишем ключевое слово typeof. Мы передаём не имя класса, а его тип (Type), и оно возвращает нам именно тип класса.

4-5. public FilteredElementCollector WhereElementIsElementType() и

public FilteredElementCollector WhereElementIsNotElementType()

2 метода, которые из нашего выбора возьмут либо только экземпляры, либо только типы. Например, мы можем написать так, в 2 вариантах:

Строки 21-25
Строки 21-25

То есть в первом случае мы берём все элементы класса WallType (типоразмеры стен), а во втором — все элементы категории "Стены", а потом из них берём только типоразмеры. Результат (в 99% случаях) будет одинаковый. Пишу 99%, потому что до конца не уверен, как поведут себя типы стен, импортированные из IFC.

6-7. public IList<Element> ToElements() и public IList<Element> ToElementIds()

Вызываются в конце. Превращают коллектор в список элементов или их Id. В большинстве случаев я пишу ToElements(). Потому что:

8. Cast<T>()

Этого метода нет в API, это метод расширения LINQ. Он приводит ("кастует") все элементы к другому типу.

Зачем нам это? Допустим, мы собрали все воздуховоды в список и хотим сделать с ними что-то (вписать габариты в параметр "Комментарии". Мы взяли все элементы класса Duct, и идём по ним в цикле. Но вот незадача — у элементов нет свойств воздуховодов. А в Lookup мы их видим:

Свойства воздуховода (Duct) и MEPCurve (базового класса)
Свойства воздуховода (Duct) и MEPCurve (базового класса)

Дело в том, что в цикле мы идём по коллекции, состоящей из Element. Это мы знаем, что в ней воздуховоды, а компилятор — не знает. И такой код не скомпилирует. То есть мы в цикле должны ввести для каждого элемента новую переменную типа Duct, привести её к нужному типу:

Duct duct = element as Duct;

И далее работать с ней. Проблемы особой нет, всего одна лишняя строчка.

Но можно решить элегантнее, и вызвать Cast<Duct>() после ToElements():

Строки 22-26
Строки 22-26

Данный код вернёт нам список воздуховодов (List<Duct>), и теперь мы можем для элементов списка использовать их свойства и методы. В конце я также вызвал ToList(), так как Cast возвращает IEnumerable, а не List.

А, ну и самое важное — тип, к которому вы приводите, нужно указывать в угловых скобках (вместо <Duct> из примера).

9. public FilteredElementCollector WherePasses(
ElementFilter filter )

Этот метод позволяет применить к коллекции любой другой фильтр из множества фильтров RevitAPI. Это тема для ещё одной большой статьи, поэтому подробно рассматривать её мы не будем. Если вам интересна эта тема, пишите в комментарии.

Для использования мы сначала создаём фильтр, а потом передаём его в коллектор:

-14

А на этом всё. В следующей статье мы научимся создавать транзакции и изменять элементы.

Подписывайтесь на мой телеграм-канал о Revit API. До новых встреч!

-15

Наука
7 млн интересуются