AutoCAD Events and Overrules
Use this skill when subscribing to database, document, application, or editor events, or when overriding default entity behavior via ObjectOverrule, DrawableOverrule, TransformOverrule, or GripOverrule.
Database Events
Database events fire when objects are added, modified, or erased. Subscribe via the Database object.
Database db = HostApplicationServices.WorkingDatabase;
db.ObjectAppended += OnObjectAppended;
db.ObjectErased += OnObjectErased;
db.ObjectModified += OnObjectModified;
db.ObjectOpenedForModify += OnObjectOpenedForModify;
ObjectAppended
Fires after a new object is added to the database (after AddNewlyCreatedDBObject).
private void OnObjectAppended(object sender, ObjectEventArgs e)
{
ObjectId id = e.DBObject.ObjectId;
// e.DBObject is the newly added object — read-only at this point
}
ObjectErased
Fires when an object is erased or unerased. Check e.Erased to distinguish.
private void OnObjectErased(object sender, ObjectErasedEventArgs e)
{
if (e.Erased)
{
// Object was erased
}
else
{
// Object was unerased (undo)
}
}
ObjectModified / ObjectOpenedForModify
ObjectOpenedForModify fires when opened for write (before changes). ObjectModified fires after modification and close. Both use ObjectEventArgs with e.DBObject.
Document Events
Subscribe via individual Document objects. Also: CommandFailed, LispWillStart, LispEnded.
Document doc = Application.DocumentManager.MdiActiveDocument;
doc.CommandWillStart += OnCommandWillStart;
doc.CommandEnded += OnCommandEnded;
doc.CommandCancelled += OnCommandCancelled;
DocumentLockModeChanged
Fires when the document lock mode changes (e.g., entering or leaving a command context).
doc.LockModeChanged += OnLockModeChanged;
private void OnLockModeChanged(object sender, DocumentLockModeChangedEventArgs e)
{
string commandName = e.GlobalCommandName;
DocumentLockMode currentMode = e.CurrentMode;
// Veto the lock change: e.Veto();
}
DocumentCollection Events
Subscribe via Application.DocumentManager. Also: DocumentToBeActivated, DocumentToBeDeactivated, DocumentCreateStarted, DocumentLockModeWillChange.
DocumentCollection docMgr = Application.DocumentManager;
docMgr.DocumentCreated += OnDocumentCreated;
docMgr.DocumentToBeDestroyed += OnDocumentToBeDestroyed;
docMgr.DocumentActivated += OnDocumentActivated;
docMgr.DocumentBecameCurrent += OnDocumentBecameCurrent;
Typical Document Lifecycle Handler
private void OnDocumentCreated(object sender, DocumentCollectionEventArgs e)
{
// Subscribe to per-document events on the new document
e.Document?.Database.ObjectModified += OnObjectModified;
}
private void OnDocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
{
// Unsubscribe per-document events before it closes
e.Document?.Database.ObjectModified -= OnObjectModified;
}
Application Events
Global events on the Application class or SystemVariableChanged on Application.
Application.SystemVariableChanged += OnSysVarChanged;
Application.Idle += OnIdle;
Application.QuitWillStart += OnQuitWillStart;
SystemVariableChanged
private void OnSysVarChanged(object sender, SystemVariableChangedEventArgs e)
{
if (e.Name == "CLAYER" && e.Changed)
{
// Current layer changed — update UI
}
}
Idle
Fires when AutoCAD is idle (no commands running). Useful for deferred processing.
private void OnIdle(object sender, EventArgs e)
{
Application.Idle -= OnIdle; // unsubscribe to avoid repeated firing
ProcessDeferredWork();
}
Editor Events
Subscribe via Editor for selection and input monitoring.
Editor ed = doc.Editor;
ed.SelectionAdded += OnSelectionAdded;
ed.SelectionRemoved += OnSelectionRemoved;
ed.PointMonitor += OnPointMonitor;
SelectionAdded
Fires when entities are added to the current selection set.
private void OnSelectionAdded(object sender, SelectionAddedEventArgs e)
{
ObjectId[] addedIds = e.AddedObjects.GetObjectIds();
// Inspect or veto selection:
// e.Remove(index) to reject specific entities
}
PointMonitor
Fires continuously as the cursor moves. Use sparingly — it runs on every mouse move.
private void OnPointMonitor(object sender, PointMonitorEventArgs e)
{
Point3d currentPoint = e.Context.ComputedPoint;
// Get entities under the cursor
FullSubentityPath[] paths = e.Context.GetPickedEntities();
// Append to tooltip
e.AppendToolTipText("Custom tooltip text");
}
ObjectOverrule
Override fundamental object operations: Open, Close, Erase, DeepClone.
public class MyObjectOverrule : ObjectOverrule
{
public override void Erase(DBObject dbObject, bool erasing)
{
if (erasing)
{
// Prevent erase by throwing ErrorStatus.NotAllowedForThisProxy
}
base.Erase(dbObject, erasing);
}
// Also overridable: Open, Close, DeepClone
}
DrawableOverrule
Customize how entities are drawn. Override WorldDraw and/or ViewportDraw.
public class MyDrawableOverrule : DrawableOverrule
{
public override bool WorldDraw(Drawable drawable, WorldDraw wd)
{
// Cast to the specific entity type
Circle circle = drawable as Circle;
if (circle == null) return base.WorldDraw(drawable, wd);
// Draw default geometry first
bool result = base.WorldDraw(drawable, wd);
// Add custom graphics — draw a crosshair at center
double r = circle.Radius * 0.5;
Point3d c = circle.Center;
wd.Geometry.WorldLine(
new Point3d(c.X - r, c.Y, c.Z), new Point3d(c.X + r, c.Y, c.Z));
wd.Geometry.WorldLine(
new Point3d(c.X, c.Y - r, c.Z), new Point3d(c.X, c.Y + r, c.Z));
return result;
}
public override int SetAttributes(Drawable drawable, DrawableTraits traits)
{
int flags = base.SetAttributes(drawable, traits);
traits.Color = 1; // force red
return flags;
}
// ViewportDraw — override for per-viewport / view-dependent graphics
// vd.Context.IsPlotGeneration is true when printing
}
TransformOverrule
Control how entities respond to move, rotate, scale, and mirror operations.
public class MyTransformOverrule : TransformOverrule
{
public override void TransformBy(Entity entity, Matrix3d transform)
{
// Example: restrict movement to X axis only
Vector3d delta = Point3d.Origin.TransformBy(transform) - Point3d.Origin;
if (Math.Abs(delta.Y) > Tolerance.Global.EqualPoint)
{
Matrix3d xOnly = Matrix3d.Displacement(new Vector3d(delta.X, 0, 0));
base.TransformBy(entity, xOnly);
return;
}
base.TransformBy(entity, transform);
}
// Also overridable: Explode
}
GripOverrule
Define custom grip points and stretch behavior for entities.
public class MyGripOverrule : GripOverrule
{
public override void GetGripPoints(Entity entity,
GripDataCollection grips, double curViewUnitSize,
int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags)
{
base.GetGripPoints(entity, grips, curViewUnitSize,
gripSize, curViewDir, bitFlags);
// Add a custom grip at midpoint of a line
Line line = entity as Line;
if (line != null)
{
GripData grip = new GripData();
grip.GripPoint = line.StartPoint +
(line.EndPoint - line.StartPoint) * 0.5;
grips.Add(grip);
}
}
public override void MoveGripPointsAt(Entity entity,
GripDataCollection grips, Vector3d offset, MoveGripPointsFlags bitFlags)
{
base.MoveGripPointsAt(entity, grips, offset, bitFlags);
}
}
Overrule Registration
AddOverrule / RemoveOverrule
// Register — third parameter: false = end of chain, true = beginning
Overrule.AddOverrule(RXObject.GetClass(typeof(Circle)), drawOverrule, false);
// Enable the overrule system globally (required)
Overrule.Overruling = true;
// Remove when done (e.g., in IExtensionApplication.Terminate)
Overrule.RemoveOverrule(RXObject.GetClass(typeof(Circle)), drawOverrule);
SetIdFilter
Restrict an overrule to specific objects by ObjectId.
overrule.SetIdFilter(new ObjectId[] { circleId1, circleId2 });
SetXDataFilter
Restrict an overrule to objects with specific XData.
overrule.SetXDataFilter(new string[] { "MyAppName" });
Toggling Overrules
Overrule.Overruling = false disables all overrules globally without removing them. Set back to true to re-enable.
Plugin Lifecycle Example
Manage events and overrules in IExtensionApplication.Initialize / Terminate.
public class MyPlugin : IExtensionApplication
{
private static MyDrawableOverrule _drawOverrule;
public void Initialize()
{
Application.DocumentManager.DocumentCreated += OnDocCreated;
Application.DocumentManager.DocumentToBeDestroyed += OnDocClosing;
_drawOverrule = new MyDrawableOverrule();
Overrule.AddOverrule(
RXObject.GetClass(typeof(Circle)), _drawOverrule, false);
Overrule.Overruling = true;
// Subscribe to the already-open document
Document doc = Application.DocumentManager.MdiActiveDocument;
if (doc != null)
doc.Database.ObjectModified += OnObjectModified;
}
public void Terminate()
{
Application.DocumentManager.DocumentCreated -= OnDocCreated;
Application.DocumentManager.DocumentToBeDestroyed -= OnDocClosing;
if (_drawOverrule != null)
Overrule.RemoveOverrule(
RXObject.GetClass(typeof(Circle)), _drawOverrule);
}
private void OnDocCreated(object sender, DocumentCollectionEventArgs e)
=> e.Document?.Database.ObjectModified += OnObjectModified;
private void OnDocClosing(object sender, DocumentCollectionEventArgs e)
=> e.Document?.Database.ObjectModified -= OnObjectModified;
private static void OnObjectModified(object sender, ObjectEventArgs e) { }
}
Gotchas
- Never modify the database inside
ObjectModifiedorObjectAppendedhandlers — the transaction is still active and re-entrant modifications crash. Defer work toApplication.Idle. - Always unsubscribe events when documents close or the plugin terminates. Leaked handlers cause null reference exceptions.
PointMonitorruns on every mouse move — keep the handler fast. Cache results and only recalculate when the cursor moves significantly.Overrule.Overrulingis a global toggle —falsedisables all overrules in the session, including other plugins'.ObjectErasedfires for both erase and unerase — always checke.Erased. Undo triggers unerase events.SetIdFilterreferences are not weak — erased objects stay in the filter. Clear or reset when targets change.DrawableOverrule.WorldDrawmust callbase.WorldDraw— skipping it makes the entity disappear.- Thread safety — handlers run on the main thread. Use
DocumentManager.ExecuteInApplicationContextfor cross-thread work; never accessMdiActiveDocumentfrom background threads. TransformOverrule.TransformByreceives the cumulative transform — displacement for move, rotation matrix for rotate.GripOverrule.GetGripPointswithGripDataCollectionis the newer API; the olderPoint3dCollectionoverload lacks custom grip styling.- Overrule class type must match the runtime class —
typeof(Entity)for broad match,typeof(Line)for specific. Derived classes inherit parent overrules. SelectionAddedcan veto viae.Remove(index)— out-of-range index corrupts the selection set silently.SystemVariableChangedfires even when unchanged — checke.Changedto filter no-ops.
Related Skills
acad-database-operations— transactions and object access patterns used in event handlersacad-editor-input—SelectionAddedandPointMonitorevents extend editor interactionacad-geometry—Point3d,Matrix3d, andVector3dused throughout transform and grip overrulesacad-blocks— overrules can targetBlockReferenceentities for custom grip and display behavior