Найти в Дзене

Создание автоматического апдейтера модели

Оглавление

Всем привет! Сегодня рассмотрим очень интересную и на самом деле простую тему: интерфейс IUpdater, как его реализовать, как зарегистрировать свой апдейтер, а также некоторые связанные с этим нюансы.

Задача

Не буду изобретать велосипед, и напишу апдейтер, который будет для труб, воздуховодов и лотков записывать их длину в параметр "Комментарии" при любых их изменениях.
Вы в принципе можете создать какой хотите апдейтер, хоть удаляющий текстовые надписи при попытке их создать, или запрещающий пользователю создавать новые типы.

Создание апдейтера

Для создания апдейтера я создал отдельный проект в своём решении для блога. Проект получился небольшой, на один файл — собственно, сам апдейтер.

Создаём класс, наследуемся от IUpdater, жмём Alt-Enter на подчёркнутые слова IUpdater и выбираем "Реализовать интерфейс":

Получаем такую заготовку:

-2

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

1. GetUpdaterName. Нам нужно вернуть имя апдейтера, которое будет видеть пользователь, если апдейтеру потребуется передать сообщение, или которое отобразится в Revit Lookup. Я особо не заморачиваюсь и делаю так:

-3

2. GetAdditionalInformation. Это дополнительная информация, которая показывается пользователю, если апдейтер не загружен (по крайней мере, так говорит справка). У меня тестовый проект, я оставляю пустую строку:

-4

3. GetChangePriority. Самый странный и неочевидный метод. Мы возвращаем значение из enum ChangePriority. Варианты там такие:

-5

Но что всё это значит? А самое интересное, что ничего. Это просто порядок выполнения апдейтеров. В случае, если одну и ту же операцию выполняют несколько апдейтеров, то первым будет тот, у кого приоритет GridsLevelsReferencePlanes, а последним — Annotations, о чём написано в описании этого enum.

Я решил проверить это, и создал 2 апдейтера с одинаковыми триггерами, один из которых выводит сообщение "First updater", а второй — "Last Updater". Порядок соблюдается. Запустить можно, взяв код из этого коммита (а окончательный код будет в конце статьи)

4. GetUpdaterId. Важный метод. Мы должны вернуть UpdaterId, который состоит из 2 частей: AddinId, который должен совпасть с AddinId нашего приложения, и Guid апдейтера. Я решил использовать свойство ActiveAddinId класса Application, почему бы и нет:

-6

5. Execute. Собственно, реализация апдейтера. Тут пишем код, который обновляет элементы.

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

Этот метод имеет внутри себя UpdaterData, из который мы можем получить текущий документ, id добавленных, изменённых и удалённых элементов и некоторые другие вещи. Меня для решения указанной в начале статьи задачи интересуют добавленные и изменённые элементы, поэтому я сделал такую реализацию:

-7

Обратите внимание, внутри апдейтера транзакции не нужны.

Собственно, на этом всё, рабочий апдейтер создан.

Регистрация апдейтера

Здесь нам нужно сделать 2 вещи: зарегистрировать апдейтер в приложении и добавить к нему триггеры, чтобы он работал. Делается это примерно так:

-8

Не забудьте вызвать этот метод в OnStartup:

-9

Мы создаём экземпляр апдейтера, а потом регистрируем его через класс UpdaterRegistry. Я рекомендую использовать перегрузки (всего их 4) метода RegisterUpdater, имеющие параметры isOptional, и передавать туда true. Тогда у нас не будет вот таких сообщений для пользователя, если у него не установлен апдейтер:

https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Error-Missing-Third-Party-Updater.html
https://www.autodesk.com/support/technical/article/caas/sfdcarticles/sfdcarticles/Error-Missing-Third-Party-Updater.html

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

Например, вот так
Например, вот так

Я создал элемент ElementClassFilter, и добавил 2 триггера — на добавление элемента и изменение его геометрии.

Смотрим результат:

-12

Длина автоматически записывается в комментарии. Апдейтер работает.

Заключение

Тема действительно оказалась несложной, апдейтер занял 44 строки кода, и ещё его регистрация — строк на 10. Итоговый код лежит в моём репозитории в этом коммите.

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

-13