Найти в Дзене

Создание WPF-приложения для Revit. Часть 2. Заполнение окна и запуск.

Оглавление

Всем привет! Сегодня завершим работу над WPF-приложением из первой части статьи, где мы создали пустое WPF-окно и запустили его из Revit. Научимся создавать ViewModel и работать с привязками данных, а так же запустим приложение и посмотрим результат.

Задача

Напоминаю задачу, поставленную в первой части

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

  • поле ввода префикса
  • поле ввода начального значения
  • выпадающий список всех параметров (выбранного элемента)
  • кнопки "Запуск" и "Отмена"

Пользователь выбирает параметр (обязательно), префикс же и начальное значение можно не вводить (по умолчанию — пустая строка и единица).

Что сделано

При запуске команды из Revit появляется окно, соответствующее требованиям задачи:

Но содержание окна пустое и при нажатии на кнопки ничего не происходит.

Решение

Нам нужно сделать так, чтобы наш плагин (класс ViewModel) знал, какие данные введены в окно (View), а окно знало, чем заполнить выпадающий список и что делать при нажатии на кнопки.

Структура проекта

  • Класс Command
  • Класс MainView (окно View)
  • Класс ViewModel

При этом ссылку на текущий документ имеет только класс Command через входящий в метод Execute класс ExternalCommandData. Чтобы не обмениваться между классами переменной commandData, создадим статический класс RevitAPI, где будем хранить все нужные нам переменные и обращаться к ним из одного места:

Обратите внимание на большое число ссылок: написанный код активно использует этот класс
Обратите внимание на большое число ссылок: написанный код активно использует этот класс

Далее просто при запуске команды проинициализируем этот класс в самом начале:

-3

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

Создание ViewModel

Создадим класс ViewModel и сделаем в нём публичные поля с доступом на чтение и запись (обязательно публичные и get-set, иначе приложение может начать работать криво).

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

Нам нужны 4 свойства: префикс, стартовый номер, список параметров и выбранный параметр. Стартовый номер будет строкой, так как TextBox хранит в себе строки.

Строки 20-25
Строки 20-25

Список параметров обязательно нужно при объявлении проинициализировать пустым списком, как на строке 23, иначе словите исключение.

Теперь давайте заполним список параметров. Заполнять будем при создании (в конструкторе), а в конструктор передавать ссылку на выбранный элемент (элемент будем выбирать в классе Command):

Итоговый код класса Command
Итоговый код класса Command

Получив элемент, соберём у него параметры. Я решил сразу отсеять нередактируемые и хранящиеся в ElementId параметры:

-6

Модификация View

Отлично, мы создали ViewModel. Но теперь нам надо указать нашему окну, что значение из выпадающего списка хранится тут, а из TextBox — тут.

Для этого мы воспользуемся инструментом привязок Binding и зададим контекст данных DataContext. Вот как это выглядит:

-7

На строке 8 мы задали класс, который будет являться контекстом данных. Это необязательно, но благодаря этому у нас появятся свойства из ViewModel во всплывающих подсказках при заполнении привязок.

На строках 13, 17, 19 и 20 мы задали контекст данных в соответствующие элементы нашего окна.

Синтаксис тут только кажется простым, на самом деле у привязки много нюансов (можно делать привязку между элементами окна, привязку к родительскому элементу, автоматически конвертировать значение и проверять его на соответствие шаблону), но это всё мы рассмотрим позднее (или можно найти на других ресурсах).

Создание команды и передача DataContext

В текущем уроке мы не рассматриваем работу с классом Command. Это довольно сложная тема, требует создания дополнительных классов и реализации INotifyPropertyChanged — хватит на ещё одну длинную статью. Поэтому создаём обработчик события Click на 25 строке XAML, а в файле xaml.cs напишем следующее:

-8

Обязательно в конструктор окна передаём ViewModel как DataContext.

А затем реализуем обработчик. Сначала мы скрываем окно методом Hide, чтобы можно было выбрать элементы для нумерации, затем возвращаем управление во ViewModel, и закрываем окно.

Выполнение плагина

Реализуем метод Numerate во ViewModel:

Код будет такой же, как в статье про try-catch (кстати, там была ошибка, которую я исправил только сейчас, кому интересно, можете найти по коммитам).

-9

В общем, тут ничего нового, но обратите внимание, что на строках 42-43 я проверяю значение из поля для стартового номера. Если оно имеет неверный формат, то мы получим единицу.

Запуск плагина

Плагин на самом деле готов, можно запустить сборку и проверить его. Если у вас где-то что-то не сошлось — в конце будет ссылка на репозиторий GitHub.

Запускать будем через Addin-manager (как установить, как проводить отладку плагинов).

Подключить плагин как команду.

Подключить плагин как кнопку на панели.

Я разместил в Ревите несколько семейств и сразу создал им марку, которая будет выводить параметр Марка:

-10

Результат (GIF-ку загрузить не получилось)

-11

Итоговый код в моём репозитории на GitHub (не забываем ставить звёздочки).

А на этом всё. Сегодня мы завершили первое полноценное приложение WPF для Revit. Не забывайте ставить лайки, подписываться на этот канал в Дзене, и на мой телеграм-канал о Revit API. До новых встреч!

-12