| name | acad-events-overrules |
| description | Database/document/editor events, ObjectOverrule, DrawableOverrule, TransformOverrule |
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;
}
ObjectErased
Fires when an object is erased or unerased. Check e.Erased to distinguish.
private void OnObjectErased(object sender, ObjectErasedEventArgs e)
{
if (e.Erased)
{
}
else
{
}
}
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;
}
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)
{
e.Document?.Database.ObjectModified += OnObjectModified;
}
private void OnDocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
{
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)
{
}
}
Idle
Fires when AutoCAD is idle (no commands running). Useful for deferred processing.
private void OnIdle(object sender, EventArgs e)
{
Application.Idle -= OnIdle;
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();
}
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;
FullSubentityPath[] paths = e.Context.GetPickedEntities();
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)
{
}
base.Erase(dbObject, erasing);
}
}
DrawableOverrule
Customize how entities are drawn. Override WorldDraw and/or ViewportDraw.
public class MyDrawableOverrule : DrawableOverrule
{
public override bool WorldDraw(Drawable drawable, WorldDraw wd)
{
Circle circle = drawable as Circle;
if (circle == null) return base.WorldDraw(drawable, wd);
bool result = base.WorldDraw(drawable, wd);
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;
return flags;
}
}
TransformOverrule
Control how entities respond to move, rotate, scale, and mirror operations.
public class MyTransformOverrule : TransformOverrule
{
public override void TransformBy(Entity entity, Matrix3d transform)
{
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);
}
}
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);
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
Overrule.AddOverrule(RXObject.GetClass(typeof(Circle)), drawOverrule, false);
Overrule.Overruling = true;
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;
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
ObjectModified or ObjectAppended handlers — the transaction is still active and re-entrant modifications crash. Defer work to Application.Idle.
- Always unsubscribe events when documents close or the plugin terminates. Leaked handlers cause null reference exceptions.
PointMonitor runs on every mouse move — keep the handler fast. Cache results and only recalculate when the cursor moves significantly.
Overrule.Overruling is a global toggle — false disables all overrules in the session, including other plugins'.
ObjectErased fires for both erase and unerase — always check e.Erased. Undo triggers unerase events.
SetIdFilter references are not weak — erased objects stay in the filter. Clear or reset when targets change.
DrawableOverrule.WorldDraw must call base.WorldDraw — skipping it makes the entity disappear.
- Thread safety — handlers run on the main thread. Use
DocumentManager.ExecuteInApplicationContext for cross-thread work; never access MdiActiveDocument from background threads.
TransformOverrule.TransformBy receives the cumulative transform — displacement for move, rotation matrix for rotate.
GripOverrule.GetGripPoints with GripDataCollection is the newer API; the older Point3dCollection overload 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.
SelectionAdded can veto via e.Remove(index) — out-of-range index corrupts the selection set silently.
SystemVariableChanged fires even when unchanged — check e.Changed to filter no-ops.
Related Skills
acad-database-operations — transactions and object access patterns used in event handlers
acad-editor-input — SelectionAdded and PointMonitor events extend editor interaction
acad-geometry — Point3d, Matrix3d, and Vector3d used throughout transform and grip overrules
acad-blocks — overrules can target BlockReference entities for custom grip and display behavior