夜雨聆风学习资料网

ARTICLE · 1102074

实战|用 AI 写 CAD 插件:一键删除标注(附源码 + 提示词)

实战|用 AI 写 CAD 插件:一键删除标注(附源码 + 提示词)

点击蓝字,关注我们

这是《零基础 CAD 插件从 0 到 1》第 12 篇

做设计改图的朋友,清理标注绝对是个麻烦事情: 别人的图纸标注样式五花八门,没用的标注散得到处都是,一根根框选删除,删半天还容易带掉旁边的线条和文字; 想批量删掉某一种样式的标注,CAD 自带功能又没筛选,要么全删要么手动挑,眼睛瞅得发酸,删错了还得一步步撤销。 特别是改老图、改客户图的时候,光清理标注就能耗掉小半天才干正事。

今天咱们用 AI 写一个标注清理插件:支持框选批量删,也支持按标注样式一键全删,可视化窗口操作,只删标注不碰图形。清理图纸标注几秒搞定,不用手动挑、不怕删错,改图效率直接提上来。

先看效果:两种模式,标注清得干净又精准

先看两个核心场景的实际操作:

  • GIF1 :打开一张满是杂乱标注的图纸 → 输入命令 ALDELDIM → 选择「框选删除」→ 框选杂乱标注 → 秒删干净,线条毫发无损。

  • GIF2 :输入 ALDELDIM→ 点击「拾取样式」→ 图纸里点一个标注 → 下拉框自动填入该样式 → 点「一键删除」→ 该样式标注瞬间清空 → Ctrl+Z 撤销恢复。

不用一根根选,不用担心误删线条。 想删选中的就框选,想删某一类样式就直接选样式名,点一下就清干净,删错了 Ctrl+Z 还能撤销,安全又高效。

这个插件解决了什么问题?

针对标注清理效率低、容易误删的痛点,做了四个核心优化:

  • 精准删除
    只识别并删除选中的标注对象,自动过滤线条、文字、块、填充等其他图形,框选的时候不用小心翼翼,不会误删图纸内容。
  • 按样式批量删除
    下拉选择指定标注样式,自动查找全图所有该样式的标注并删除。清理外来图纸、统一标注规范的时候尤其好用,不用逐个筛选。
  • 可视化界面
    用标准窗口操作,直观明了,不用记命令参数,不用在命令行输选项,点选一下就能操作,新手也能上手。
  • 支持撤销回退
    所有删除操作都支持 CAD 原生撤销,删错了按 Ctrl+Z 就能恢复,不用担心手滑误操作。

交互设计:可视化窗口,两步搞定

完全贴合日常改图习惯,操作简单不用学:

  1. 启动命令
    输入 ALDELDIM 回车,弹出操作窗口
  2. 选择删除方式
  • 框选删除:点击按钮后回到图纸,可连续框选,按 ESC 结束命令

  • 按样式删除:两种方式任选

    • 下拉选择:直接从下拉框选样式名

    • 拾取样式:点击按钮后去图纸里点一个标注,自动获取它的样式

没有复杂参数,没有多余步骤,打开就知道怎么用。

核心干货:给 AI 的精准提示词

还是那句话

用 AI 写插件写出来的功能不对,本质是需求没说细。AI 就像助理,你说清楚规则,它才能做准。下面是实测多轮、输出稳定能用的提示词模板,直接复制就能用:

角色设定:你是资深 CAD .NET 二次开发工程师,精通 C# 与 CAD 原生 API,面向全屋定制设计场景开发插件。

功能需求:开发一个「一键删除标注」的插件,

具体要求:

命令名:ALDELDIM 

界面:
使用 WPF 制作操作窗口,包含两种删除模式选项,界面简洁适配设计场景
模式一:
选择集删除。点击按钮后回到图纸框选对象,只过滤删除标注(Dimension),不影响其他图形
模式二:
按样式删除。下拉列出当前图纸所有标注样式,选择后删除全图所有该样式的标注
逻辑要求:
所有删除操作在事务中执行,支持 CAD 原生撤销;删除后命令行提示删除数量

环境:.NET Framework 4.8,AutoCAD 2021,引用 AcMgd、AcDbMgd;

输出要求:

给出完整可复制编译的代码

关键逻辑加中文注释

说明编译和加载方法

写提示词的核心原则

  1. 先定身份先让 AI 进入专业 CAD 开发的角色,避免用错 API、写出行外代码。

  2. 再划边界明确命令名、CAD 版本、运行环境,从根源减少兼容问题和报错。

  3. 讲清规则怎么保存、怎么注入、怎么设为当前,每一步规则都说死,不给 AI 自由发挥的空间。

  4. 固定格式要完整代码、要注释、要部署说明,都明确提出来,输出直接能用。

照着这个结构写,AI 输出代码的可用率至少提升 80%。

核心源码分享

下面是核心实现代码,基于 C# + WPF + CAD .NET API 编写,两种删除模式,支持撤销操作。

using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.Runtime;using AcApp = Autodesk.AutoCAD.ApplicationServices.Application;namespace DimDeletePlugin{    public class Commands    {        [CommandMethod("ALDELDIM")]        publicvoidAlDelDimCommand()        {            Document doc = AcApp.DocumentManager.MdiActiveDocument;            if (doc == null) return;            DimDeleteWindow win = new DimDeleteWindow();            // 将 CAD 主窗口设为 WPF 窗口的 Owner            System.Windows.Interop.WindowInteropHelper helper =                new System.Windows.Interop.WindowInteropHelper(win);            helper.Owner = AcApp.MainWindow.Handle;            // 使用 CAD 原生的模态窗口显示方法            AcApp.ShowModalWindow(win);        }    }}

入口代码

using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.EditorInput;using System;using System.Collections.Generic;using System.Windows;using System.Windows.Input;using AcApp = Autodesk.AutoCAD.ApplicationServices.Application;namespace DimDeletePlugin{    public partial class DimDeleteWindow : Window    {        // 记住"拾取"到的样式的真实 ObjectId(手动选时为 Null)        private ObjectId _pickedStyleId = ObjectId.Null;        // 标志位:正在拾取过程中,防止 SelectionChanged 清空 _pickedStyleId        private bool _isPickingStyle = false;        publicDimDeleteWindow()        {            InitializeComponent();            LoadStyleList();        }        // ============================================================        // 窗口交互        // ============================================================        privatevoidWindow_MouseDown(object sender, MouseButtonEventArgs e)        {            if (e.LeftButton == MouseButtonState.Pressed)                this.DragMove();        }        privatevoidBtnMinimize_Click(object sender, RoutedEventArgs e)        {            this.WindowState = WindowState.Minimized;        }        privatevoidBtnClose_Click(object sender, RoutedEventArgs e)        {            this.Close();        }        // ============================================================        // 加载当前图纸所有"命名"标注样式到下拉框        // ============================================================        privatevoidLoadStyleList()        {            Document doc = AcApp.DocumentManager.MdiActiveDocument;            if (doc == null) return;            Database db = doc.Database;            List<string> styleNames = new List<string>();            using (Transaction tr = db.TransactionManager.StartTransaction())            {                DimStyleTable dst = tr.GetObject(db.DimStyleTableId, OpenMode.ForRead) as DimStyleTable;                if (dst != null)                {                    foreach (ObjectId id in dst)                    {                        DimStyleTableRecord dstr = tr.GetObject(id, OpenMode.ForRead) as DimStyleTableRecord;                        if (!dstr.Name.StartsWith("*"))                            styleNames.Add(dstr.Name);                    }                }                tr.Commit();            }            cmbStyle.ItemsSource = styleNames;            if (styleNames.Count > 0)                cmbStyle.SelectedIndex = 0;        }        // ============================================================        // 下拉框选择变化:用户手动切换时清空拾取缓存        // ============================================================        privatevoidCmbStyle_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)        {            if (!_isPickingStyle)                _pickedStyleId = ObjectId.Null;        }        // ============================================================        // 模式一:框选删除        // 可连续框选,按 ESC 结束命令        // ============================================================        privatevoidBtnSelectDelete_Click(object sender, RoutedEventArgs e)        {            this.Hide();            Document doc = AcApp.DocumentManager.MdiActiveDocument;            if (doc == null) { this.Close(); return; }            Editor ed = doc.Editor;            Database db = doc.Database;            int totalCount = 0;            int roundCount = 0;            SelectionFilter filter = new SelectionFilter(new TypedValue[]            {                new TypedValue((int)DxfCode.Start, "DIMENSION")            });            while (true)            {                PromptSelectionOptions pso = new PromptSelectionOptions();                if (roundCount == 0)                    pso.MessageForAdding = "\n请框选要删除的标注(按 ESC 结束命令) ";                else                    pso.MessageForAdding = $"\n请继续框选标注(已删除 {totalCount} 个,按 ESC 结束命令) ";                pso.AllowDuplicates = false;                PromptSelectionResult psr = ed.GetSelection(pso, filter);                // ESC → 结束命令                if (psr.Status == PromptStatus.Cancel)                {                    ed.WriteMessage($"\n命令结束,本次共删除 {totalCount} 个标注。");                    break;                }                // 空格/空回车 → 继续提示框选                if (psr.Status == PromptStatus.None)                    continue;                // 正常框选 → 删除后继续循环                if (psr.Status == PromptStatus.OK && psr.Value.Count > 0)                {                    int count = 0;                    using (Transaction tr = db.TransactionManager.StartTransaction())                    {                        foreach (SelectedObject so in psr.Value)                        {                            Entity ent = tr.GetObject(so.ObjectId, OpenMode.ForWrite) as Entity;                            if (ent != null)                            {                                ent.Erase();                                count++;                            }                        }                        tr.Commit();                    }                    totalCount += count;                    roundCount++;                    ed.WriteMessage($"\n第 {roundCount} 次删除 {count} 个标注(累计 {totalCount} 个)");                    continue;                }            }            this.Show();            this.Activate();        }        // ============================================================        // 拾取样式:从图纸中拾取一个标注,获取其样式        // ============================================================        privatevoidBtnPickStyle_Click(object sender, RoutedEventArgs e)        {            Document doc = AcApp.DocumentManager.MdiActiveDocument;            if (doc == null) return;            Editor ed = doc.Editor;            Database db = doc.Database;            this.Hide();            string styleName = null;            ObjectId pickedId = ObjectId.Null;            PromptEntityOptions peo = new PromptEntityOptions("\n请拾取一个标注对象以获取其样式 ");            peo.SetRejectMessage("\n只能选择标注对象!");            peo.AddAllowedClass(typeof(Dimension), false);            PromptEntityResult per = ed.GetEntity(peo);            if (per.Status == PromptStatus.OK)            {                using (Transaction tr = db.TransactionManager.StartTransaction())                {                    Dimension dim = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Dimension;                    if (dim != null)                    {                        pickedId = dim.DimensionStyle;                        DimStyleTableRecord dstr = tr.GetObject(pickedId, OpenMode.ForRead) as DimStyleTableRecord;                        if (dstr != null)                            styleName = dstr.Name;                    }                    tr.Commit();                }            }            if (!string.IsNullOrEmpty(styleName) && !pickedId.IsNull)            {                // 开启保护标志,防止 SelectionChanged 清空刚缓存的 Id                _isPickingStyle = true;                try                {                    _pickedStyleId = pickedId;                    // 把样式名同步到下拉框                    var list = new List<string>();                    foreach (var item in cmbStyle.Items)                        list.Add(item.ToString());                    bool found = false;                    for (int i = 0; i < list.Count; i++)                    {                        if (string.Equals(list[i].Trim(), styleName.Trim(), StringComparison.OrdinalIgnoreCase))                        {                            cmbStyle.SelectedIndex = i;                            found = true;                            break;                        }                    }                    if (!found)                    {                        list.Add(styleName);                        cmbStyle.ItemsSource = null;                        cmbStyle.ItemsSource = list;                        cmbStyle.SelectedItem = styleName;                    }                    ed.WriteMessage($"\n已拾取样式:{styleName}");                }                finally                {                    _isPickingStyle = false;                }            }            this.Show();            this.Activate();        }        // ============================================================        // 模式二:按样式删除        // 双保险匹配:样式名 + ObjectId,且必须在文档锁内执行        // ============================================================        privatevoidBtnStyleDelete_Click(object sender, RoutedEventArgs e)        {            Document doc = AcApp.DocumentManager.MdiActiveDocument;            if (doc == null) { this.Close(); return; }            Database db = doc.Database;            Editor ed = doc.Editor;            if (cmbStyle.SelectedItem == null)            {                MessageBox.Show("请先选择或拾取标注样式!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);                return;            }            string styleName = cmbStyle.SelectedItem.ToString();            ObjectId pickedStyleId = _pickedStyleId;            int count = 0;            string errorInfo = null;            this.Hide();            try            {                // 关键:WPF 按钮事件修改图纸,必须手动锁文档                using (DocumentLock docLock = doc.LockDocument())                {                    // ========== 1. 拿当前空间所有标注 ==========                    TypedValue[] filterArr = new TypedValue[]                    {                        new TypedValue((int)DxfCode.Start, "DIMENSION")                    };                    SelectionFilter sf = new SelectionFilter(filterArr);                    PromptSelectionResult psr = ed.SelectAll(sf);                    if (psr.Status != PromptStatus.OK || psr.Value == null || psr.Value.Count == 0)                    {                        MessageBox.Show("当前空间中没有任何标注。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);                        this.Show();                        this.Activate();                        return;                    }                    // ========== 2. 单事务:边遍历边删除 ==========                    using (Transaction tr = db.TransactionManager.StartTransaction())                    {                        foreach (SelectedObject so in psr.Value)                        {                            if (so == null || so.ObjectId.IsNull) continue;                            try                            {                                Dimension dim = tr.GetObject(so.ObjectId, OpenMode.ForRead) as Dimension;                                if (dim == null || dim.IsErased) continue;                                DimStyleTableRecord dstr = tr.GetObject(dim.DimensionStyle, OpenMode.ForRead) as DimStyleTableRecord;                                if (dstr == null) continue;                                // 双保险比较                                bool nameMatch = string.Equals(                                    dstr.Name.Trim(),                                    styleName.Trim(),                                    StringComparison.OrdinalIgnoreCase);                                bool idMatch = (!pickedStyleId.IsNull) && (dim.DimensionStyle == pickedStyleId);                                if (nameMatch || idMatch)                                {                                    dim.UpgradeOpen();                                    dim.Erase();                                    count++;                                }                            }                            catch                            {                                continue;                            }                        }                        tr.Commit();                    }                }                _pickedStyleId = ObjectId.Null;                ed.WriteMessage($"\n已删除样式「{styleName}」的标注共 {count} 个\n");            }            catch (System.Exception ex)            {                errorInfo = $"{ex.GetType().Name}\n{ex.Message}";                ed.WriteMessage($"\n删除过程出现异常:{ex.Message}");            }            if (errorInfo != null)            {                MessageBox.Show($"删除过程出现异常:\n\n{errorInfo}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);            }            else            {                MessageBox.Show($"清理完成!共删除 {count} 个标注。\n如果删错了,可以按 Ctrl+Z 撤销。",                                "执行成功", MessageBoxButton.OK, MessageBoxImage.Information);            }            this.Close();        }    }}

核心代码

说明:代码默认只删除模型空间的标注,可根据需要扩展布局空间;窗口样式、按钮文案都可以根据自己的使用习惯调整。

用 AI 开发插件的完整流程

这类工具型插件,用 AI 分步开发,上手最快:

  1. 需求拆解
    把「一键删除标注」拆成「WPF 界面→选择集过滤删除→按样式遍历删除」三个模块,逐个让 AI 实现,不要一次性丢完整需求。
  2. 分步验证
    先写选择集删除逻辑,测准只删标注不碰其他图形;再加样式删除逻辑,测准匹配和遍历;最后做 WPF 界面整合。每一步跑通再往下,出问题好定位。
  3. 实测优化
    拿不同的图纸测试:比如带多种样式的图、带块里标注的图,看删除是否彻底、会不会误删;把遇到的问题丢给 AI,让它优化边界处理。
  4. 细节调整
    比如窗口大小、按钮样式、是否加确认提示,都可以根据自己的操作习惯改,让 AI 直接调参数就行。

成品领取 & 下期预告

成品插件直接领

不想自己编译、调代码的朋友,关注公众号 「下班让AI教我做插件」,后台回复 「一键删除标注」,直接领取编译好的 DLL 插件文件,NETLOAD 加载或者是使用我写的dll插件加载工具箱。 完整工程源码拿到手就能二次修改。

下期预告

下一篇咱们继续深化标注提效工具,如果有想要的插件可以在评论区留言,我看看哪个留言最多就做哪个。

我是做插件的设计师,专注用 AI 提高 CAD 设计效率。关注我,下班一起做插件,把重复工作交给程序。

E

N

D

相关学习资料