Всем привет! Сегодня завершим работу над WPF-приложением из первой части статьи, где мы создали пустое WPF-окно и запустили его из Revit. Научимся создавать ViewModel и работать с привязками данных, а так же запустим приложение и посмотрим результат.
Задача
Напоминаю задачу, поставленную в первой части
Создадим плагин-нумератор с возможностью добавления постоянного префикса. Пользователь выбирает элемент, появляется окно со следующими компонентами:
- поле ввода префикса
- поле ввода начального значения
- выпадающий список всех параметров (выбранного элемента)
- кнопки "Запуск" и "Отмена"
Пользователь выбирает параметр (обязательно), префикс же и начальное значение можно не вводить (по умолчанию — пустая строка и единица).
Что сделано
При запуске команды из Revit появляется окно, соответствующее требованиям задачи:
Но содержание окна пустое и при нажатии на кнопки ничего не происходит.
Решение
Нам нужно сделать так, чтобы наш плагин (класс ViewModel) знал, какие данные введены в окно (View), а окно знало, чем заполнить выпадающий список и что делать при нажатии на кнопки.
Структура проекта
- Класс Command
- Класс MainView (окно View)
- Класс ViewModel
При этом ссылку на текущий документ имеет только класс Command через входящий в метод Execute класс ExternalCommandData. Чтобы не обмениваться между классами переменной commandData, создадим статический класс RevitAPI, где будем хранить все нужные нам переменные и обращаться к ним из одного места:
Далее просто при запуске команды проинициализируем этот класс в самом начале:
Отлично, мы заполнили все поля, и теперь можем обращаться к активному документу из любого места кода и не передавать его из класса в класс.
Создание ViewModel
Создадим класс ViewModel и сделаем в нём публичные поля с доступом на чтение и запись (обязательно публичные и get-set, иначе приложение может начать работать криво).
Я ранее не писал про классы и их свойства подробно, лишь упоминал вскользь. Почитать можно тут.
Нам нужны 4 свойства: префикс, стартовый номер, список параметров и выбранный параметр. Стартовый номер будет строкой, так как TextBox хранит в себе строки.
Список параметров обязательно нужно при объявлении проинициализировать пустым списком, как на строке 23, иначе словите исключение.
Теперь давайте заполним список параметров. Заполнять будем при создании (в конструкторе), а в конструктор передавать ссылку на выбранный элемент (элемент будем выбирать в классе Command):
Получив элемент, соберём у него параметры. Я решил сразу отсеять нередактируемые и хранящиеся в ElementId параметры:
Модификация View
Отлично, мы создали ViewModel. Но теперь нам надо указать нашему окну, что значение из выпадающего списка хранится тут, а из TextBox — тут.
Для этого мы воспользуемся инструментом привязок Binding и зададим контекст данных DataContext. Вот как это выглядит:
На строке 8 мы задали класс, который будет являться контекстом данных. Это необязательно, но благодаря этому у нас появятся свойства из ViewModel во всплывающих подсказках при заполнении привязок.
На строках 13, 17, 19 и 20 мы задали контекст данных в соответствующие элементы нашего окна.
Синтаксис тут только кажется простым, на самом деле у привязки много нюансов (можно делать привязку между элементами окна, привязку к родительскому элементу, автоматически конвертировать значение и проверять его на соответствие шаблону), но это всё мы рассмотрим позднее (или можно найти на других ресурсах).
Создание команды и передача DataContext
В текущем уроке мы не рассматриваем работу с классом Command. Это довольно сложная тема, требует создания дополнительных классов и реализации INotifyPropertyChanged — хватит на ещё одну длинную статью. Поэтому создаём обработчик события Click на 25 строке XAML, а в файле xaml.cs напишем следующее:
Обязательно в конструктор окна передаём ViewModel как DataContext.
А затем реализуем обработчик. Сначала мы скрываем окно методом Hide, чтобы можно было выбрать элементы для нумерации, затем возвращаем управление во ViewModel, и закрываем окно.
Выполнение плагина
Реализуем метод Numerate во ViewModel:
Код будет такой же, как в статье про try-catch (кстати, там была ошибка, которую я исправил только сейчас, кому интересно, можете найти по коммитам).
В общем, тут ничего нового, но обратите внимание, что на строках 42-43 я проверяю значение из поля для стартового номера. Если оно имеет неверный формат, то мы получим единицу.
Запуск плагина
Плагин на самом деле готов, можно запустить сборку и проверить его. Если у вас где-то что-то не сошлось — в конце будет ссылка на репозиторий GitHub.
Запускать будем через Addin-manager (как установить, как проводить отладку плагинов).
Подключить плагин как команду.
Подключить плагин как кнопку на панели.
Я разместил в Ревите несколько семейств и сразу создал им марку, которая будет выводить параметр Марка:
Результат (GIF-ку загрузить не получилось)
Итоговый код в моём репозитории на GitHub (не забываем ставить звёздочки).
А на этом всё. Сегодня мы завершили первое полноценное приложение WPF для Revit. Не забывайте ставить лайки, подписываться на этот канал в Дзене, и на мой телеграм-канал о Revit API. До новых встреч!