嘤嘤嘤写了一堆。网页一卡都没了……草稿里也找不到……以前会有定时保存的……可能修改不会有吧额……那就再开一个避免重蹈覆辙……续上面的估计没有第一次漂亮了哎就当再次复习了四、图元操作万能套路创建 → new → AppendEntity → 提交// 标准创建流程 Line line new Line(p1, p2); // 1. new 创建 tr.AddNewlyCreatedDBObject(line, true); // 2. 加入数据库 AppendEntity(line); // 3. 加入空间可选写法 tr.Commit(); // 4. 提交读取 → tr.GetObject(id, ForRead)Entity ent tr.GetObject(id, OpenMode.ForRead) as Entity;修改 → tr.GetObject(id,ForWrite) → 改属性 → 提交Entity ent tr.GetObject(id, OpenMode.ForWrite); // 必须写打开 ent.ColorIndex 1; // 修改 tr.Commit(); // 提交删除 → tr.GetObject(id,ForWrite) → ent.Erase() → 提交Entity ent tr.GetObject(id, OpenMode.ForWrite); ent.Erase(); // 删除 tr.Commit(); // 提交五、几何相关的Point2d / Point3d 是结构体Struct不是类Point3d a new Point3d(10, 20, 30); Point3d b a; // 复制一份全新的 b.X 999; // 改 b // a 依然是 10,20,30 // b 变成 999,20,30 Point2d p1 new Point2d(0,0); Point2d p2 p1; // 复制值 p2.X 100; // p1 还是 (0,0)顺便提一嘴官网看到带美元符号的不是付费的意思是静态static的意思在Matrix3d这里基本静态的是创建矩阵非静态实例的是操作已有矩阵。另外一般大写的是C#小写的是C的例如GetDistanceTo和getDistanceTo【创建】Point2d p2 new Point2d(x, y); Point3d p3 new Point3d(x, y, z); Point3d org Point3d.Origin; // (0,0,0)【取值】double x p3.X; double y p3.Y; double z p3.Z;【距离】using Autodesk.AutoCAD.Geometry; // 1. Point3d 点到点只能用 DistanceTo Point3d p1 new Point3d(0,0,0); Point3d p2 new Point3d(10,0,0); double distPoint p1.DistanceTo(p2); // ✅ 编译通过 // double distError p1.GetDistanceTo(p2); // ❌ 编译报错Point3d无此方法 // 2. Curve2d 点到曲线只能用 GetDistanceTo Line2d line2d new Line2d(new Point2d(0,0), new Point2d(10,0)); Point2d pt2d new Point2d(5, 5); double distCurve line2d.GetDistanceTo(pt2d); // ✅ 编译通过 // double distCurveError line2d.DistanceTo(pt2d); // ❌ 编译报错Curve2d无此方法类方法名作用Point3dDistanceTo(Point3d)点→点 直线距离Point2dDistanceTo(Point2d)点→点 直线距离Curve2dGetDistanceTo(Point2d)点→曲线 最短距离Curve3dGetDistanceTo(Point3d)点→曲线 最短距离Curve2dGetDistanceTo(Curve2d)曲线→曲线 距离Curve3dGetDistanceTo(Curve3d)曲线→曲线 距离【运算】Point3d p p1 p2; Point3d p p1 - p2; Point3d p p1 * 2; Point3d p p1 / 2; Vector3d vec p2 - p1; // 点-点向量 Point3d p p vec; // 点向量点【2D ↔ 3D 互转】Point2d p2 p3.Convert2d(); Point3d p3 p2.Convert3d();【矩阵变换】Point3d newP p.TransformBy(matrix);【相等判断】bool eq p1.IsEqualTo(p2, 0.001);【常用小公式】Point3d mid p1 (p2 - p1) * 0.5; // 中点 Vector3d dir (p2 - p1).Normalize(); // 单位方向 Point3d newP p1 dir * 10; // 沿方向走10坐标WCS 世界坐标系 为主WCS 世界坐标系原点000永远在同一个位置X向右Y向上Z垂直屏幕向外永远不会被用户修改、移动、旋转。API拿到的、数据库存的永远是WCS代码只认WCSUCS 用户坐标系人看着方便界面显示给用户看用户可以新建、旋转、移动、换视角。DCS显示坐标系/屏幕坐标系鼠标/屏幕交互左上角是原点往右 X往下 Y频频木像素极少用旋转、矩阵变换用 Matrix3dMatrix3d是一个“变形工具包黑盒子”他专门做移动、旋转、缩放、镜像、斜切这5件事。【固定模板】// 1. 定义一个点 Point3d pt new Point3d(10, 0, 0); // 2. 创建变形矩阵旋转/移动/缩放/镜像 Matrix3d mat Matrix3d.Rotation(...); // 3. 应用变换 → 得到新点 Point3d newPt pt.TransformBy(mat);注意TransformBy 只会计算新的点原来的点不变不直接影响图纸显示TransformBy 生成 Point3d 内存坐标不生成 DBPointPoint3d 不是对象是 struct值类型存在栈不受GC管理是内存里的点他是数据坐标。DBPoint 才是CAD图纸可见的存在堆Heap需要GCDBPoint点、Line线、Polyline多边形、Circle圆……// 1. 只是坐标看不见 Point3d pt new Point3d(10, 20, 0); // 2. 真正画到图上看得见 DBPoint dbPt new DBPoint(pt);【旋转】Matrix3d.Rotation(角度, Z轴, 中心点)// 角度弧度Math.PI 180° double angle Math.PI / 2; // 90度 Point3d basePoint new Point3d(0,0,0); // 旋转基点 // 构建旋转矩阵绕 Z 轴 平面旋转 Matrix3d matRot Matrix3d.Rotation(angle, Vector3d.ZAxis, basePoint); // 应用到点/实体 Point3d newPt pt.TransformBy(matRot); line.TransformBy(matRot);【移动】Matrix3d.Rotation(角度, Z轴, 中心点)Point3d fromPt new Point3d(0,0,0); Point3d toPt new Point3d(10,20,0); // 移动矩阵从 from → to Matrix3d matMove Matrix3d.Displacement(fromPt, toPt); // 应用 ent.TransformBy(matMove);【缩放】Matrix3d.Scaling(比例, Z轴, 中心点)double scale 2.0; // 放大2倍 Point3d basePoint new Point3d(0,0,0); Matrix3d matScale Matrix3d.Scaling(scale, Vector3d.ZAxis, basePoint); ent.TransformBy(matScale);【镜像】Matrix3d.Mirror(轴点1, 轴点2)// 镜像轴两点确定一条直线 Point3d p1 new Point3d(0,0,0); Point3d p2 new Point3d(0,10,0); Matrix3d matMirror Matrix3d.Mirror(p1, p2); ent.TransformBy(matMirror);【组合变换-矩阵叠加】矩阵是右乘 → 右边先直线左边后执行A *B先B后AMatrix3d mat1 旋转; Matrix3d mat2 移动; // 叠加先旋转 → 再移动 Matrix3d finalMat mat2 * mat1; // 一次变换完成 Point3d newPt pt.TransformBy(finalMat);长度、面积用几何方法【长度】line.Lengthcurve.Length【面积】poly.Arearegion.Area【距离】point.DistanceTo(point)cure.GetDistanceTo(point)【交点】curve.IntersectWith(curve)Curve2d.Intersect六、选择集定义选择集 用户在 CAD 里框选/点选的一堆图元线、圆、块、点…我们的代码要做4件事1、用PromptSelectionOptions设置选择提示2、用SelectionFilter设置过滤条件3、用ed.GetSelection()让用户选择4、遍历 SelectionSet5、拿到每一个图元的ObjectId6、用事务打开并操作模板【核心固定写法】using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; // 固定开头必写 Document doc Application.DocumentManager.MdiActiveDocument; Database db doc.Database; Editor ed doc.Editor; // 1. 设置选择提示 PromptSelectionOptions pso new PromptSelectionOptions(); pso.MessageForAdding 请选择图元; // 2. 设置过滤替换这里的条件即可 TypedValue[] filters { new TypedValue(0, LINE) // 只选直线 }; SelectionFilter filter new SelectionFilter(filters); // 3. 获取选择集 PromptSelectionResult res ed.GetSelection(pso, filter); // // ✅ 关键选择成功后 必须开启事务 才能操作图元 // if (res.Status PromptStatus.OK) { // 这一行绝对不能少 using (Transaction tr db.TransactionManager.StartTransaction()) { // 遍历选择集 foreach (SelectedObject so in res.Value) { ObjectId id so.ObjectId; // 打开图元必须在事务内 Entity ent tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent ! null) { ed.WriteMessage(\n选中图元 ent.Layer); } } tr.Commit(); // 提交事务 } }【只选 直线Line】TypedValue[] filters { new TypedValue(0, LINE) };【只选 圆Circle】TypedValue[] filters { new TypedValue(0, CIRCLE) };【只选 指定图层】TypedValue[] filters { new TypedValue(8, 轴线) };【只选 指定颜色图元】1红 2黄 3绿 4青 5蓝 6紫 7白TypedValue[] filters { new TypedValue(62, 1) };【只选 指定块】TypedValue[] filters { new TypedValue(0, INSERT), new TypedValue(2, 门) };【多条件组合】//只选图层 墙线 类型 直线 TypedValue[] filters { new TypedValue(0, LINE), new TypedValue(8, 墙线) };//选 墙线 OR 轴线 OR 门窗 三层 TypedValue[] filters { new TypedValue(-4, or), new TypedValue(8, 墙线), new TypedValue(8, 轴线), new TypedValue(8, 门窗), new TypedValue(-4, or) };TypedValue[] filters { new TypedValue(-4, and), // 同时满足 // 条件1直线 或 圆 new TypedValue(-4, or), new TypedValue(0, LINE), new TypedValue(0, CIRCLE), new TypedValue(-4, or), // 条件2不是轴线层 new TypedValue(-4, not), new TypedValue(8, 轴线), new TypedValue(-4, not), new TypedValue(-4, and), };过滤代码表数字代表含义示例-4逻辑符组码ADN且、OR或、NOT非、XOR异或0图元类型LINECIRCLEARCTEXTINSERT1文字内容ABC1232块名门窗办公桌8图层墙线轴线尺寸62颜色1 红2 黄3 绿4 青5 蓝6 紫7 白逻辑符号表逻辑开头写法结尾写法意思ANDandand同时满足默认就是AND,不用写ORoror满足一个就行NOTnotnot不是这个、排除这个XORxorxor二选一很少用七、图层、颜色、线型 规范【核心原则】图元的颜色、线型 尽量不要单独设置全部交给图层去控制。图层管样式图元管内容。【图层】图层 CAD的文件夹 样式总管一层统一颜色、线型、线宽、开关/冻结/锁定操作图层必须用的2个类LayerTable图层的总表所有图层都在这里LayerTableRecord一个图层的信息名称、颜色、开关图层标准操作模板创建 / 判断 / 获取// 固定写法获取图层 Database db HostApplicationServices.WorkingDatabase; using (Transaction tr db.TransactionManager.StartTransaction()) { LayerTable lt tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; // 判断图层是否存在 if (lt.Has(我的图层)) { // 图层已存在直接用 } else { // 创建新图层 LayerTableRecord ltr new LayerTableRecord(); ltr.Name 我的图层; ltr.Color Color.FromColorIndex(ColorMethod.ByAci, 3); // 绿色 // 写入图层表 tr.GetObject(lt, OpenMode.ForWrite); lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); } tr.Commit(); }【颜色】标准色1 红2 黄3 绿4 青5 蓝6 紫7 白规范交给图层line.Layer 墙线; // ✅ 墙线层是红色线就自动变红不规范单独给图元上色全局硬改line.ColorIndex 1; // ❌ 不推荐图元自己覆盖颜色原因1、改图元颜色脱离图层控制后期无法统一修改2、别人用你的CAD文件改图层颜色却改不动你的图元【线型Linetype】线型也要从图层来不要单独设置line.Linetype MyLayer; // ✅ 正确八、事件事件监听触发订阅事件CAD干完自动喊你Application.IdleCAD 空闲时触发用途后台刷新、轻量任务// 订阅/注销 Application.Idle WhenIdle; Application.Idle - WhenIdle; // 方法名字随便改WhenIdle / 空闲执行 / ABC 都行 private void WhenIdle(object sender, EventArgs e) { // 这里写你要做的事 }注意Idle 会疯狂触发 → 必须控制 “执行频率”1、加事件间隔每隔1~2秒跑一次不是每次Idle都跑。DateTime lastRun DateTime.Now; private void OnIdle(object sender, EventArgs e) { if ((DateTime.Now - lastRun).TotalSeconds 1) return; // 不到1秒直接退出不执行 // 这里写你的代码 DoSomething(); lastRun DateTime.Now; // 更新最后执行时间 }2、只在“真空闲”时执行判断是否有命令正在运行有命令就不跑if (doc.CommandInProgress) return;3、代码必须轻量化❌ 大量遍历图元❌ 频繁开启事务❌ 弹窗、输入、界面阻塞✅ 刷新界面✅ 状态检查✅ 轻量自动任务4、用完立即-注销避免一直跑Document.CommandWillStart命令即将开始doc.CommandWillStart BeforeCommand; doc.CommandWillStart - BeforeCommand; private void BeforeCommand(object sender, CommandEventArgs e) { // e.CommandName 拿到命令名 }使用场景举例1、禁止用户使用某些命令比如禁止删除、禁止炸开2、命令开始前做初始化比如画直线前切换图层3、监听用户要执行什么命令做日志、统计、操作记录4、自动准备选择集/变量命令开始前先预先加载数据Document.CommandEnded命令已经结束doc.CommandEnded AfterCommand; doc.CommandEnded - AfterCommand; private void AfterCommand(object sender, CommandEventArgs e) { }Database.ObjectAppended图元被【加入数据库】时触发db.ObjectAppended OnObjectAdded; db.ObjectAppended - OnObjectAdded; private void OnObjectAdded(object sender, ObjectEventArgs e) { // e.DBObject 拿到图元 }注意触发了 ObjectAppended≠界面一定看得见因为ObjectAppended只要图元加入数据库任何块、任何空间就触发。包括1、显示空间看得见模型空间 ModelSpace图纸空间 PaperSpace2、非显示空间看不见块定义内部普通块、匿名块、内部块只进数据库、没放到任何空间里例如db.AddDBObject(line);【正确的显示流程】1、new Line () → 创建图元内存2、添加到模型空间 ModelSpace → 自动触发 ObjectAppended3、事务 Commit4、界面显示【如何判断是不是真正滑倒界面上】if (ent.BlockId db.CurrentSpaceId) { // ✅ 真正显示在模型/图纸空间 } else { // ❌ 在块里、或其他空间界面看不见 }Database.ObjectErased有东西被删除db.ObjectErased OnObjectDeleted; db.ObjectErased - OnObjectDeleted; private void OnObjectDeleted(object sender, ObjectEventArgs e) { }Database.ObjectModified图元被修改db.ObjectModified OnObjectChanged; db.ObjectModified - OnObjectChanged; private void OnObjectChanged(object sender, ObjectEventArgs e) { }Document.BeginSave / EndSave保存开始 / 结束// 保存前 doc.BeginSave BeforeSave; doc.BeginSave - BeforeSave; private void BeforeSave(object sender, DocumentBeginSaveEventArgs e) { // e.FileName 文件名 } //保存后 doc.EndSave AfterSave; doc.EndSave - AfterSave; private void AfterSave(object sender, EventArgs e) { }SystemEvents.AppDomainUnload插件卸载时统一注销所有事件AppDomain.CurrentDomain.DomainUnload OnPluginUnload; private void OnPluginUnload(object sender, EventArgs e) { // 这里把所有事件 - 一遍防崩溃 Application.Idle - WhenIdle; doc.CommandWillStart - BeforeCommand; // ...其他事件都 - } System.AppDomain.CurrentDomain.DomainUnload - OnDomainUnload; // 一般不用通用写法套路对象.事件 方法; //订阅 对象.事件 - 方法; //注销不注销 内存泄漏 崩溃 ** private void 方法(object sender, 参数 e) { // 这里写你要干的事 }【文档类】1. CommandEnded命令结束时触发→ 自动修改刚画的图元2. BeginSave / EndSave保存前 / 保存后→ 自动检查、自动处理3. DocumentActivated / DocumentDestroyed切换文档、关闭文档【数据库图元类】1. ObjectAppended图元被画出来时触发→ 自动改图层、自动编号2. ObjectErased图元被删除时3. ObjectModified图元被修改时移动、旋转、拉伸【系统类】1. SystemEvents.AppDomainUnload插件卸载时→ 统一注销所有事件防止崩溃2. Application.SystemQuiescentCAD 完全启动完成后3. LayerModified / LayerDeleted图层被修改 / 删除4. LinetypeModified线型被修改常用事件全套完整可运行 Demousing Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; using System; public class EventDemo { // 全局保存 doc 和 db方便事件用 private Document doc Application.DocumentManager.MdiActiveDocument; private Database db HostApplicationServices.WorkingDatabase; // 一次性订阅所有事件 [CommandMethod(StartEvents)] public void StartEvents() { // 1. 空闲事件 Application.Idle OnIdle; // 2. 命令开始/结束 doc.CommandWillStart OnCommandWillStart; doc.CommandEnded OnCommandEnded; // 3. 图元增删改 db.ObjectAppended OnObjectAppended; db.ObjectErased OnObjectErased; db.ObjectModified OnObjectModified; // 4. 保存事件 doc.BeginSave OnBeginSave; doc.EndSave OnEndSave; // 5. 插件卸载时统一清理只不- AppDomain.CurrentDomain.DomainUnload OnDomainUnload; doc.Editor.WriteMessage(\n✅ 事件已启动); } // 手动停止事件也会被卸载时自动调用 [CommandMethod(StopEvents)] public void StopEvents() { UnsubscribeAll(); doc.Editor.WriteMessage(\n❌ 事件已停止); } // 统一注销所有事件核心 private void UnsubscribeAll() { Application.Idle - OnIdle; doc.CommandWillStart - OnCommandWillStart; doc.CommandEnded - OnCommandEnded; db.ObjectAppended - OnObjectAppended; db.ObjectErased - OnObjectErased; db.ObjectModified - OnObjectModified; doc.BeginSave - OnBeginSave; doc.EndSave - OnEndSave; } // 各个事件方法 // 空闲 private void OnIdle(object sender, EventArgs e) { // 轻量代码别写重逻辑 } // 命令即将开始 private void OnCommandWillStart(object sender, CommandEventArgs e) { Editor ed doc.Editor; ed.WriteMessage($\n→ 命令开始: {e.CommandName}); } // 命令结束 private void OnCommandEnded(object sender, CommandEventArgs e) { Editor ed doc.Editor; ed.WriteMessage($\n← 命令结束: {e.CommandName}); } // 图元添加 private void OnObjectAppended(object sender, ObjectEventArgs e) { using (Transaction tr db.TransactionManager.StartTransaction()) { if (e.DBObject is Entity ent) { // 真正显示在模型空间才打印 if (ent.BlockId db.CurrentSpaceId) { doc.Editor.WriteMessage($\n➕ 添加图元: {ent.GetType().Name}); } } tr.Commit(); } } // 图元删除 private void OnObjectErased(object sender, ObjectEventArgs e) { doc.Editor.WriteMessage($\n➖ 删除图元: {e.DBObject.GetType().Name}); } // 图元修改 private void OnObjectModified(object sender, ObjectEventArgs e) { // 图元被移动/旋转时触发 } // 保存前 private void OnBeginSave(object sender, DocumentBeginSaveEventArgs e) { doc.Editor.WriteMessage($\n 准备保存: {e.FileName}); } // 保存后 private void OnEndSave(object sender, EventArgs e) { doc.Editor.WriteMessage(\n✅ 保存完成); } // 插件卸载时自动清理防止崩溃 private void OnDomainUnload(object sender, EventArgs e) { UnsubscribeAll(); } }九、性能要点1、少遍历全图多用选择集/过滤遍历全图巨慢2、批量操作统一一个事务。不要每操作一个图元就开一次事务N个操作用一个事务频繁事务巨卡、崩溃3、不要在Idle里做heavy操作Idle会疯狂触发Heavy 遍历、大量事务、循环、弹窗正确做法加时间间隔、轻量判断4、关闭屏幕刷新提速神器ed.SetSuppressScreenDisplay(true); // 关闭刷新 // 你的批量操作... ed.SetSuppressScreenDisplay(false); // 恢复刷新十、常见坑没事务 / 没 CommitForRead 想改图 → 报错持有 Entity 跨事务 → 崩溃事件没注销 → 内存泄漏多线程直接操作 CAD → 必崩GetLastRect 遍历全图 → 大图卡爆没 try/catch → 闪退、丢图、崩 CAD批量操作不关刷新 → 巨慢Idle 做 heavy 操作 → 卡到爆选择集不过滤 → 遍历全图慢死忘记 Dispose / 释放资源 → 内存越来越大