Создание плагинов для AutoCAD с помощью .NET API (часть 2 – работа с лентой [Ribbon])

В прошлой своей статье я пообещал, что напишу еще несколько небольших заметок о разработке плагинов для AutoCAD. На Хабре сведений по этой теме крайне мало — пожалуй, можно и добавить еще пару материалов в обойму. В этой статье я приведу пример создания на ленте AutoCAD новой вкладки с несколькими элементами управления. public static string disclaimer = «Автор не является профессиональным разработчиком и не обладает глубокими знаниями AutoCAD. Этот пост — просто небольшой рассказ о создания плагина.»; Введение Когда я в свое время начинал работать с лентой, то больше всего мне помог пример, изложенный здесь. В основном я буду опираться именно на него.Кроме того, некоторую информацию можно почерпнуть из этого поста Kean Walmsley.Для начала вспомним, как выглядит лента в AutoCAD: 0aab5f77862a4845973866da328ea443.pngВ верхней части расположен список вкладок ленты (Home, Mesh Modeling, Render…). При выборе вкладки на ленте отображаются элементы управления этой вкладки, сгруппированные в панели (Modeling, Mesh, Solid Editing…).

Ну что же, приступим.

1. Создание нового проекта плагина Этому была посвящена прошлая статья. В качестве требуемой версии .NET Framework в приведенных ниже примерах указана .NET Framework 3.5.Можно сразу добавить каркас кода: using System; using Autodesk.AutoCAD.Runtime; using Autodesk.Windows;

namespace MyAutoCADDll { public class Commands: IExtensionApplication { // эта функция будет вызываться при выполнении в AutoCAD команды «TestCommand» [CommandMethod («TestCommand»)] public void MyCommand () {

}

// Функции Initialize () и Terminate () необходимы, чтобы реализовать интерфейс IExtensionApplication public void Initialize () {

}

public void Terminate () {

} } } 2. Добавление ссылок на необходимые библиотеки В данном случае пригодятся библиотеки AutoCAD .NET API с именами AcMgd.dll, AcDbMgd.dll и AdWindows.dll (не забываем отключать CopyLocal!). Кроме того, нужно добавить ссылки на три сборки самой .NET: Presentation Core, Presentation Framework и WindowsBase.3. Собственно написание кода для создания новой вкладки С точки зрения кода все выглядит очень просто. В AutoCAD .NET API уже имеются классы, отвечающие за работу с лентой. Они находятся в пространстве имен Autodesk.Windows (его содержит контейнер AdWindows.dll).Чтобы создать новую вкладку на ленте, необходимо:

создать элементы интерфейса; сгруппировать эти элементы в контейнеры; создать панели, на которых будут размещены эти контейнеры; создать вкладку, на которой будут размещены эти панели; добавить созданную вкладку на ленту AutoCAD. Код: // эта функция будет вызываться при выполнении в AutoCAD команды «TestCommand» [CommandMethod («TestCommand»)] public void MyCommand () { // создаем выпадающий список Autodesk.Windows.RibbonCombo comboBox1 = new RibbonCombo (); comboBox1.Id = »_combobox1»;

// создаем кнопку Autodesk.Windows.RibbonButton button1 = new Autodesk.Windows.RibbonButton (); button1.Id = »_button1»;

// создаем контейнер для элементов Autodesk.Windows.RibbonPanelSource rbPanelSource = new Autodesk.Windows.RibbonPanelSource (); rbPanelSource.Title = «Новая панель элементов»; // добавляем в контейнер элементы управления rbPanelSource.Items.Add (comboBox1); rbPanelSource.Items.Add (new RibbonSeparator ()); rbPanelSource.Items.Add (button1);

// создаем панель RibbonPanel rbPanel = new RibbonPanel (); // добавляем на панель контейнер для элементов rbPanel.Source = rbPanelSource;

// создаем вкладку RibbonTab rbTab = new RibbonTab (); rbTab.Title = «Новая вкладка»; rbTab.Id = «HabrRibbon»; // добавляем на вкладку панель rbTab.Panels.Add (rbPanel); // получаем указатель на ленту AutoCAD Autodesk.Windows.RibbonControl rbCtrl = ComponentManager.Ribbon; // добавляем на ленту вкладку rbCtrl.Tabs.Add (rbTab); // делаем созданную вкладку активной («выбранной») rbTab.IsActive = true; } Собираем проект, запускаем AutoCAD, загружаем с помощью команды NETLOAD наш плагин, выполняем команду TestCommand…63ce4bb1246a4763a2743bdcb40b321f.PNGДа, не самый впечатляющий результат. :)Но ничего, чуть позже сделаем вкладку повеселее. А пока разберемся с тем, что уже есть.

4. Поиск элементов на ленте Для поиска вкладки на ленте можно использовать метод ComponentManager.Ribbon.FindTab (string id). В качестве аргумента необходимо указать Id вкладки, заданный при ее создании.Существуют аналогичные методы для поиска панели (ComponentManager.Ribbon.FindPanel (string id, bool SearchActiveTabOnly)) и прочих элементов управления (ComponentManager.Ribbon.FindItem (string id, bool SearchActiveTabOnly)).

В случае успешного нахождения элемента приведенные функции вернут соответствующий объект, иначе будет возврашено значение null.

5. Обработка нажатия кнопки Для привязки обработчика нажатия кнопки служит свойство CommandHandler класса RibbonButton. В этом свойстве необходимо указать метод, реализующий интерфейс System.Windows.Input.ICommand.В рамках интерфейса ICommand класс должен реализовать событие CanExecuteChanged, а также функции CanExecute и Execute.

Код примера с обработчиком нажатия кнопки: using System; using Autodesk.AutoCAD.Runtime; using Autodesk.Windows;

namespace MyAutoCADDll { public class Commands: IExtensionApplication { // эта функция будет вызываться при выполнении в AutoCAD команды «TestCommand» [CommandMethod («TestCommand»)] public void MyCommand () { // создаем выпадающий список Autodesk.Windows.RibbonCombo comboBox1 = new RibbonCombo (); comboBox1.Id = »_combobox1»;

// создаем кнопку Autodesk.Windows.RibbonButton button1 = new Autodesk.Windows.RibbonButton (); button1.Id = »_button1»; // привязываем к кнопке обработчик нажатия button1.CommandHandler = new CommandHandler_Button1();

// создаем контейнер для элементов Autodesk.Windows.RibbonPanelSource rbPanelSource = new Autodesk.Windows.RibbonPanelSource (); rbPanelSource.Title = «Новая панель элементов»; // добавляем в контейнер элементы управления rbPanelSource.Items.Add (comboBox1); rbPanelSource.Items.Add (new RibbonSeparator ()); rbPanelSource.Items.Add (button1);

// создаем панель RibbonPanel rbPanel = new RibbonPanel (); // добавляем на панель контейнер для элементов rbPanel.Source = rbPanelSource;

// создаем вкладку RibbonTab rbTab = new RibbonTab (); rbTab.Title = «Новая вкладка»; rbTab.Id = «HabrRibbon»; // добавляем на вкладку панель rbTab.Panels.Add (rbPanel);

// получаем указатель на ленту AutoCAD Autodesk.Windows.RibbonControl rbCtrl = ComponentManager.Ribbon; // добавляем на ленту вкладку rbCtrl.Tabs.Add (rbTab); // делаем созданную вкладку активной («выбранной») rbTab.IsActive = true; }

// Функции Initialize () и Terminate () необходимы, чтобы реализовать интерфейс IExtensionApplication public void Initialize () {

}

public void Terminate () {

} }

// обработчик нажатия кнопки public class CommandHandler_Button1: System.Windows.Input.ICommand { public event EventHandler CanExecuteChanged;

public bool CanExecute (object param) { return true; }

public void Execute (object parameter) { System.Windows.MessageBox.Show («Habr!»); } } } NB: Событие CanExecuteChanged оповещает пользователей команды о возможном изменении ее доступности для выполнения (короче говоря, работает она или не работает). Функция CanExecute позволяет узнать, доступна ли команда для выполнения в данный момент времени. А функция Execute — это собственно те действия, которые должна выполнять команда, когда ее вызвали.В настоящем примере команда доступна всегда, и это состояние не меняется. В моей реальной задаче тоже ни разу не возникало необходимости как-то использовать первые два параметра.)

Теперь после нажатия кнопки на экране появится окно с сообщением.3df553be2cd74cc48106a32d473279f7.PNG6. Взаимодействие с выпадающим списком (RibbonCombo) 6.1 Добавление, изменение, удаление элементов списка Все элементы выпадающего списка RibbonCombo содержатся в его свойстве Items. Оно имеет тип System.Collections.ObjectModel.ObservableCollection, причем в качестве типа содержимого выступает System.Object. Таким образом, элементом коллекции может быть объект любого класса. К сожалению, если просто добавить в этот массив несколько текстовых строк, то желаемого эффекта мы не получим: comboBox1.Items.Add («добавим»); comboBox1.Items.Add («несколько»); comboBox1.Items.Add («элементов»); c519a821740a4ce981697781fa053ad7.PNGЧтобы получить приличный выпадающий список, в качестве его элементов можно использовать экземпляры рассмотренного выше класса RibbonButton:

Autodesk.Windows.RibbonButton tempRibBut1 = new Autodesk.Windows.RibbonButton (); tempRibBut1.Id = »_temp_button_1»; tempRibBut1.Text = «элемент 1»; tempRibBut1.ShowText = true; Autodesk.Windows.RibbonButton tempRibBut2 = new Autodesk.Windows.RibbonButton (); tempRibBut2.Id = »_temp_button_2»; tempRibBut2.Text = «элемент 2»; tempRibBut2.ShowText = true;

comboBox1.Items.Add (tempRibBut1); comboBox1.Items.Add (tempRibBut2); В результате увидим вот что: ae4272de4ff040b88d96b5f8f30226c6.PNGПри необходимости можно использовать заложенные в ObservableCollection свойства и методы, в частности:

метод Remove (object item) — для удаления указанного элемента; метод RemoveAt (int index) — для удаления элемента на указанной позиции; метод Clear () — для удаления всех элементов из коллекции; свойство Count — для получения количества элементов в коллекции. Текущий элемент списка RibbonButton хранится в его свойстве Current.NB: Обычно элемент списка соотносится с каким-то объектом предметной области. Чтобы иметь возможность быстро определить, с чем сопоставлен тот или иной элемент списка, можно использовать свойство Tag класса RibbonButton: Autodesk.Windows.RibbonButton tempRibBut1 = new Autodesk.Windows.RibbonButton (); tempRibBut1.Id = »_temp_button_1»; tempRibBut1.Text = «элемент 1»; tempRibBut1.ShowText = true; tempRibBut1.Tag = «elementTag»; // задаем тег comboBox1.Items.Add (tempRibBut1); Тогда при обработке элемента списка можно посмотреть, какой был задан тег: object obj = comboBox1.Items[0]; string itemTag = (obj as RibbonButton).Tag; // «elementTag» Можно пойти и еще дальше. Поскольку свойство Tag имеет тип System.Object, в качестве тега может выступать объект любого класса, в том числе и созданного самим программистом: tempRibBut1.Tag = new MyClass («objectDecription»); После этого можно будет обратиться к любому свойству этого объекта: object obj = comboBox1.Items[0]; MyClass itemTag = (obj as RibbonButton).Tag as MyClass; string myClassDecription = itemTag.Description; При возникновении острого желания экономить строки есть возможность писать конструкции вида string myClassDecription = ((comboBox1.Items[0] as RibbonButton).Tag as MyClass).Description Разумеется, в реальном коде необходимо убеждаться, что полученные значения не равны null. 6.2 Обработка события выбора элемента списка При выборе элемента списка RibbonCombo генерируется событие CurrentChanged. Вот простой пример обработчика такого события: // обработчик выбора нового элемента выпадающего списка public static void comboBox1_CurrentChanged (object o, RibbonPropertyChangedEventArgs args) { if (args.NewValue!= null) { System.Windows.MessageBox.Show ((args.NewValue as RibbonButton).Text); } } Полный код примера: using System; using Autodesk.AutoCAD.Runtime; using Autodesk.Windows;

namespace MyAutoCADDll { public class Commands: IExtensionApplication { // эта функция будет вызываться при выполнении в AutoCAD команды «TestCommand» [CommandMethod («TestCommand»)] public void MyCommand () { // создаем выпадающий список Autodesk.Windows.RibbonCombo comboBox1 = new RibbonCombo (); comboBox1.Id = »_combobox1»;

// добавляем новые элементы в список Autodesk.Windows.RibbonButton tempRibBut1 = new Autodesk.Windows.RibbonButton (); tempRibBut1.Id = »_temp_button_1»; tempRibBut1.Text = «элемент 1»; tempRibBut1.ShowText = true; tempRibBut1.Tag = «btn1»; Autodesk.Windows.RibbonButton tempRibBut2 = new Autodesk.Windows.RibbonButton (); tempRibBut2.Id = »_temp_button_2»; tempRibBut2.Text = «элемент 2»; tempRibBut2.ShowText = true; tempRibBut2.Tag = «btn2»;

comboBox1.Items.Add (tempRibBut1); comboBox1.Items.Add (tempRibBut2);

// привязываем к списку обработчик выбора нового элемента comboBox1.CurrentChanged += comboBox1_CurrentChanged;

// создаем кнопку Autodesk.Windows.RibbonButton button1 = new Autodesk.Windows.RibbonButton (); button1.Id = »_button1»; // привязываем к кнопке обработчик нажатия button1.CommandHandler = new CommandHandler_Button1();

// создаем контейнер для элементов Autodesk.Windows.RibbonPanelSource rbPanelSource = new Autodesk.Windows.RibbonPanelSource (); rbPanelSource.Title = «Новая панель элементов»; // добавляем в контейнер элементы управления rbPanelSource.Items.Add (comboBox1); rbPanelSource.Items.Add (new RibbonSeparator ()); rbPanelSource.Items.Add (button1);

// создаем панель RibbonPanel rbPanel = new RibbonPanel (); // добавляем на панель контейнер для элементов rbPanel.Source = rbPanelSource;

// создаем вкладку RibbonTab rbTab = new RibbonTab (); rbTab.Title = «Новая вкладка»; rbTab.Id = «HabrRibbon»; // добавляем на вкладку панель rbTab.Panels.Add (rbPanel);

// получаем указатель на ленту AutoCAD Autodesk.Windows.RibbonControl rbCtrl = ComponentManager.Ribbon; // добавляем на ленту вкладку rbCtrl.Tabs.Add (rbTab); // делаем созданную вкладку активной («выбранной») rbTab.IsActive = true; }

// обработчик выбора нового элемента выпадающего списка public static void comboBox1_CurrentChanged (object o, RibbonPropertyChangedEventArgs args) { if (args.NewValue!= null) { System.Windows.MessageBox.Show ((args.NewValue as RibbonButton).Text); } }

// Функции Initialize () и Terminate () необходимы, чтобы реализовать интерфейс IExtensionApplication public void Initialize () {

}

public void Terminate () {

} }

// обработчик нажатия кнопки public class CommandHandler_Button1: System.Windows.Input.ICommand { public event EventHandler CanExecuteChanged;

public bool CanExecute (object param) { return true; }

public void Execute (object parameter) { System.Windows.MessageBox.Show («Habr!»); } } } Результат: 8ac45566168b413cb48f7f2ce8591f57.PNG7. Настройка внешнего вида элементов управления Имеющиеся в AutoCAD .NET API классы, на мой взгляд, обладают не самыми широкими возможностями по настройке своего внешнего вида. Однако базовые вещи, безусловно, есть.Во-первых, элементы управления можно располагать друг под другом — это особенно удобно, если используются «узкие» элементы вроде выпадающих списков.

Во-вторых, у каждого элемента управления есть свойства, позволяющие изменять его внешний вид. Например, для выпадающего списка можно задать заголовок и ширину, для кнопки — размер (большая или маленькая) и подпись. Кроме того, можно добавить всплывающие подсказки (в примере она добавлена для третьей кнопки).

Код (обратите внимание, была добавлена ссылка на сборку и пространство имен System.Drawing): using System; using System.Drawing; using Autodesk.AutoCAD.Runtime; using Autodesk.Windows;

namespace MyAutoCADDll { public class Commands: IExtensionApplication { // эта функция будет вызываться при выполнении в AutoCAD команды «TestCommand» [CommandMethod («TestCommand»)] public void MyCommand () { // создаем квадратик цвета морской волны (он будет старательно играть роль иконки) Bitmap bmp = new Bitmap (1, 1); bmp.SetPixel (0, 0, Color.Aquamarine); bmp = new Bitmap (bmp, 1024, 1024); IntPtr hBitmap = bmp.GetHbitmap (); System.Windows.Media.Imaging.BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap ( hBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions ());

// создаем выпадающие списки Autodesk.Windows.RibbonCombo comboBox1 = new RibbonCombo (); comboBox1.Id = »_combobox1»; comboBox1.Width = 200; comboBox1.Text = «Список 1»; comboBox1.ShowText = true; Autodesk.Windows.RibbonCombo comboBox2 = new RibbonCombo (); comboBox2.Id = »_combobox2»; comboBox2.Width = 200; comboBox2.Image = bs; comboBox2.ShowImage = true;

// создаем кнопки Autodesk.Windows.RibbonButton button1 = new Autodesk.Windows.RibbonButton (); button1.Id = »_button1»; Autodesk.Windows.RibbonButton button2 = new Autodesk.Windows.RibbonButton (); button2.Id = »_button2»;

// создаем вертикальные панели, на которых будут размещены друг под другом выпадающие списки и кнопки Autodesk.Windows.RibbonRowPanel RowPanel1 = new Autodesk.Windows.RibbonRowPanel (); Autodesk.Windows.RibbonRowPanel RowPanel2 = new Autodesk.Windows.RibbonRowPanel ();

// размещаем в вертикальных панелях выпадающие списки и кнопки RowPanel1.Items.Add (comboBox1); RowPanel1.Items.Add (new RibbonRowBreak ()); RowPanel1.Items.Add (comboBox2); RowPanel2.Items.Add (button1); RowPanel2.Items.Add (new RibbonRowBreak ()); RowPanel2.Items.Add (button2);

// создаем кнопки большого размера Autodesk.Windows.RibbonButton button3 = new Autodesk.Windows.RibbonButton (); button3.Id = »_button3»; button3.IsToolTipEnabled = true; button3.ToolTip = «Это большая кнопка»; button3.Size = Autodesk.Windows.RibbonItemSize.Large; button3.LargeImage = bs; Autodesk.Windows.RibbonButton button4 = new Autodesk.Windows.RibbonButton (); button4.Id = »_button4»; button4.Text = »^___^»; button4.ShowText = true; button4.Size = Autodesk.Windows.RibbonItemSize.Large; button4.LargeImage = bs;

// создаем контейнеры для элементов Autodesk.Windows.RibbonPanelSource rbPanelSource1 = new Autodesk.Windows.RibbonPanelSource (); rbPanelSource1.Title = «Новая панель элементов»; Autodesk.Windows.RibbonPanelSource rbPanelSource2 = new Autodesk.Windows.RibbonPanelSource (); rbPanelSource2.Title = «Еще одна панель»;

// добавляем в контейнеры элементы управления rbPanelSource1.Items.Add (RowPanel1); rbPanelSource1.Items.Add (RowPanel2); rbPanelSource1.Items.Add (new RibbonSeparator ()); rbPanelSource1.Items.Add (button3); rbPanelSource2.Items.Add (button4);

// создаем панели RibbonPanel rbPanel1 = new RibbonPanel (); RibbonPanel rbPanel2 = new RibbonPanel ();

// добавляем на панели контейнеры для элементов rbPanel1.Source = rbPanelSource1; rbPanel2.Source = rbPanelSource2;

// создаем вкладку RibbonTab rbTab = new RibbonTab (); rbTab.Title = «Новая вкладка»; rbTab.Id = «HabrRibbon»;

// добавляем на вкладку панели rbTab.Panels.Add (rbPanel1); rbTab.Panels.Add (rbPanel2);

// получаем указатель на ленту AutoCAD Autodesk.Windows.RibbonControl rbCtrl = ComponentManager.Ribbon;

// добавляем на ленту вкладку rbCtrl.Tabs.Add (rbTab);

// делаем созданную вкладку активной («выбранной») rbTab.IsActive = true; }

// Функции Initialize () и Terminate () необходимы, чтобы реализовать интерфейс IExtensionApplication public void Initialize () {

}

public void Terminate () {

} } } Результат: 3146ce16530f418d9ff5378cbe4f97ed.PNG683c6a49d960455d950e46c50a5373bc.PNG

На этом статья подходит к концу. В следующий раз напишу о работе со слоями и простыми графическими объектами.

© Habrahabr.ru