Всем привет! Сегодня мы научимся создавать оконные приложения для Revit с помощью Windows Forms. Прежде чем начать, пару вводных слов:
Windows Forms — устаревшая технология, и практически везде заменена на WPF (а он постепенно меняется на MAUI). Главный недостаток Forms — в нём используется событийная модель, вместо привязок, используемых в WPF. То есть когда мы пишем код, мы обрабатываем происходящие с элементами окна события (пользователь нажал на кнопку — произошли действия). Это не позволяет отделить написание непосредственно окна от бизнес-логики приложения (что довольно легко происходит в WPF при использовании паттерна Model-View-ViewModel), из-за чего такие приложения труднее разрабатывать и поддерживать.
Тем не менее, я решил рассказать об этой технологии, потому что она простая, интуитивная и местами очень удобная (например, есть удобный графический конструктор с готовыми элементами). Кроме того, в этом блоге я транслирую свой опыт, а начинал я именно с этого (макросы — команды — Windows Forms — WPF).
Создание приложения
Задача
Создадим приложение, которое предлагает пользователю выбрать элемент. После выбора появляется окно с выпадающим списком, где перечислены все параметры (кроме имеющих тип хранения ElementId), и текстовым полем, где отображено значение выбранного параметра. Пользователь редактирует значение в текстовом поле, нажимает кнопку, и значение перезаписывается.
Решение
1. Создание окна
Создадим новый проект VS и подключим к нему все ссылки (здесь напоминаю порядок действий). У нас получится что-то вроде этого:
Добавляем форму (дадим ей имя MainWindow):
Получим что-то подобное:
Если у вас не видна панель элементов, то нажмите Ctrl-Alt-X или Вид — Панель элементов.
В этой статье я не ставлю перед собой цель рассказать про все элементы и нюансы Windows Forms. Поэтому мы просто добавим элементы: выпадающий список (ComboBox), текстовое поле (TextBox) и 2 кнопки (Button): Запуск и Отмена. Но вы можете сами посмотреть другие элементы в тех же панелях Visual Studio (разумеется, делитесь скринами ваших экспериментов в комментариях).
Итак, что мы делаем:
В панели элементов выбираем "Стандартные элементы управления" и вытягиваем оттуда выбранные элементы. Руками компонуем их в окне. Если нажать на элемент правой кнопкой мыши и выбрать "Свойства", то появится панель свойств элемента (она обязательно потребуется нам).
Далее отредактируем элементы. Зададим им имена (чтобы можно было обращаться к ним в коде), и изменим текст на кнопках. Всё делается через панель свойств.
Имена элементов:
- Запуск — RunButton
- Отмена — CancelButton
- TextBox — TextUserValue
- ComboBox — ParametersBox
Тут вы можете поиграться с отображением окна (я поменял фон и увеличил шрифт на кнопках до 10). А вот с именами элементов играться не советую, делайте их максимально понятными и очевидными, чтобы потом и вы, и кто-то другой, мог понять ваш код.
2. Наполнение окна
Перейдём в наш класс MainCommand (основную команду) и напишем стартовый код:
Почему я использовал try-catch.
Если сейчас запустить нашу команду, то появится окно (но ничего с ним сделать мы не сможем, только закрыть крестиком). Заполним его элементами.
Для начала перейдём в файл MainWindow.designer.resx и найдём в его конце наши именованные элементы:
Заменим модификаторы private на public, чтобы обращаться к ним из других мест кода.
Затем, находясь в конструкторе окна, нажмём Вид — Код (или F7). Окно откроется в виде кода. Добавим свойство Element, через которое мы будем передавать в окно наш элемент:
Затем перейдём к коду основной команды и напишем следующее (комментарии можно не повторять):
Тут остановимся подробнее. В данном коде мы добавляем в ParametersBox строки (имена параметров). Вообще, это необязательно, и можно добавлять любые объекты. Только отображаться они будут в не в виде своего свойства Name, а в виде полного квалифицированного имени класса (точнее, в виде вызова метода ToString() на них). Если проинициализировать ComboBox (передать в свойство DataSource) список параметров, то получим список одинаковых строк "Autodesk.Revit.DB.Parameter" на выходе (строка 44). Исправить это можно, передав имя нужного свойства в DisplayMember (строка 45). Но туда можно передать только имя свойства, а цепочку свойств — нельзя. А имя параметра лежит в его свойстве Definition.
В WPF это бы сработало — там свойство называется DisplayMemberPath.
Поэтому я проинициализировал ComboBox именами параметров (строка 41), а первоначальный вариант закомментировал.
Итак, мы имеем окно, выпадающий список со всеми (кроме ElementId) параметрами. Осталось только сделать так, чтобы TextBox менял текст при изменении выбранного параметра, а по кнопкам выполнялись команды.
Вновь идём в конструктор, выбираем наш выпадающий список, в свойствах щёлкаем значок с молнией и ищем нужное нам событие по его имени. В данном случае это SelectionChangedCommited. Справа пишем SelectionChanged, и VS создаст метод для обработки события и перекинет нас на него:
Поскольку мы передали элемент в класс MainWindow (см. код основной команды, строка 32), мы можем обратиться к этому элементу в коде. Написанный в данном методе код выполнится тогда, когда мы изменим выбор в выпадающем списке.
С кнопкой "Запуск" можно поступить аналогично, а можно просто кликнуть на неё дважды в конструкторе: это создаст обработчик события в коде.
Запишем в него основной код плагина:
Если ничего не выбрано, то ничего не делаем (строка 34).
Получаем из переданного элемента и имени параметра сам параметр, а с элемента — его документ. Запускаем в документе транзакцию.
Переменная result будет использоваться для отображения результата (если формат ввода некорректный, обновление не произойдёт)
Далее мы проверяем тип хранения параметра, и если это не строка, то преобразуем его к соответствующему типу методом Parse (это статический метод, он вызывается не на переменной, а на имени типа). Тут у нас может случиться исключение, если строка имеет неверный формат, или если значение параметра заблокировано, поэтому используем try-catch. В конце выводим результат в TaskDialog.
Проверим плагин, запустив его через Addin Manager (используем его для отладки):
На самом деле, всё заработало не сразу: например, на отладке я выловил проблему с отображением имени параметра, из-за чего пришлось заполнять выпадающий список строками. До этого было вот так:
Проверим плагин, перенеся addin и dll в папку для плагинов C:\ProgramData\Autodesk\Revit\Addins\2023
Команда появилась — значит всё сделано правильно. Если хотите, можете превратить её в кнопку на панели.
Итоговый код в моём репозитории на github.
А на этом всё. В ближайшее время я начну подробно писать о технологии WPF, и начну наверное с создания в ней аналогичного приложения. Если у вас есть идеи, на каком примере можно описать создание оконных приложений лучше — пишите в комментарии, я подумаю над вашими темами. Главное требование — приложение должно быть достаточно простым, чтобы и статья, и код не раздулись слишком сильно. Тут важно не сделать что-то очень сложное, а показать принципы создания оконных приложений.
Также подписывайтесь на мой телеграм-канал, там иногда выходит дополнительный контент, которого нет на дзене.
Пробуйте создавать свои приложения, делитесь скринами с результатами, спрашивайте, если что-то не получается. До новых встреч!