夜雨聆风学习资料网

ARTICLE · 1090098

实战|用 AI 写 CAD 插件:设置标注(附源码 + 提示词)

实战|用 AI 写 CAD 插件:设置标注(附源码 + 提示词)

点击蓝字,关注我们

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

做全屋定制的设计师肯定都被标注样式折腾过:CAD 的标注样式是跟图纸走的,新建一张空白图,样式栏里空空如也,又得重新建一遍标准 —— 字高、箭头、偏移、精度挨个调,调完一套完整样式又得七八分钟。公司有统一出图标准,要么靠模板文件,要么别的图里复制,最后调出来的样式五花八门,说白了,CAD 原生就做不到「一次设好,所有新图通用」。

今天我们就用 AI 写一个真正跨图纸的标注样式插件:一次把你的标准样式存好,以后不管新建多少空白图纸,一键注入样式,输入命令设为当前默认。不用存模板、不用复制粘贴、不用找别的图复制,新图打开直接用,画出来的标注永远统一标准。

先看效果:一次保存,永久通用

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

首次使用先叫你设置样式

打开新的图纸可以看到也是有之前的样式的

不用每次开新图都重建样式,不用记参数,不用来回切换图纸复制。第一次花 10 秒拾取保存,后面所有新图一键生效,标注样式永远统一,图纸干干净净没有多余东西。

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

针对标注样式反复设置、跨图复用麻烦的痛点,做了四个核心优化:

  1. 样式跨图纸永久保存把完整的标注样式参数存在本地配置文件,不跟图纸走。一次保存,换图、重启 CAD 都能复用,真正的一次设置永久使用。

  2. 新图一键注入样式新建空白图纸,执行命令自动把标准样式创建到当前图里,直接设为当前标注样式。不用手动新建、不用从别的图复制,图里干干净净只有你要的标准样式。

  3. 批量套用一键统一选中图里所有标注,一键统一成标准样式,旧图、外来图几秒规范完,比格式刷效率高几十倍。

  4. 自动设为当前默认注入样式后自动设为当前默认,接下来画的标注自动套用标准,不用手动去样式栏切换。

交互设计:两种场景,都只要几步

完全符合日常操作习惯,不用学习复杂操作:

首次配置(只做一次)

  1. 打开一张带有目标样式的图纸

  2. 输入 KJDim 回车,选择「拾取当前样式保存为标准」

  3. 选择要保存的样式名,确认后自动永久保存

日常使用(所有图纸通用)

  1. 新建 / 打开任意图纸

  2. 输入 KJDim 回车

  3. 自动注入标准样式并设为当前;如果提前选中标注则批量套用

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

还是那句话

用 AI 写插件,写出来的功能不对,本质是需求没说细。AI 就像助理,你说清楚规则,它才能做准。

下面是实测多轮、输出稳定能用的提示词模板,直接复制就能用:

角色设定

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

功能需求

:开发一个「跨图纸标注样式管理器」插件,具体要求:

命令名:KJDim

核心逻辑

:带本地配置记忆功能,标注样式参数保存在本地 ini 文件,跨图纸永久通用;支持从当前图纸已有样式保存为标准

首次流程

:启动命令后检测本地配置,若不存在则提示用户从当前图纸选择一个已有标注样式,读取该样式完整参数并保存到本地配置文件

日常流程

:检测到配置文件存在,则读取参数;检查当前图纸是否存在同名样式,不存在则按参数创建该标注样式;创建后自动设为当前文档的默认标注样式

批量套用:用户点击按钮选中标注对象,则将选中的所有标注批量应用该标准样式

环境:.NET Framework 4.8,引用 AcMgd、AcDbMgd;配置文件保存在插件同目录

界面要求使用wpf

输出要求:

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

关键逻辑加中文注释

说明编译和加载方法

写提示词的核心原则

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

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

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

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

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

核心源码分享

下面是核心实现代码,基于 C# +CAD .NET API 编写,样式参数本地存储,跨图纸通用,新图自动注入并设为当前。

命令主逻辑:样式拾取替换 + 新图注入 + 设为当前 + 批量套用

using System;using System.Globalization;using System.Linq;using Autodesk.AutoCAD.ApplicationServices;using Autodesk.AutoCAD.DatabaseServices;using Autodesk.AutoCAD.EditorInput;using Autodesk.AutoCAD.Runtime;[assembly: CommandClass(typeof(KJDimPlugin.KJDimCommand))]namespaceKJDimPlugin{    publicclassKJDimCommand    {        privatestring _pendingStyleName;        // ═══════════════════════════════════════════════════════════        //  主命令        // ═══════════════════════════════════════════════════════════        [CommandMethod("KJDim")]        publicvoidExecuteKJDim()        {            var doc = Application.DocumentManager.MdiActiveDocument;            if (doc == null) return;            _pendingStyleName = null;            try            {                if (!IniConfigManager.ConfigExists())                {                    if (!RunStyleConfigFlow(doc, isFirstTime: true)) return;                    if (_pendingStyleName != null) return;                }                else                {                    var parameters = DimStyleService.LoadParamsFromIni();                    if (parameters.Count == 0)                    {                        doc.Editor.WriteMessage("\n配置文件损坏,重新初始化...\n");                        IniConfigManager.DeleteConfig();                        if (!RunStyleConfigFlow(doc, isFirstTime: true)) return;                        if (_pendingStyleName != null) return;                    }                    else                    {                        string std = DimStyleService.EnsureDimStyle(doc.Database, parameters);                        doc.Editor.WriteMessage($"\n标准样式 \"{std}\" 已就绪并置为当前。\n");                    }                }                RunDailyLoop(doc);            }            catch (System.Exception ex)            {                doc.Editor.WriteMessage($"\n运行错误:{ex.Message}\n");            }        }        // ═══════════════════════════════════════════════════════════        //  首次配置流程        // ═══════════════════════════════════════════════════════════        privateboolRunStyleConfigFlow(Document doc, bool isFirstTime)        {            var db = doc.Database;            var ed = doc.Editor;            var firstWin = new FirstTimeWindow(isFirstTime);            var r1 = Application.ShowModalWindow(firstWin);            if (r1 != true) returnfalse;            if (firstWin.Action == FirstTimeAction.CreateNew)            {                string newStyle = DoCreateNewStyle(doc);                if (string.IsNullOrEmpty(newStyle)) returnfalse;                returntrue;            }            elseif (firstWin.Action == FirstTimeAction.UseExisting)            {                string chosen = DoPickExistingStyle(doc);                if (string.IsNullOrEmpty(chosen)) returnfalse;                var parameters = DimStyleService.ReadDimStyleParams(db, chosen);                if (parameters == null) returnfalse;                DimStyleService.SaveParamsToIni(parameters);                DimStyleService.EnsureDimStyle(db, parameters);                ed.WriteMessage($"\n标准样式 \"{chosen}\" 已保存并置为当前。\n" +                                $"配置文件:{IniConfigManager.GetConfigPath()}\n");                returntrue;            }            returnfalse;        }        // ═══════════════════════════════════════════════════════════        //  新建样式        // ═══════════════════════════════════════════════════════════        privatestringDoCreateNewStyle(Document doc)        {            var db = doc.Database;            var ed = doc.Editor;            var nameWin = new NameInputWindow();            var nr = Application.ShowModalWindow(nameWin);            if (nr != true) returnnull;            string newName = nameWin.StyleName;            double dimScale = nameWin.DimScale;            if (DimStyleService.GetAllDimStyleNames(db).Contains(newName))            {                ed.WriteMessage($"\n样式 \"{newName}\" 已存在,请换一个名称。\n");                returnnull;            }            if (!CreateStyleFromDefaultTemplate(db, newName, dimScale))            {                ed.WriteMessage($"\n创建样式 \"{newName}\" 失败。\n");                returnnull;            }            _pendingStyleName = newName;            doc.CommandEnded -= OnDimStyleCommandEnded;            doc.CommandEnded += OnDimStyleCommandEnded;            ed.WriteMessage(                $"\n已创建样式 \"{newName}\"(比例 1:{dimScale:0.##})并置为当前。\n" +                $"正在打开【标注样式管理器】...\n");            doc.SendStringToExecute("_.DIMSTYLE\n", true, false, false);            return newName;        }        privatevoidOnDimStyleCommandEnded(object sender, CommandEventArgs e)        {            if (!string.Equals(e.GlobalCommandName, "DIMSTYLE",                StringComparison.OrdinalIgnoreCase)) return;            var doc = sender as Document;            if (doc == null) return;            doc.CommandEnded -= OnDimStyleCommandEnded;            string styleName = _pendingStyleName;            _pendingStyleName = null;            if (string.IsNullOrEmpty(styleName)) return;            var db = doc.Database;            var ed = doc.Editor;            try            {                var parameters = DimStyleService.ReadDimStyleParams(db, styleName);                if (parameters == null) return;                DimStyleService.SaveParamsToIni(parameters);                DimStyleService.EnsureDimStyle(db, parameters);                ed.WriteMessage($"\n标准样式 \"{styleName}\" 已保存并置为当前。\n" +                                $"配置文件:{IniConfigManager.GetConfigPath()}\n");                RunDailyLoop(doc);            }            catch (System.Exception ex)            {                ed.WriteMessage($"\n保存参数失败:{ex.Message}\n");            }        }        // ═══════════════════════════════════════════════════════════        //  选取已有样式        // ═══════════════════════════════════════════════════════════        privatestringDoPickExistingStyle(Document doc)        {            var db = doc.Database;            var ed = doc.Editor;            var items = DimStyleService.GetAllDimStyleItems(db);            if (items.Count == 0)            {                ed.WriteMessage("\n当前图纸中没有标注样式。\n");                returnnull;            }            var pickWin = new StylePickWindow(items);            var pr = Application.ShowModalWindow(pickWin);            if (pr != true) returnnull;            string chosen = pickWin.SelectedStyleName;            using (var tr = db.TransactionManager.StartTransaction())            {                var table = (DimStyleTable)tr.GetObject(db.DimStyleTableId, OpenMode.ForRead);                if (table.Has(chosen))                {                    ObjectId sid = table[chosen];                    db.Dimstyle = sid;                    var rec = (DimStyleTableRecord)tr.GetObject(sid, OpenMode.ForRead);                    db.SetDimstyleData(rec);                }                tr.Commit();            }            return chosen;        }        // ═══════════════════════════════════════════════════════════        //  从系统模板创建        // ═══════════════════════════════════════════════════════════        privateboolCreateStyleFromDefaultTemplate(Database db, string newName, double dimScale)        {            using (var tr = db.TransactionManager.StartTransaction())            {                var table = (DimStyleTable)tr.GetObject(db.DimStyleTableId, OpenMode.ForRead);                if (table.Has(newName)) { tr.Commit(); returnfalse; }                table.UpgradeOpen();                var newRecord = new DimStyleTableRecord();                string baseName = (db.Measurement == MeasurementValue.English)                    ? "STANDARD" : "ISO-25";                if (table.Has(baseName))                {                    var baseRec = (DimStyleTableRecord)tr.GetObject(                        table[baseName], OpenMode.ForRead);                    newRecord.CopyFrom(baseRec);                }                newRecord.Name = newName;                newRecord.Dimscale = dimScale;                if (table.Has(newRecord.Name)) { tr.Commit(); returnfalse; }                ObjectId id = table.Add(newRecord);                tr.AddNewlyCreatedDBObject(newRecord, true);                db.Dimstyle = id;                db.SetDimstyleData(newRecord);                tr.Commit();            }            returntrue;        }        // ═══════════════════════════════════════════════════════════        //  日常循环        // ═══════════════════════════════════════════════════════════        privatevoidRunDailyLoop(Document doc)        {            while (true)            {                var db = doc.Database;                var ed = doc.Editor;                string std = IniConfigManager.ReadValue("Name", "KJ_STD");                double stdScale = 1.0;                double.TryParse(IniConfigManager.ReadValue("Dimscale", "1"),                    NumberStyles.Any, CultureInfo.InvariantCulture, out stdScale);                if (stdScale <= 0) stdScale = 1.0;                var allStyles = DimStyleService.GetAllDimStyleItems(db);                var win = new DailyUseWindow(std, stdScale, allStyles);                var res = Application.ShowModalWindow(win);                if (res != true) return;                switch (win.Action)                {                    case DailyAction.ReplaceAll:                        ReplaceAllDimensions(doc, win.SourceStyle, win.TargetStyle);                        return;                    case DailyAction.ReplaceSelection:                        ReplaceSelectionInteractive(doc, win.TargetStyle);                        return;                    case DailyAction.CreateNewStyle:                        DoCreateNewStyle(doc);                        return;                    case DailyAction.ChangeStandardStyle:                        ChangeStandardStyle(doc);                        break;                }            }        }        // ═══════════════════════════════════════════════════════════        //  更换标准样式(直接弹样式列表)        // ═══════════════════════════════════════════════════════════        privatevoidChangeStandardStyle(Document doc)        {            var db = doc.Database;            var ed = doc.Editor;            var items = DimStyleService.GetAllDimStyleItems(db);            if (items.Count == 0) return;            var pickWin = new StylePickWindow(items);            var pr = Application.ShowModalWindow(pickWin);            if (pr != true) return;            string chosen = pickWin.SelectedStyleName;            var p = DimStyleService.ReadDimStyleParams(db, chosen);            if (p == null) return;            DimStyleService.SaveParamsToIni(p);            DimStyleService.EnsureDimStyle(db, p);            ed.WriteMessage($"\n标准样式已更换为 \"{chosen}\"。\n");        }        // ═══════════════════════════════════════════════════════════        //  全图替换        // ═══════════════════════════════════════════════════════════        privatevoidReplaceAllDimensions(Document doc, string sourceStyle, string targetStyle)        {            var db = doc.Database;            var ed = doc.Editor;            int count = 0;            using (var tr = db.TransactionManager.StartTransaction())            {                var dimTable = (DimStyleTable)tr.GetObject(db.DimStyleTableId, OpenMode.ForRead);                if (!dimTable.Has(sourceStyle) || !dimTable.Has(targetStyle))                {                    ed.WriteMessage("\n源样式或目标样式在当前图纸中不存在。\n");                    tr.Commit();                    return;                }                ObjectId srcId = dimTable[sourceStyle];                ObjectId tgtId = dimTable[targetStyle];                var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);                foreach (ObjectId btrId in bt)                {                    var btr = (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead);                    foreach (ObjectId entId in btr)                    {                        var ent = tr.GetObject(entId, OpenMode.ForRead);                        if (ent is Dimension dim && dim.DimensionStyle == srcId)                        {                            dim.UpgradeOpen();                            dim.DimensionStyle = tgtId;                            count++;                        }                    }                }                tr.Commit();            }            ed.WriteMessage($"\n已将 {count} 个标注从 \"{sourceStyle}\" 替换为 \"{targetStyle}\"。\n");        }        // ═══════════════════════════════════════════════════════════        //  交互式选择 + 替换        // ═══════════════════════════════════════════════════════════        privatevoidReplaceSelectionInteractive(Document doc, string targetStyle)        {            var db = doc.Database;            var ed = doc.Editor;            var opt = new PromptSelectionOptions            {                MessageForAdding = "\n请选择要替换的标注对象:",                AllowDuplicates = false            };            var res = ed.GetSelection(opt);            if (res.Status != PromptStatus.OK)            {                ed.WriteMessage("\n未选择任何对象,操作取消。\n");                return;            }            int count = 0;            using (var tr = db.TransactionManager.StartTransaction())            {                var dimTable = (DimStyleTable)tr.GetObject(db.DimStyleTableId, OpenMode.ForRead);                if (!dimTable.Has(targetStyle))                {                    ed.WriteMessage($"\n目标样式 \"{targetStyle}\" 在当前图纸中不存在。\n");                    tr.Commit();                    return;                }                ObjectId tgtId = dimTable[targetStyle];                foreach (SelectedObject so in res.Value)                {                    if (so == null) continue;                    var ent = tr.GetObject(so.ObjectId, OpenMode.ForRead);                    if (ent is Dimension dim)                    {                        dim.UpgradeOpen();                        dim.DimensionStyle = tgtId;                        count++;                    }                }                tr.Commit();            }            ed.WriteMessage($"\n已将选中的 {count} 个标注替换为 \"{targetStyle}\"。\n");        }    }}

说明:配置文件保存在插件同目录的KJDimStyle.ini,想更换标准样式删除配置文件重新拾取即可;代码默认包含文字高度、箭头大小、偏移、精度等核心参数,可根据出图标准自行扩展更多样式参数。

用 AI 开发插件的完整流程

这类带记忆功能的工具,用 AI 分步开发,上手最快:

  1. 需求拆解把「跨图纸标注样式」拆成「配置读写→样式选择→注入创建→设为当前」四个模块,逐个让 AI 实现,不要一次性丢完整需求。

  2. 分步验证先写配置读写逻辑,测好保存和读取;再加样式选择和创建逻辑,测准参数还原;最后加设为当前和批量套用逻辑。每一步跑通再往下,出问题好定位。

  3. 实测优化拿不同版本的图纸测试:比如旧版本 CAD 的图、带自定义箭头的图,看样式注入是否完整;把遇到的问题丢给 AI,让它优化参数兼容。

  4. 细节调整比如默认样式名、包含哪些参数、是否自动建图层,都可以根据自己的公司标准改,让 AI 直接调参数就行。

成品领取 & 下期预告

成品插件直接领

如果不想自己折腾的小伙伴,可以后台回复 「标注设置」,直接领取编译好的 DLL 插件文件,使用NETLOAD 加载或是用我开发的dll插件加载工具。完整工程源码 ,拿到手就能二次修改。

下期预告

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

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

E

N

D

相关学习资料