I received this question from Vikas Hajela a few days ago:
I am developing a plugin in C#, which will add a link in Quick Access Toolbar in AutoCAD. […] My problem is that I don’t know how to add a link into existing Quick Access Toolbar and Menu Bar in AutoCAD using ObjectARX SDK and C#. Also I want that on click of that link it should open a new window.
We’re going to look at some code that – on initialization of the application – adds an item to AutoCAD’s “Big A” Application Menu and to the Quick Access Toolbar (the “quick launch” toolbar towards the left of the main application window’s title bar). To solve this I borrowed some code and techniques from a couple of DevNotes on the ADN site: Use the .NET API to add a menu Item to Application Menu (big A) and The arrow of the Dialog Launcher button on my Ribbon panel does not show. It should be noted that this code will need at least AutoCAD 2010 to execute.
Here’s the C# code. To make it work you will need to place a couple of .ico files in your DLL’s folder (these could very easily be stored as resources in your application’s project, which is left as an exercise for the reader).
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Windows;
using System.Windows.Media.Imaging;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using System;
namespace AppMenus
{
public class ExtApp : IExtensionApplication
{
// String constants
const string appText = "Browse Photosynth";
const string appDesc =
"Browse the Photosynth site and import point " +
"clouds into AutoCAD.";
const string smallFile = "Browser-16x16.ico";
const string largeFile = "Browser-32x32.ico";
const string bpCmd = "_.BP";
public void Initialize()
{
// We defer the creation of our Application Menu to when
// the menu is next accessed
ComponentManager.ApplicationMenu.Opening +=
new EventHandler<EventArgs>(ApplicationMenu_Opening);
// We defer the creation of our Quick Access Toolbar item
// to when the application is next idle
Application.Idle += new EventHandler(Application_OnIdle);
}
public void Terminate()
{
// Assuming these events have fired, they have already
// been removed
ComponentManager.ApplicationMenu.Opening -=
new EventHandler<EventArgs>(ApplicationMenu_Opening);
Application.Idle -= new EventHandler(Application_OnIdle);
}
void Application_OnIdle(object sender, EventArgs e)
{
// Remove the event when it is fired
Application.Idle -= new EventHandler(Application_OnIdle);
// Add our Quick Access Toolbar item
AddQuickAccessToolbarItem();
}
void ApplicationMenu_Opening(object sender, EventArgs e)
{
// Remove the event when it is fired
ComponentManager.ApplicationMenu.Opening -=
new EventHandler<EventArgs>(ApplicationMenu_Opening);
// Add our Application Menu
AddApplicationMenu();
}
private void AddApplicationMenu()
{
ApplicationMenu menu = ComponentManager.ApplicationMenu;
if (menu != null && menu.MenuContent != null)
{
// Create our Application Menu Item
ApplicationMenuItem mi = new ApplicationMenuItem();
mi.Text = appText;
mi.Description = appDesc;
mi.LargeImage = GetIcon(largeFile);
// Attach the handler to fire out command
mi.CommandHandler = new AutoCADCommandHandler(bpCmd);
// Add it to the menu content
menu.MenuContent.Items.Add(mi);
}
}
private void AddQuickAccessToolbarItem()
{
Autodesk.Windows.ToolBars.QuickAccessToolBarSource qat =
ComponentManager.QuickAccessToolBar;
if (qat != null)
{
// Create our Ribbon Button
RibbonButton rb = new RibbonButton();
rb.Text = appText;
rb.Description = appDesc;
rb.Image = GetIcon(smallFile);
// Attach the handler to fire out command
rb.CommandHandler = new AutoCADCommandHandler(bpCmd);
// Add it to the Quick Access Toolbar
qat.AddStandardItem(rb);
}
}
private System.Windows.Media.ImageSource GetIcon(string ico)
{
// We'll look for our icons in the folder of the assembly
// (we could also use a resources, of course)
string path =
Path.GetDirectoryName(
Assembly.GetExecutingAssembly().Location
);
// Check our .ico file exists
string fileName = path + "\\" + ico;
if (File.Exists(fileName))
{
// Get access to it via a stream
Stream fs =
new FileStream(
fileName,
FileMode.Open,
FileAccess.Read,
FileShare.Read
);
using (fs)
{
// Decode the contents and return them
IconBitmapDecoder dec =
new IconBitmapDecoder(
fs,
BitmapCreateOptions.PreservePixelFormat,
BitmapCacheOption.Default
);
return dec.Frames[0];
}
}
return null;
}
}
// A class to fire commands to AutoCAD
public class AutoCADCommandHandler
: System.Windows.Input.ICommand
{
private string _command = "";
public AutoCADCommandHandler(string cmd)
{
_command = cmd;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged;
#pragma warning restore 67
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
if (!String.IsNullOrEmpty(_command))
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
doc.SendStringToExecute(
_command + " ", false, false, false
);
}
}
}
}
A few comments on the code:
- We delay the creation of both the Application Menu and Quick Access Toolbar items, but for different reasons:
- The Application Menu only gets created when it’s first accessed, so we need to wait for that to happen before adding our item
- The Quick Access Toolbar item cannot be created on Initialize(), as our module may have been loaded on AutoCAD startup and the QAT may not yet be ready
- We have temporarily disabled a warning (CS0067) which tells us that an event handler – which we need to implement to complete the ICommand interface – is not used in our code
Now let’s see it in action. As you can probably tell from the code, it’s basically adding a “launch” UI to the application I showed in the last post.
When we NETLOAD the application (or auto-load it on AutoCAD start-up), we see our Quick Access Toolbar icon gets added:
We get more information when we hover over it:
We also have our new Application Menu item:
When we select either item, our BP command – as implemented previously – gets launched. Vikas had requested a dialog be shown, but I strongly recommend that this is implemented via a command rather than being displayed directly in the code. This just helps AutoCAD synchronise its user interface appropriately and will avoid lots of subtle issues you might otherwise hit.