A big thanks to Viru Aithal, from our DevTech India team, for providing the code that inspired this post.
Update: it turns out I didn't look deeply enough into the origins of the code behind this post. The code that inspired Viru's code that inspired mine came from our old friend Mike Tuersley, who's delivering a class on customizing the Options dialog at this year's AU (in just over a week). Thanks, Mike! :-)
One way that applications often want to integrate with AutoCAD is via the dialog displayed by the OPTIONS command. Luckily it's relatively easy to add your own custom tab to this dialog using .NET, allowing users to view and modify your application's settings. I'm going to take a similar approach to this topic as I did with task dialogs: part 1 (this post) focuses on a very simple implementation to show the basic capabilities of the mechanism, and part 2 will look at a more "real world" implementation that goes as far as providing access to and storing some actual (well, realistic, at least) application settings.
The first step when implementing your custom tab is to add a User Control to your project. As this activity goes beyond just copy & pasting code, here's a sample project demonstrating the technique shown in this post.
Once you have a User Control in your project (ours has the default name UserControl1), you can design it to add in some custom controls - whether buttons, checkboxes, combos, or more complex controls such as a property grid (something I'll show in the next post). Here's the design I settled for, containing just a few, simple controls, each added with its default name:
You can clearly adjust the layout, as you wish, with anchor points, docking, etc. It's worth setting the "modifier" for each of the controls you've added to public or internal, assuming you want to access their state directly from elsewhere in the project. The other way is to expose them via public properties, but that's really up to you.
Here's the code that goes behind this dialog, the important activity being to set the "dirty" flag for the control when a particular component control is selected:
using System;
using System.Windows.Forms;
using Autodesk.AutoCAD.ApplicationServices;
namespace OptionsDlg
{
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(
object sender,
EventArgs e
)
{
TabbedDialogExtension.SetDirty(this, true);
}
private void checkBox1_CheckedChanged(
object sender,
EventArgs e
)
{
TabbedDialogExtension.SetDirty(this, true);
}
private void comboBox1_SelectedIndexChanged(
object sender,
EventArgs e
)
{
TabbedDialogExtension.SetDirty(this, true);
}
}
}
Now for our rest of the implementation. We're going to add our custom tab on load of the application (see these two previous posts for the approach for running code on application load), so there's no need to implement a command. Here's the C# code:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
[assembly:
ExtensionApplication(
typeof(OneNeedsOptions.Initialization)
)
]
namespace OneNeedsOptions
{
class Initialization : IExtensionApplication
{
static OptionsDlg.UserControl1 _uc;
public void Initialize()
{
Application.DisplayingOptionDialog +=
new TabbedDialogEventHandler(
Application_DisplayingOptionDialog
);
}
public void Terminate()
{
Application.DisplayingOptionDialog -=
new TabbedDialogEventHandler(
Application_DisplayingOptionDialog
);
}
private static void ButtonPressedMessage(string name)
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage(name + " button pressed.\n");
}
private static void ShowSettings()
{
if (_uc != null)
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
ed.WriteMessage(
"Settings were: checkbox contains {0}," +
" combobox contains {1}\n",
_uc.checkBox1.Checked,
_uc.comboBox1.Text
);
}
}
private static void OnOK()
{
ButtonPressedMessage("OK");
ShowSettings();
}
private static void OnCancel()
{
ButtonPressedMessage("Cancel");
}
private static void OnHelp()
{
ButtonPressedMessage("Help");
}
private static void OnApply()
{
ButtonPressedMessage("Apply");
ShowSettings();
}
private static void Application_DisplayingOptionDialog(
object sender,
TabbedDialogEventArgs e
)
{
if (_uc == null)
{
_uc = new OptionsDlg.UserControl1();
TabbedDialogExtension tde =
new TabbedDialogExtension(
_uc,
new TabbedDialogAction(OnOK),
new TabbedDialogAction(OnCancel),
new TabbedDialogAction(OnHelp),
new TabbedDialogAction(OnApply)
);
e.AddTab("My Custom Settings", tde);
}
}
}
}
When we build the application, load it into AutoCAD and run the OPTIONS command, we should see our custom settings appear in our new tab:
As the various settings in the dialog get modified (even the button being clicked), you'll see the "Apply" button get enabled - a direct result of the settings on the control being considered "dirty". As you select the various buttons belonging to the dialog you should see messages on the command-line, as the respective events get fired.
In the next post we'll extend this example to provide access to persistent properties, as you would want to do from a real application.

Subscribe via RSS
Hi Kean,
For this posting, would you be willing to post the entire solution? I am having difficulties understanding the various source files that are involved and how everything ties together.
Ron
Posted by: Ron Hanson | November 19, 2008 at 04:38 PM
Hi Ron,
Certainly - the link is in the post, but here it is again.
Regards,
Kean
Posted by: Kean | November 19, 2008 at 04:40 PM
Hi Kean,
I was wondering about testing of AutoCAD plug-ins in general. How do you write unit tests if you depend on the AutoCAD API which doesn't run independently of AutoCAD? In general, what's your recommended strategy for testing a plug-in?
Thanks.
Posted by: namin | November 19, 2008 at 08:56 PM
Hi Kean,
I've just started Autocad automation with C# and I've got a question. Is there a way to get the max X, minX, max Y and min Y values of all entities within a drawing?
Imagine I have a drawing with some entities like arcs, lines or even polylines. I'd like to know what are those values so I could draw a rectangle to fit all entities within it. Did you get it?
Tks
Posted by: Filipe Scur | November 19, 2008 at 10:23 PM
Kean,
Thanks for keeping up the interesting posts.
Regards
kwb
Posted by: Kerry Brown | November 20, 2008 at 12:53 AM
@Tks,
You can use the property GeometricExtents of an entity. If you have a list of entities, you can easily loop through them to figure out the bounding box. Here is an example in F#.
Posted by: Nada Amin | November 20, 2008 at 03:50 AM
Hi Nada,
Thanks for responding to Filipe's question.
We (meaning Autodesk) use various methods to test our software, but for unit testing we have a large set of scripts that exercise the various APIs. These have to run within AutoCAD, of course.
Having some kind of test suite for your own internal methods would seem a logical way to approach this, from my perspective.
I'd be interested to hear how others approach this.
Kean
Posted by: Kean | November 21, 2008 at 09:57 AM
Thanks a lot guys! And Kean, you're doing such a great job over here.
Thanks again.
Posted by: Filipe | November 21, 2008 at 12:27 PM
Hi, when are providing an article on status bar i.e trayitem on pene custom icons. I haveen trying the following code without sucess. The icon gets loaded but disappears after working with cad eg aftaer minimizing the drawing.
attached code..
Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.Runtime
Public Class Class1
Implements Autodesk.AutoCAD.Runtime.IExtensionApplication
Overridable Sub Initialize() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Initialize
AddTrayItems()
End Sub
Function GetEmbeddedIcon(ByVal strName As String) As Drawing.Icon
Return New Drawing.Icon(System.Reflection.Assembly.GetExecutingAssembly.GetManifestResourceStream(strName))
End Function
Sub AddTrayItems()
Try
Dim doc As Document = Application.DocumentManager.MdiActiveDocument
Dim ti As New TrayItem()
ti.ToolTipText = "MyCadAddon Info"
ti.Icon = GetEmbeddedIcon("MyCadAddon.blue.ico")
Application.StatusBar.TrayItems.Add(ti)
Dim bw As New TrayItemBubbleWindow()
bw.Title = "MyCadAddon Application:"
'bw.HyperText = ""
' bw.HyperLink = ""
bw.Text = "This application has been loaded sucessfully"
bw.IconType = IconType.Information
ti.ShowBubbleWindow(bw)
Catch
End Try
End Sub
Overridable Sub Terminate() Implements Autodesk.AutoCAD.Runtime.IExtensionApplication.Terminate
End Sub
End Class
Posted by: eddie | November 22, 2008 at 12:51 PM
Sorry - I don't have time to look into this. Perhaps someone on the AutoCAD .NET Discussion Group can help (or the ADN team, if you're a member).
It looks as though you adapted the technique from this previous post. I assume you're not also implementing the Closed event handler, which removes the icon in that example?
Kean
Posted by: Kean | November 24, 2008 at 07:49 AM
About the unit testing:
A tool exists, Gallio that (among many other features) runs MbUnit tests in the AutoCAD context.
I have not yet had the time to look into it, but it seems interesting. The documentation on AutoCAD integration is scarce though, so trying it out is the only way to go right now.
Another option, from my point of view, 'Contracting' all your functionality. This means writing interfaces for the self-written functionality, and mocking it in the unit tests.
This way you don't need to use the acad dll's, but it does create a LOT of overhead for your code. It means proxying all acad-functionality you need, again in your own code.
If anyone has any other options (or links), please share...
Posted by: Bert | December 24, 2008 at 10:12 AM