В этом уроке мы реализуем обещание из Урока 0 — создадим рабочую команду, которая:
- создаёт лист (с правильной обработкой ошибок)
- подсчитывает существующие листы в документе
- выводит пользователю понятную сводку
✅ Требования: Завершите настройку
Applicationиз Урока 0. Если в проекте нет основных надписей, команда уведомит об этом и корректно завершится.
Application vs. Command
- Application (запускается с Revit): настраивает ваш аддин — создаёт панели и кнопки на ленте. Это «инфраструктура и интерфейс».
- Command (выполняется по нажатию кнопки): выполняет основную работу — читает/изменяет модель. Это «логика инструмента».
В коде эти роли представлены разными интерфейсами:
| Интерфейс | Когда запускается | Для чего | Примеры |
|---|---|---|---|
IExternalApplication | При старте/выключении Revit | Добавить ленту, зарегистрировать события | Добавить панель и кнопку |
IExternalCommand | Когда пользователь нажимает кнопку | Выполнить работу (read/modify model) | Создать листы, разместить виды |
IExternalDBApplication | При старте/выключении (без UI) | Слушать DB-события | Batch/monitoring, analytics |
IExternalCommandAvailability | Перед активацией команды | Включать/отключать кнопку по состоянию | Отключать без открытого doc |
IExternalEventHandler | По запросу из modeless UI | Безопасно трогать модель из UI/фоновых потоков | Progress windows, async tasks |
🧰
Nice3point.Revit.Toolkitпредоставляет удобные базовые классыExternalApplicationиExternalCommand, которые упрощают эти паттерны. В этом уроке мы работаем с «чистым»IExternalCommandдля понимания основ, но также покажем интеграцию с лентой через Nice3point.
Почему Revit требует IExternal* vs External* классы?
Кратко: Revit — хост-приложение. Он вызывает только классы, реализующие официальные интерфейсы. Основные точки входа: IExternalApplication (запуск/завершение) и IExternalCommand (пользовательские инструменты).
Откуда ExternalApplication / ExternalCommand (без I)?
Это вспомогательные базовые классы из библиотек (например, Nice3point). Они реализуют необходимые интерфейсы и предоставляют удобные свойства (Document, UiDocument), делая код лаконичнее. Revit по-прежнему работает с интерфейсами.
Можно ли держать оба в одном классе?
Обычно нет. Лучше разделять из-за разных жизненных циклов:
IExternalApplication/ExternalApplication— однократно за сессию Revit (создание UI, регистрация событий)IExternalCommand/ExternalCommand— при каждом нажатии кнопки (выполнение работы: транзакции, правки)
Стандартный паттерн — два отдельных класса в одной сборке:
| |
Как Revit находит ваш код (.addin mapping)
Revit читает файл .addin, который указывает на вашу сборку и entry-class. Два типовых варианта:
A) Рекомендуется: Application-loader + лента (одна запись .addin)
- Одна запись
<AddIn Type="Application">на ваш Application-класс. - В OnStartup создаёте панель ленты и добавляете push button, привязанный к вашему классу команды.
| |
В Application.OnStartup() регистрируете кнопку примерно так:
new PushButtonData("CreateSheet", "Create Sheet", assemblyPath, "Test.Commands.CreateSheetCommand");
B) Только команда (видна в Add-Ins → External Tools)
— Запись <AddIn Type="Command">, указывающая напрямую на класс команды. Ленту настраивать не требуется.
| |
Когда и что использовать?
| Нужно… | Используйте |
|---|---|
| Кнопка на ленте + логика старта | IExternalApplication/ExternalApplication (создать UI и привязать кнопки) |
| Действие по клику, читающее/меняющее модель | IExternalCommand/ExternalCommand с transactions |
| Слушатели DB-событий без UI | IExternalDBApplication |
| Динамически включать/выключать кнопку | IExternalCommandAvailability |
| Безопасно менять модель из modeless/async | IExternalEventHandler |
Коротко: Интерфейсы IExternal* — контракт, который вызывает Revit. Классы External* упрощают работу, но соблюдают тот же контракт. Application (запуск и UI) и Command (логика) лучше разделять и связывать через ленту.
Домашнее задание
Напишите команду CreateSheetCommand, которая:
- Получает активный
Documentиз контекста Revit - Запускает
Transactionс именем “Create Sheet” - Находит тип основной надписи (не экземпляр!) и создаёт новый
ViewSheet - Фиксирует транзакцию (
Commit) при успехе - Собирает все листы и формирует краткий отчёт:
- общее количество листов
- первые 5 листов в формате
Номер-Название
- Показывает результат через
TaskDialog - Возвращает
Result.Succeeded; при ошибке — откат с понятным сообщением
Решение
Показать/Скрыть Код
Project Solution
| |
StartupCommand.cs (команда)
| |
Application.cs
| |
Если вы не используете Nice3point, добавьте кнопку через чистый Revit API в
IExternalApplication.OnStartupи свяжите её сCreateSheetCommandв .addin. Чистую ленту разберём в одном из следующих уроков.
Результат

Пошаговое руководство
1) Обязательный атрибут
| |
Почему Manual?
- Вы полностью контролируете транзакции:
Start()- Начать транзакцию,Commit()- Завершить транзакцию,RollBack()- Отменить транзакцию.
- Это стандарт для команд, изменяющих модель,
Альтернативы TransactionMode.Manual:
TransactionMode.ReadOnly— только для чтения/анализа (без изменений в модели)
2) Контракт команды
| |
- Revit вызывает
Executeпри нажатии кнопки - Метод возвращает
Result:Succeeded,Cancelled,Failed - Ключевой параметр:
commandData.Application.ActiveUIDocument.Document→ активный документ
3) Транзакции (сеть безопасности)
Все изменения модели выполняются внутри Transaction:
| |
4) Эффективный поиск элементов
FilteredElementCollector — основной инструмент поиска элементов в модели:
| |
5) Создание листа ViewSheet.Create
| |
Без типа основной надписи Revit не сможет создать лист — поэтому обрабатываем InvalidOperationException.
6) Общение с пользователем
TaskDialog обеспечивает нативное отображение сообщений в Revit.
Интерфейсы — наглядные различия
IExternalApplication → запуск/завершение, настройка интерфейса и событий
IExternalCommand → выполнение по клику, основная логика (с транзакциями)
IExternalDBApplication → фоновые процессы (без UI), события базы данных
IExternalEventHandler → асинхронные операции с моделью
IExternalCommandAvailability → управление доступностью команд по контексту
IExternalCommand vs. ExternalCommand
- Существует два способа создания команд:
Обычный интерфейс (этот урок)
| |
Удобный базовый класс Nice3point
| |
IExternalCommand— это «чистый» интерфейс Revit API. Ты полностью контролируешь - описание методаExecute(), сам достаёшь UIApplication, UIDocument, Document из аргументов и работаешь напрямую с API. Такой подход даёт максимальную прозрачность и полезен для понимания, как Revit вызывает твой код.ExternalCommandот Nice3point — это удобная обёртка. Она уже реализуетIExternalCommandза тебя и предоставляет готовые свойства (Document, UiDocument, Application), убирает шаблонный код и упрощает старт проекта. Под капотом > используется тот же самый Revit API.
📌 Вывод: Разница только в уровне абстракции:
ExternalCommand— это «синтаксический сахар», аIExternalCommand— чистый API. На продакшн можно писать на базовом классе для скорости, но для обучения и отладки полезно начать с интерфейса.
Устранение неполадок
“Основные надписи не найдены”
- Вставка → Загрузить семейство → добавьте основную надпись (.rfa)
Кнопка не отображается
- Проверьте настройки .addin и полное имя класса. При использовании Nice3point убедитесь, что
ExternalApplicationзапускается
Ошибки транзакций
- Вызывайте
Start()перед изменениями модели, завершайте каждую транзакциюCommit()илиRollBack()
ActiveUIDocument равно null
- Нет открытого проекта. Откройте модель в Revit
Что дальше
Урок 2: Основы программирования (биты, байты, системы счисления в C#)
Далее: размещение видов на листах, продвинутые фильтры элементов, API выбора и немодальные инструменты с IExternalEventHandler
Особая благодарность Aussie BIM Guru за вдохновляющие материалы по Revit API.
