Всем привет!
В данной публикации мы рассмотрим пример программного построения собственной вкладки на ленте, а также несколько проблемных вопросов, связанных с лентой
В последнее время стало очень популярным нововведение под названием лента (Ribbon). Autodesk не стал отставать от жизни и, начиная с 2009 автокада, тоже начал использовать ленту.
Мы рассмотрим пример создания вкладки на ленте для 2010 автокада и рассмотрим несколько сопутствующих "проблемных" вопросов. Почему для него? Да потому-что в 2009 лента была еще "сырая" (но там тоже можно так сделать), и потому-что для последующих автокадов (2011-2013) код будет анологичен.
Вариантов добавить свою вкладку на ленту несколько:
- создать файл АПИ (cui) со своей лентой и подгружать его. На мой взгляд самый плохой и проблемный вариант. ИМХО
- создать вкладку на ленте программно - этот вариант и рассмотрим
- создать вкладку используя технологию WPF - я не стал разбираться :)
Примечание: Я не буду углубляться в каждую мелочь и "красиво" все описывать - пример не сложный. Да и я так красиво излагать не умею :D
Пример рассматривается с учетом, что вы знаете как писать (хотя-бы самые простые) плагины для автокада средствами .net. Для работы я использую Microsoft Visual Studio 2010
Итак, приступим:
1. Открываем VS2010 и создаем новый проект:

2. Далее к нашему проекту подключаем ссылки:
- Проект - Добавить ссылку - вкладка "Обзор" - из папки с автокадом 2010 выбираем файлы: acdbmgd.dll, acmgd.dll, AdWindows.dll. Не забываем в свойствах этих файлов поставить значение false для параметра Копировать локально.
- Проект - Добавить ссылку - вкладка "NET" - добавляем ссылки на следующие библиотеки: PresentationCore, PresentationFramework, WindowsBase.

3. Теперь рассмотрим из чего состоит лента на примере этой картинки:

Тут в принципе и описывать нечего :)
4. Добавляем в наш проект две иконки размерами 16х16 и 32х32 в формате .png. Так, как я ленивый, то использовал одинаковую картинку первую попавшеюся на просторах интернет :)
Главное для этих файлов поставить значение Resource для параметра Действие при построение

5. Переходим к коду. Открываем файл Class1.cs (Или можете свой создать), удаляем класс Class1 и создаем свой собственный класс, унаследованный от IExtensionApplication. Назовем его ExampleRibbon.
Далее я хотел как-то пошагово описать все действия, но решил, что удобней и понятней будет сразу привести весь код с пояснениями:
using System; using System.Collections.Generic; using System.Linq; using System.Text; // Acad using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.Windows; using acadApp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace ACadRibbon { public class ExampleRibbon : IExtensionApplication { // Инициализация нашего плагина public void Initialize() { /* ленту грузим с помощью обработчика событий: * Этот вариант нужно использовать, если ваш плагин * стоит в автозагрузке, т.к. он (плагин) инициализируется * до построения ленты */ //Autodesk.Windows.ComponentManager.ItemInitialized += new EventHandler(ComponentManager_ItemInitialized); // Т.к. мы грузим плагин через NETLOAD, то строим вкладку в ленте сразу BuildRibbonTab(); } // Происходит при закрытии автокада public void Terminate() { // Тут в принципе ничего не требуется делать } /* Обработчик события * Следит за событиями изменения окна автокада. * Используем его для того, чтобы "поймать" момент построения ленты, * учитывая, что наш плагин уже инициализировался */ void ComponentManager_ItemInitialized(object sender, Autodesk.Windows.RibbonItemEventArgs e) { // Проверяем, что лента загружена if (Autodesk.Windows.ComponentManager.Ribbon != null) { // Строим нашу вкладку BuildRibbonTab(); //и раз уж лента запустилась, то отключаем обработчик событий Autodesk.Windows.ComponentManager.ItemInitialized -= new EventHandlerRibbonItemEventArgs>(ComponentManager_ItemInitialized); } } // Построение вкладки void BuildRibbonTab() { // Если лента еще не загружена if (!isLoaded()) { // Строим вкладку CreateRibbonTab(); // Подключаем обработчик событий изменения системных переменных acadApp.SystemVariableChanged += new SystemVariableChangedEventHandler(acadApp_SystemVariableChanged); } } // Проверка "загруженности" ленты bool isLoaded() { bool _loaded = false; RibbonControl ribCntrl = Autodesk.Windows.ComponentManager.Ribbon; // Делаем итерацию по вкладкам ленты foreach (RibbonTab tab in ribCntrl.Tabs) { // И если у вкладки совпадает идентификатор и заголовок, то значит вкладка загружена if (tab.Id.Equals("RibbonExample_ID") & tab.Title.Equals("RibbonExample")) { _loaded = true; break; } else _loaded = false; } return _loaded; } /* Удаление своей вкладки с ленты * В данном примере не используем */ void RemoveRibbonTab() { try { RibbonControl ribCntrl = Autodesk.Windows.ComponentManager.Ribbon; // Делаем итерацию по вкладкам ленты foreach (RibbonTab tab in ribCntrl.Tabs) { if (tab.Id.Equals("RibbonExample_ID") & tab.Title.Equals("RibbonExample")) { // И если у вкладки совпадает идентификатор и заголовок, то удаляем эту вкладку ribCntrl.Tabs.Remove(tab); // Отключаем обработчик событий acadApp.SystemVariableChanged -= new SystemVariableChangedEventHandler(acadApp_SystemVariableChanged); // Останавливаем итерацию break; } } } catch (Autodesk.AutoCAD.Runtime.Exception ex) { Autodesk.AutoCAD.ApplicationServices.Application. DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message); } } /* Обработка события изменения системной переменной * Будем следить за системной переменной WSCURRENT (текущее рабочее пространство), * чтобы наша вкладка не "терялась" при изменение рабочего пространства */ void acadApp_SystemVariableChanged(object sender, SystemVariableChangedEventArgs e) { if (e.Name.Equals("WSCURRENT")) BuildRibbonTab(); } // Создание нашей вкладки void CreateRibbonTab() { try { // Получаем доступ к ленте RibbonControl ribCntrl = Autodesk.Windows.ComponentManager.Ribbon; // добавляем свою вкладку RibbonTab ribTab = new RibbonTab(); ribTab.Title = "RibbonExample"; // Заголовок вкладки ribTab.Id = "RibbonExample_ID"; // Идентификатор вкладки ribCntrl.Tabs.Add(ribTab); // Добавляем вкладку в ленту // добавляем содержимое в свою вкладку (одну панель) addExampleContent(ribTab); // Делаем вкладку активной (не желательно, ибо неудобно) //ribTab.IsActive = true; // Обновляем ленту (если делаете вкладку активной, то необязательно) ribCntrl.UpdateLayout();
} catch (System.Exception ex) { Autodesk.AutoCAD.ApplicationServices.Application. DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message); } } // Строим новую панель в нашей вкладке void addExampleContent(RibbonTab ribTab) { try { // создаем panel source RibbonPanelSource ribSourcePanel = new RibbonPanelSource(); ribSourcePanel.Title = "RibbonExample"; // теперь саму панель RibbonPanel ribPanel = new RibbonPanel(); ribPanel.Source = ribSourcePanel; ribTab.Panels.Add(ribPanel); // создаем пустую tooltip (всплывающая подсказка) RibbonToolTip tt; // создаем split button RibbonSplitButton risSplitBtn = new RibbonSplitButton(); /* Для RibbonSplitButton ОБЯЗАТЕЛЬНО надо указать * свойство Text, а иначе при поиске команд в автокаде * будет вылетать ошибка. */ risSplitBtn.Text = "RibbonSplitButton"; // Ориентация кнопки risSplitBtn.Orientation = System.Windows.Controls.Orientation.Vertical; // Размер кнопки risSplitBtn.Size = RibbonItemSize.Large; // Показывать изображение risSplitBtn.ShowImage = true; // Показывать текст risSplitBtn.ShowText = true; // Стиль кнопки risSplitBtn.ListButtonStyle = Autodesk.Private.Windows.RibbonListButtonStyle.SplitButton; risSplitBtn.ResizeStyle = RibbonItemResizeStyles.NoResize; risSplitBtn.ListStyle = RibbonSplitButtonListStyle.List; /* Далее создаем две кнопки и добавляем их * не в панель, а в RibbonSplitButton */ #region Кнопка-пример №1 // Создаем новый экземпляр подсказки tt = new RibbonToolTip(); // Отключаем вызов справки (в данном примере её нету) tt.IsHelpEnabled = false; // Создаем кнопку RibbonButton ribBtn = new RibbonButton(); /* В свойство CommandParameter (параметры команды) * и в свойство Command (отображает команду) подсказки * пишем вызываемую команду */ ribBtn.CommandParameter = tt.Command = "_Line"; // Имя кнопки ribBtn.Name = "ExampleButton1"; // Заголовок кнопки и подсказки ribBtn.Text = tt.Title = "Кнопка-пример №1"; // Создаем новый (собственный) обработчик команд (см.ниже) ribBtn.CommandHandler = new RibbonCommandHandler(); // Ориентация кнопки ribBtn.Orientation = System.Windows.Controls.Orientation.Horizontal; // Размер кнопки ribBtn.Size = RibbonItemSize.Large; /* Т.к. используем размер кнопки Large, то добавляем * большое изображение с помощью специальной функции (см.ниже) */ ribBtn.LargeImage = LoadImage("icon_32"); // Показывать картинку ribBtn.ShowImage = true; // Показывать текст ribBtn.ShowText = true; // Заполняем содержимое всплывающей подсказки tt.Content = "Я кнопочка №1. Нажми меня и я нарисую отрезок"; // Подключаем подсказку к кнопке ribBtn.ToolTip = tt; // Добавляем кнопку в RibbonSplitButton risSplitBtn.Items.Add(ribBtn); #endregion // Делаем текущей первую кнопку risSplitBtn.Current = ribBtn; // Далее создаем вторую кнопку по аналогии с первой #region Кнопка-пример №2 tt = new RibbonToolTip(); tt.IsHelpEnabled = false; ribBtn = new RibbonButton(); ribBtn.CommandParameter = tt.Command = "_Pline"; ribBtn.Name = "ExampleButton2"; ribBtn.Text = tt.Title = "Кнопка-пример №2"; ribBtn.CommandHandler = new RibbonCommandHandler(); ribBtn.Orientation = System.Windows.Controls.Orientation.Horizontal; ribBtn.Size = RibbonItemSize.Large; ribBtn.LargeImage = LoadImage("icon_32"); ribBtn.ShowImage = true; ribBtn.ShowText = true; tt.Content = "Я кнопочка №2. Нажми меня и я нарисую полилинию"; ribBtn.ToolTip = tt; risSplitBtn.Items.Add(ribBtn); #endregion // Добавляем RibbonSplitButton в нашу панель ribSourcePanel.Items.Add(risSplitBtn); // Создаем новую строку RibbonRowPanel ribRowPanel = new RibbonRowPanel(); // Создаем третью кнопку по аналогии с предыдущими. // Отличие только в размере кнопки (и картинки) #region Кнопка-пример №3 tt = new RibbonToolTip(); tt.IsHelpEnabled = false; ribBtn = new RibbonButton(); ribBtn.CommandParameter = tt.Command = "_Circle"; ribBtn.Name = "ExampleButton3"; ribBtn.Text = tt.Title = "Кнопка-пример №3"; ribBtn.CommandHandler = new RibbonCommandHandler(); ribBtn.Orientation = System.Windows.Controls.Orientation.Vertical; ribBtn.Size = RibbonItemSize.Standard; ribBtn.Image = LoadImage("icon_16"); ribBtn.ShowImage = true; ribBtn.ShowText = false; tt.Content = "Я кнопочка №3. Нажми меня и я нарисую кружочек"; ribBtn.ToolTip = tt; ribRowPanel.Items.Add(ribBtn); #endregion // Добавляем строку в нашу панель ribSourcePanel.Items.Add(ribRowPanel); } catch (System.Exception ex) { Autodesk.AutoCAD.ApplicationServices.Application. DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message); } } // Получение картинки из ресурсов // Данная функция найдена на просторах интернет System.Windows.Media.Imaging.BitmapImage LoadImage(string ImageName) { return new System.Windows.Media.Imaging.BitmapImage( new Uri("pack://application:,,,/ACadRibbon;component/" + ImageName + ".png")); } /* Собственный обраотчик команд * Это один из вариантов вызова команды по нажатию кнопки */ class RibbonCommandHandler : System.Windows.Input.ICommand { public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged;
public void Execute(object parameter) { Document doc = acadApp.DocumentManager.MdiActiveDocument; if (parameter is RibbonButton) { // Просто берем команду, записанную в CommandParameter кнопки // и выпоняем её используя функцию SendStringToExecute RibbonButton button = parameter as RibbonButton; acadApp.DocumentManager.MdiActiveDocument.SendStringToExecute( button.CommandParameter + " ", true, false, true); } } } } }
|
6. Все - код готов. Компилируем его (Построение - Построить решение), открываем автокад 2010, выполняем команду NETLOAD и выбираем наш плагин ...\ACadRibbon\ACadRibbon\bin\Debug\ACadRibbon.dll.
И сразу же после загрузки мы увидим, что у нас добавилась новая вкладка на ленте

Данный вариант активно используется в моем плагине ModPlus и пока не вызвал нареканий
Также вы можете скачать исходник примера.
Надеюсь пример окажется полезным!
Автор статьи: Александр Пекшев aka Modis
Комментарии
{
if (tab.Id.Equals("RibbonExample_ID") & tab.Title.Equals("RibbonExample"))
{
// И если у вкладки совпадает идентификатор и заголовок, то удаляем эту вкладку
ribCntrl.Tabs.Remove(tab);
// Отключаем обработчик событий
acadApp.SystemVariableChanged -= new SystemVariableChangedEventHandler(acadApp_SystemVariableChanged);
// Останавливаем итерацию
break;
}
}
не проще ч/з LINQ?
У меня с LINQ еще пока мало опыта работы :) Я понимаю, что можно одной строчкой написать, однако не знаю как туда "запихнуть" отключение обработчика событий.
Да и вообще - это все вариантность )) Может строк и больше, чем можно, но главное, чтобы работало!
Как раз таки это самый разумный и правильный вариант. Создавать CUI гораздо удобней средствами AutoCAD, если знаешь как. А чтобы знать - нужно читать справку. А уже загружать частичный CUI можно и программно.
Цель этой статьи - описать вариант, который очень сложно найти в инете (тем более на-русском языке), да с учетом некоторых особенностей, которые так-же не очень легко найти
Авторизоваться