When working with AutoCAD .NET, running code from a modeless dialog can sometimes be inconvenient. In particular, triggering commands or executing methods in the correct context often requires additional setup, which makes quick testing more difficult.
To simplify this workflow, this article demonstrates an approach to dynamically build a palette containing buttons linked to commands and methods defined in the current project.
Idea Overview
- Use a custom attribute to tag methods that should appear in the palette
- Use reflection to scan the assembly and find those methods
- Dynamically generate UI buttons for each tagged method
- Execute commands or methods depending on how they are defined
This approach allows you to quickly expose internal tools without building a full UI each time.
Tagging Methods with a Custom Attribute
- [PaletteMethod] is used to mark methods for inclusion
- [CommandMethod] is used for AutoCAD commands
This enables two execution paths:
- Methods without a command → invoked via reflection
- Methods with a command → executed in AutoCAD command context
Execution Strategy
- Commands are executed using
ExecuteInCommandContextAsync() - Regular methods are invoked using
MethodInfo.Invoke()
Full Implementation
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Windows;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using WinForms = System.Windows.Forms;
namespace ModelessDialogs
{
[AttributeUsage(AttributeTargets.Method)]
public class PaletteMethod : Attribute { }
public class Commands
{
private static PaletteSet _ps = null;
[PaletteMethod]
[CommandMethod("PC")]
public void PaletteCcommands()
{
if (_ps == null)
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
var ed = doc.Editor;
var asm = Assembly.GetExecutingAssembly();
var type = asm.GetType("ModelessDialogs.Commands");
if (type == null)
{
ed.WriteMessage("\nCould not find the command class.");
return;
}
var bs = new List<WinForms.Button>();
var i = 1;
foreach (var m in type.GetMethods())
{
var cmdName = "";
var palette = false;
foreach (var a in m.CustomAttributes)
{
if (a.AttributeType.Name == "CommandMethodAttribute")
{
cmdName = (string)a.ConstructorArguments[0].Value;
}
else if (a.AttributeType.Name == "PaletteMethod")
{
palette = true;
}
}
if (palette)
{
var b = new WinForms.Button();
b.SetBounds(50, 40 * i, 100, 30);
if (String.IsNullOrEmpty(cmdName))
{
b.Text = m.Name;
b.Click += (s, e) =>
{
var b2 = (WinForms.Button)s;
var mi = type.GetMethod(b2.Text);
if (mi != null)
{
mi.Invoke(this, null);
}
};
}
else
{
b.Text = cmdName;
b.Click += async (s, e) =>
{
var dm = Application.DocumentManager;
var doc2 = dm.MdiActiveDocument;
if (doc2 == null) return;
var ed2 = doc2.Editor;
await dm.ExecuteInCommandContextAsync(
async (obj) =>
{
await ed2.CommandAsync("_." + cmdName);
},
null
);
};
}
bs.Add(b);
i++;
}
}
var uc = new WinForms.UserControl();
uc.Controls.AddRange(bs.ToArray());
_ps = new PaletteSet("PC", new Guid("87374E16-C0DB-4F3F-9271-7A71ED921566"));
_ps.Add("CMDPAL", uc);
_ps.MinimumSize = new Size(200, (i + 1) * 40);
_ps.DockEnabled = (DockSides)(DockSides.Left | DockSides.Right);
}
_ps.Visible = true;
}
private static void DisplayMessage(string str, bool postPrompt = true)
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
doc.Editor.WriteMessage("\n{0}\n", str);
if (postPrompt)
doc.Editor.PostCommandPrompt();
}
[PaletteMethod]
public void Method1()
{
DisplayMessage("Method 1");
}
[PaletteMethod]
public void Method2()
{
DisplayMessage("Method 2");
}
[PaletteMethod]
public void Method3()
{
DisplayMessage("Method 3");
}
[PaletteMethod]
public void Method4()
{
DisplayMessage("Method 4");
}
[PaletteMethod]
[CommandMethod("TEST")]
public static void CommandTest()
{
DisplayMessage("This is a command!", false);
}
}
}
Why This Approach is Useful
- Quickly test logic without building a full UI
- Expose internal tools dynamically
- Handle modeless dialog execution scenarios
- Reduce repetitive development effort
Conclusion
This approach provides a flexible way to dynamically expose commands and methods through a palette. It is especially useful for debugging and rapid development workflows in AutoCAD.
