Найти тему

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

Оглавление

Часть 1 здесь.

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

Делегаты

Начнём издалека. Делегаты в C# — специальные объекты, которые представляют собой ссылки на методы. Подробнее о них можно почитать тут.

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

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

Реализация

За реализацией мы обратимся к Revit Lookup — сделаем хендлеры также, как сделаны в этом проекте. В нём используются шаблоны Романа Карповича Nice3point, а конкретно для настройки универсальных хэндлеров используется входящий в их состав Revit Toolkit.

Подключим Toolkit к нашему проекту через nuget:

Выбирайте версию, соответствующую вашей версии Revit
Выбирайте версию, соответствующую вашей версии Revit

В Revit Lookup хэндлеры создаются в классе приложения при запуске. У меня приложения нет, так что я реализую IExternalApplication, чтобы всё было красиво.

В общем, проблемы в том, чтобы создать хэндлеры в команде, нет. Работать всё будет. Но у вас, скорее всего, будет и приложение, и несколько команд для него, поэтому не буду показывать вам неаккуратные примеры кода.

Класс Application:

-2

А теперь модифицируем наш код:

  1. Удаляем ModelessWindowHandler и все ссылки на него.
  2. Код из его метода Execute переносим во ViewModel, но оборачиваем всё в Handler:
-3

То есть теперь непосредственно команда из ViewModel выполняет действия, а в контекст API мы погружаемся за счёт Handler.

3. Добавляем отписку от события в метод закрытия окна (ссылку на метод не забываем добавить в XAML):

Файл xaml.cs
Файл xaml.cs
xaml
xaml

CloseRequest во ViewModel и команде нам тоже больше не нужен.

Итоговый код вы можете посмотреть в моём репозитории, а изменения по сравнению с частью 1 — в коммите.

Результат

Окно теперь работает как надо: при изменении выбранного элемента отображается его марка, мы можем заменить её и окно останется открытым. Не забудьте перенести все файлы из bin/Debug в папку для addin-ов. Про автоматический перенос пишу тут, но в этом проекте я его не включал.

Как это работает:

Код Handler-ов от Nice3point можно посмотреть прямо в RevitToolkit на гитхабе, либо в своём проекте нажать на имя типа с клавишей Ctrl и посмотреть прямо в VS:

Декомпилированный код от Nice3Point (на GitHub будет тот де самый)
Декомпилированный код от Nice3Point (на GitHub будет тот де самый)

Под капотом там хранится Action<UIApplication>, который представляет собой ссылку на метод — тот самый делегат:

-7

Реализацию этого делегата мы передаём каждый раз, когда вызываем публичный метод Raise(Action action):

Красным как раз и обведена наша реализация action
Красным как раз и обведена наша реализация action

Этот метод вызывает Raise() для ExternalEvent, после чего вызывается Execute, куда передаётся наш action, который в конце перезаписывается как null Handler вновь готов к работе.

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

В решении из статьи остался минус — нет ничего, что помешало бы запустить более одного окна одновременно, что может привести к багам. Кстати, решение для приложения с немодальным окном вы можете взять, создав проект на шаблонах Романа. Только внимательно читайте описание пред началом работы.

Заключение

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

-9