Pull to refresh

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

Reading time 12 min
Views 14K
В прошлой своей статье я пообещал, что напишу еще несколько небольших заметок о разработке плагинов для AutoCAD. На Хабре сведений по этой теме крайне мало — пожалуй, можно и добавить еще пару материалов в обойму. В этой статье я приведу пример создания на ленте AutoCAD новой вкладки с несколькими элементами управления.

public static string disclaimer = "Автор не является профессиональным разработчиком и не обладает глубокими знаниями AutoCAD. Этот пост – просто небольшой рассказ о создании плагина.";

Введение


Когда я в свое время начинал работать с лентой, то больше всего мне помог пример, изложенный здесь. В основном я буду опираться именно на него.
Кроме того, некоторую информацию можно почерпнуть из этого поста Kean Walmsley.

Для начала вспомним, как выглядит лента в AutoCAD:

В верхней части расположен список вкладок ленты (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).

Чтобы создать новую вкладку на ленте, необходимо:
  1. создать элементы интерфейса;
  2. сгруппировать эти элементы в контейнеры;
  3. создать панели, на которых будут размещены эти контейнеры;
  4. создать вкладку, на которой будут размещены эти панели;
  5. добавить созданную вкладку на ленту 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…


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

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 — это собственно те действия, которые должна выполнять команда, когда ее вызвали.

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

Теперь после нажатия кнопки на экране появится окно с сообщением.


6. Взаимодействие с выпадающим списком (RibbonCombo)


6.1 Добавление, изменение, удаление элементов списка

Все элементы выпадающего списка RibbonCombo содержатся в его свойстве Items. Оно имеет тип System.Collections.ObjectModel.ObservableCollection, причем в качестве типа содержимого выступает System.Object. Таким образом, элементом коллекции может быть объект любого класса. К сожалению, если просто добавить в этот массив несколько текстовых строк, то желаемого эффекта мы не получим:
comboBox1.Items.Add("добавим");
comboBox1.Items.Add("несколько");
comboBox1.Items.Add("элементов");



Чтобы получить приличный выпадающий список, в качестве его элементов можно использовать экземпляры рассмотренного выше класса 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);

В результате увидим вот что:


При необходимости можно использовать заложенные в 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!");
        }
    }
}

Результат:


7. Настройка внешнего вида элементов управления


Имеющиеся в 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()
        {

        }
    }
}

Результат:




На этом статья подходит к концу. В следующий раз напишу о работе со слоями и простыми графическими объектами.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+13
Comments 2
Comments Comments 2

Articles