ADN Open CIS
Сообщество программистов Autodesk в СНГ

29/11/2014

Как получить все уровни, упорядоченные по высоте

Вопрос: Как я могу получить коллекцию уровней, отсортированную по высоте уровня? Пока что мне кажется, что FilteredElementCollector возвращает IEnumerable и его можно отсортировать с помощью OrderBy, затем, вызвав метод ToElementIds(), то будет создан набор идентификаторов, в уже нужном порядке. Верно?

Вот код, который я попробовал. Он не работает:

Код - C#: [Выделить]
  1.   UIDocument uiDoc = this.ActiveUIDocument;
  2.   Document doc = uiDoc.Document;
  3.  
  4.   FilteredElementCollector levCollector
  5.     = new FilteredElementCollector( doc );
  6.  
  7.   ICollection<Element> levelsCollection
  8.     = levCollector.OfClass( typeof( Level ) )
  9.       .OrderBy( lev => lev.Elevation )
  10.       .ToElementIds();

Кто-то может мне подсказать, что я делаю не так?

Ответ 1: Я не разбирался с остальным кодом, но я по крайней мере вижу, что если вы определяете переменную как ICollection<Element>, то эту коллекцию нужно заполнять объектами типа Element, а не ElementId:

Код - C#: [Выделить]
  1. ICollection<Element>... .ToElementIds();

Ответ 2:

Код - C#: [Выделить]
  1.   static IOrderedEnumerable<Level> FindAndSortLevels(
  2.     Document doc )
  3.   {
  4.     return new FilteredElementCollector( doc )
  5.       .WherePasses( new ElementClassFilter( typeof( Level ), false ) )
  6.       .Cast<Level>()
  7.       .OrderBy( e => e.Elevation );
  8.   }

Ответ 3: Нужно заменить вот эту часть:

Код - C#: [Выделить]
  1.   ICollection<Element> levelsCollection
  2.     = levCollector.OfClass( typeof( Level ) )
  3.       .OrderBy( lev => lev.Elevation )
  4.       .ToElementIds();

На вот эту:

Код - C#: [Выделить]
  1. List<Level> levelsCollection
  2.     = levCollector.OfClass( typeof( Level ) )
  3.       .OfType<Level>()
  4.       .OrderBy( lev => lev.Elevation )
  5.       .ToList();

Причины:

  • Возвращаемое значение не может быть ICollection<Element> если вызывается метод ToElementIds()
  • Метод OfClass() возвращает коллекцию объектов типа Element, поэтому вы можете отсортировать их по высоте (Elevation), только после приведения к типу Level. Поэтому я и предложил использовать OfType<Level>().
  • Метод OrderBy возвращает IOrderedEnumerable<T> и если вы хотите обращаться к объектам коллекции по индексу, т.е. list[i], то нужно вызвать метод ToList(), который вернет List<T>

Ответ 4: Дополнения от Jeremy:

Использование методов класса FilteredElementCollector для предварительной фильтрации элементов всегда предпочтительней, чем явное использование ElementFilter, так как в этом случае будут гарантировано использоваться так называемые «быстрые» фильтры. Поэтому и было предложено сначала использовать OfClass( typeof( Level ) ) для явного использования ElementClassFilter.

Класс FilteredElementCollector уже сам по себе является перечисляемой коллекцией. Поэтому зачастую не нужно дополнительно создавать коллекции .NET, более подробно можно почитать в статье Оптимизация поиска элементов (на англ.)

Вот моя реализация:

Код - C#: [Выделить]
  1. As a result of those considerations, here is my suggestion for an optimal solution:
  2.  
  3.   IOrderedEnumerable<Level> GetSortedLevels( Document doc )
  4.   {
  5.     return new FilteredElementCollector( doc )
  6.       .OfClass( typeof( Level ) )
  7.       .Cast<Level>()
  8.       .OrderBy( lev => lev.Elevation );
  9.   }

Еще множество примеров по использованию FilteredElementCollector, смотрите в примерах The Building Coder, в модуле CmdCollectorPerformance.cs.

Источник: http://thebuildingcoder.typepad.com/blog/2014/11/webgl-goes-mobile-and-sorted-level-retrieval.html

Обсуждение: http://adn-cis.org/forum/index.php?topic=1118

Опубликовано 29.11.2014