ADN Open CIS
Сообщество программистов Autodesk в СНГ

17/02/2017

Как получить данные кнопки, которая запускает команду

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

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

Поискав в документации по Revit API, ничего подобного не нашлось. Но, вспомнив о том, что помимо официальных сборок Revit API, есть еще недокументированные сборки, которые содержат много интересных фишек, и которые используется самим Revit. В частности есть сборка AdWindows.dll, в которой и прописана вся логика работы с интерфейсом, в частности с лентой.

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

В этой сборке есть статический класс – ComponentManager.  Раз статический, можно использовать его где угодно и когда угодно. Вернее, доступ к нему есть отовсюду, но можно ли им воспользоваться или нет, зависит от контекста.

Первое событие, что мне попалось на глаза – ComponentManager.ItemExecuted. В аргументах обработки события мы видим класс RibbonItemExecutedEventArgs, где есть свойство Item. Логично предположить, что после того, как будет нажата кнопка на ленте, будет вызвано это событие. В обработчике события мы получим доступ к тому, что же именно было нажато. Попробуем реализовать такой алгоритм:

1)      Подпишемся на это событие при старте приложения

2)      В обработке события запишем в статическую переменную тот элемент управления на ленте, которые был использован для выполнения

3)      В команде обратимся к этому статическому свойству и извлечем оттуда название.

Код - C#: [Выделить]
  1. class App : IExternalApplication
  2.     {
  3.         private static RibbonItem _executedItem;
  4.  
  5.         public static RibbonItem ExecutedItem
  6.         {
  7.             get { return _executedItem; }
  8.         }
  9.        
  10.         public Result OnStartup(UIControlledApplication a)
  11.         {
  12.            // Подписываемся на событие при старте приложения
  13.             ComponentManager.ItemExecuted += ComponentManager_ItemExecuted
  14.             return Result.Succeeded;
  15.         }       
  16.     }

И команда:

Код - C#: [Выделить]
  1.             var item = App.ExecutedItem;
  2.             if (item == null)
  3.             {
  4.                 return Result.Failed;               
  5.             }
  6. TaskDialog.Show(“ADN-CIS”, item.Id);

Выводим Id кнопки в сообщении.

Запускаем. И… почти все работает, если бы ни одно но... Событие ItemExecuted вызывается уже после окончания выполнения кода команды. Таким образом мы получаем кнопку, которая была нажата ранее.

Дальнейший поиск привел к еще одному событию -  ComponentManager.UIElementActivated. Согласно названию, данное событие должно возникать, в тот момент, когда элемент управления станет активным. Нажатие мышкой на него приводит к активации. Смущает только то, что событие будет вызываться не только по нажатию кнопки, а вообще по любому действию на ленте – переключение по вкладкам, панелям и т.п. В целом не критично, но все же.

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

В итоге получился такой код:

Код - C#: [Выделить]
  1.     class App : IExternalApplication
  2.     {
  3.         private static RibbonItem _executedItem;
  4.  
  5.         public static RibbonItem ExecutedItem
  6.         {
  7.             get { return _executedItem; }
  8.         }
  9.         
  10.         public Result OnStartup(UIControlledApplication a)
  11.         {
  12.             // Подписываемся на событие активации элеента на ленте
  13.             ComponentManager.UIElementActivated += ComponentManager_UIElementActivated;
  14.  
  15.             var panel = a.CreateRibbonPanel("ADN-CIS");
  16.  
  17.             var assemblyName = Assembly.GetExecutingAssembly().Location;
  18.  
  19.             // Создаем 10 кнопок с разным идентификатором и текстом
  20.             for (int i = 0; i < 10; i++)
  21.             {
  22.                 var buttonName = string.Format("ADN-CIS_Button{0}", i);
  23.  
  24.                 // каждая кнопка запускает одну и ту же команду
  25.                 PushButtonData pushButtonData =
  26.                     new PushButtonData(buttonName, string.Format("Кнопка {0}", i), assemblyName, "ADNCIS.Command");
  27.                 panel.AddItem(pushButtonData);
  28.             }
  29.  
  30.  
  31.             return Result.Succeeded;
  32.         }
  33.  
  34.         void ComponentManager_UIElementActivated(object sender, UIElementActivatedEventArgs e)
  35.         {
  36.             _executedItem = e.Item;
  37.         }
  38.        
  39.         public Result OnShutdown(UIControlledApplication a)
  40.         {          
  41.             return Result.Succeeded;
  42.         }
  43.     }

И такая команда:

Код - C#: [Выделить]
  1.     [Transaction(TransactionMode.Manual)]
  2.     public class Command : IExternalCommand
  3.     {
  4.         public Result Execute(
  5.           ExternalCommandData commandData,
  6.           ref string message,
  7.           ElementSet elements)
  8.         {
  9.  
  10.             var item = App.ExecutedItem;
  11.             if (item == null)
  12.             {
  13.                 return Result.Failed;               
  14.             }
  15.  
  16.             var taskDialog = new TaskDialog("ADN-CIS");
  17.            
  18.             // Свойство Text содержит текст, который видит пользователь.
  19.             taskDialog.MainInstruction = item.Text;
  20.  
  21.             // В свойстве Id хранится идентификатор объекта
  22.             // Формируется он как то так, где последняя часть - это идентификатор кнопки, который мы задали при создании
  23.             // "CustomCtrl_%CustomCtrl_%Add-Ins%ADN-CIS%ADN-CIS_Button2"
  24.            
  25.             var buttonName = item.Id.Substring(item.Id.LastIndexOf('%')+1, item.Id.Length - (item.Id.LastIndexOf('%') + 1));
  26.             taskDialog.MainContent = buttonName;
  27.  
  28.             taskDialog.Show();
  29.  
  30.             return Result.Succeeded;
  31.         }       
  32.     }

В заключении – видео с демонстрацией работы.

Автор: Виктор Чекалин

Обсуждение: http://adn-cis.org/forum/index.php?topic=7644

Опубликовано 17.02.2017
Отредактировано 17.02.2017 в 17:28:55