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

31/12/2013

Доступ к определениям общих параметров проекта

Работая со статьей Определение типа общего параметра я вспомнил об одной проблемке, с которой я сам сталкивался при работе с общими параметрами проекта.

Мне нужно было определить, есть ли в проекте общий параметр с определенным GUID или именем и в случае если такой есть в проекте, то удалить его из списка общих параметров проекта. Предпочтительней конечно по GUID, так как параметров с одинаковым именем может быть несколько.

Прежде чем приступить к решению, давайте немного вспомним как общие параметры задаются в интерфейсе Revit, и как это же самое сделать программно.

В интерфейсе Revit создание параметра осуществляется с помощью файла общих параметров, который затем можно передать другому участнику проекта, либо повторно использовать в другом проекте.

Прежде чем создать новый общий параметр, необходимо описать базовые свойства параметра: его название, тип и категорию.

 

После создания параметра файл общих параметров выглядит вот так:

Код - XML: [Выделить]
  1. # This is a Revit shared parameter file.
  2. # Do not edit manually.
  3. *META VERSION MINVERSION
  4. META 2 1
  5. *GROUP ID NAME
  6. GROUP 1 Группа 1
  7. *PARAM GUID NAME DATATYPE DATACATEGORY GROUP VISIBLE
  8. PARAM 05210621-7802-4b40-8c8b-2863132cddf8 ADN-CIS LENGTH 1 1
  9.  

Как мы видим, в этом текстовом файле просто содержится так информация, которую мы задали в интерфейсе. Но самое для нас главное, что здесь содержится GUID параметра, по которому мы в дальнейшем можем идентифицировать параметр.

Тут же кстати можно заметить маленькую хитрость. Последнее свойство в файле - Видимость параметра (VISIBLE), которое мы не можем редактировать в интерфейсе, но можем вручную отредактировать в файле, хоть в нем и предупреждается что не стоит его редактировать руками.

Идем дальше. Описание параметра мы создали. Но это описание хранится только лишь в текстовом файле и Revit еще абсолютно ничего не знает, как этот параметр использовать в проекте.

Для того, чтобы добавить параметр в проект и задать его конкретным категориям, а также его тип - параметр экземпляра или типа, нужно сделать привязку описания к категориям проекта. В интерфейсе это делается с помощью команды Управление -> Параметры проекта

 

Таким образом, добавление общего параметра в проект состоит из двух частей: Создание описание параметра в файле общих параметров и привязка параметра к категориям проекта.

Для программного создания параметра необходимо выполнить те же самые шаги. И, к сожалению, без файла общих параметров программно создать параметр не получится.

Не будем здесь подробно описывать как создать общий параметр программно, так как тема немного не об этом. Вкратце лишь напомню, что нужно получить доступ к файлу общих параметров с помощью ControlledApplication.OpenSharedParameterFile(). Предварительно нужно установить путь к файлу общих параметров с помощью свойства ControlledApplication.SharedParametersFileName.

Метод возвращает ControlledApplication.OpenSharedParameterFile() объект класса DefinitionFile, где вы можете создавать группы (DefinitionGroup), а в группах Описание параметра (Definition). Либо же можно получить доступ к существующем в файле описанием.

Затем вам нужно создать новую привязку (Binding). Если же вы хотите задать параметр экземпляра, то необходимо использовать InstanceBinding. Для параметра типа нужен TypeBinding.

В привязке вам нужно так же задать список категорий, к которым необходимо привязать параметр. Все как в интерфейсе.

И последний шаг, нужно добавить эту привязку непосредственно проект с помощью метода Document.ParameterBindings.Insert().

С теорией разобрались. Вернемся к проблеме. Напомню, что нам нужно найти общий параметр в проекте с определенным GUID.

Давайте посмотрим, что нам предлагает API.

Единственное свойство, которое дает нам доступ к параметрам проекта - Document.ParameterBindings, которое возвращает объект класса BindingMap. Обратимся к свойствам и методам этого класса. Для удаления существует метод Remove(), для проверки - Contains(). Чтобы получить конкретную привязку свойство Item, для C# это метод get_Item, т.к. C# не поддерживает свойства с индексаторами.

Вроде бы все хорошо. Но есть одно но... все эти методы принимают в качестве параметра объект класса Definition. А получить Definition мы можем из файла общих параметров.

Вот тут и возникает проблема. Во-первых, файла общих параметров может и не быть, так как фактически, после того как параметр привязали к проекту, сам файл уже не нужен. Во-вторых, даже если бы он был, то это значительно усложнит код, так как нужно проделать дополнительные действия по поиску конкретного описания параметра в этом файле.

Пойдем с другой стороны. Класс BindingMap реализует интерфейс IEnumerable, а это значит, что можно применить замечательную конструкцию foreach или LINQ.

Код - C#: [Выделить]
  1.             foreach (var parameterBinding in doc.ParameterBindings)
  2.             {
  3.                 Debug.Print(parameterBinding.GetType().ToString());
  4.             }

Но и тут нас ожидает разочарование. Тип объекта parameterBindings в этом случае будет TypeBinding  или InstanceBinding, то есть кроме как узнать является ли неизвестный нам параметр параметром типа или экземпляра, а также получить категории, к которым он привязан - не получится.

Копнем глубже. Оператор foreachявляется лишь так называемым "синтаксическим сахаром" языка C#, для облегчения перечисления всех методов коллекции. Те же самые действия можно получить с помощью других стандартных конструкций, в которые по сути и превращается конструкция foreach

Код - C#: [Выделить]
  1.             var parameterBindingEnumerator = doc.ParameterBindings.GetEnumerator();
  2.  
  3.             while (parameterBindingEnumerator.MoveNext())
  4.             {
  5.                 var parameterBinding = parameterBindingEnumerator.Current;
  6.             }

 

По какой то причине Autodesk решил немного усложнить жизнь,  создали свой Enumerator и создали новый метод BindingMap.ForwardIterator() для доступа к нему. А вот метод BindingMap.ForwardIterator() возвращает уже объект класса DefinitionBindingMapIterator, у которого есть два свойства - Currentи Key.

Свойство Keyимеет тип Definition. А это как раз то что нам нужно!

Но здесь нас опять поджидает подвох. GUID содержится только у ExternalDefinition, а свойство Key всегда InternalDefinition, даже если параметр создан с помощью файла общих параметров. Очередной минус в копилку Autodesk.

Таким образом, чтобы найти определение нужного параметра по GUID, хотелось бы использовать такой метод:

Код - C#: [Выделить]
  1.         public Definition GetDefinitionByGuid(BindingMap bindingMap, Guid parameterGuid)
  2.         {
  3.             var iterator = bindingMap.ForwardIterator();
  4.             while (iterator.MoveNext())
  5.             {
  6.                 var definition = iterator.Key;
  7.                 if (definition is ExternalDefinition) // GUID содержится только у общих параметров
  8.                 {
  9.                     var externalDefinition = definition as ExternalDefinition;
  10.                     if (externalDefinition.GUID.Equals(parameterGuid))
  11.                         return externalDefinition;
  12.                 }
  13.             }
  14.             return null;
  15.         }
  16.  

Но, к сожалению, придется воспользоваться поиском по наименованию:

Код - C#: [Выделить]
  1.          public IEnumerable<Definition> GetDefinitionsByName(BindingMap bindingMap, string name)
  2.         {
  3.             var iterator = bindingMap.ForwardIterator();
  4.             while (iterator.MoveNext())
  5.             {
  6.                 var definition = iterator.Key;
  7.                 if (definition.Name.Equals(name))
  8.                     yield return definition;
  9.             }           
  10.         }
  11.  

Либо же получать определение из файла.

Кстати, интересно можно ли все же получить определение из файла общих параметров и удалить затем с помощью BindingMap.Remove(). Ведь в файле будет ExternalDefinition, а в проекте InternalDefintion. Возможно, что кроме как наименованием параметра больше никак и не определить.

Автор: Виктор Чекалин
Автор перевода: Виктор Чекалин

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

Опубликовано 31.12.2013
Отредактировано 01.01.2014 в 16:06:53