AutoCAD C#:如何在可编辑保存的对话框中展示Xdata并调用窗体
AutoCAD 扩展数据(Xdata) 窗体编辑实现方案
核心问题排查
原有代码无法正常实现功能的原因:
- 在
Form1_Load方法内局部声明的ListView属于方法级变量,创建后未加入窗体的Controls控件集合,运行时不会在窗体上渲染 - 原生
ListView默认关闭行编辑能力,未配置对应属性时无法支持用户修改内容 - 跨类调用窗体时如果使用全局静态实例、非模态调用,在AutoCAD运行环境下极易出现界面锁死、权限报错问题
可直接落地的实现代码
首先完成窗体端的控件初始化、Xdata加载、保存回写逻辑:
public partial class Form1 : Form { // ListView声明为窗体级字段,不要放在Load方法内作为局部变量 private ListView listViewXdata; // 存储当前操作的实体ObjectId,避免跨类传值混乱 private ObjectId _targetEntId; // 重载构造函数,直接传入需要编辑的实体ID public Form1(ObjectId targetEntId) { InitializeComponent(); _targetEntId = targetEntId; InitControlConfig(); } // 初始化控件配置 private void InitControlConfig() { listViewXdata = new ListView { Dock = DockStyle.Top, Height = 350, View = View.Details, FullRowSelect = true, LabelEdit = true, GridLines = true }; // 配置两列展示:Xdata组码类型、对应值 listViewXdata.Columns.Add("扩展数据项", 150); listViewXdata.Columns.Add("值", 250); Button btnSave = new Button { Text = "保存修改", Dock = DockStyle.Bottom, Height = 30 }; btnSave.Click += BtnSave_Click; // *关键:必须把控件加入窗体控件集合,否则运行时不会显示* this.Controls.Add(listViewXdata); this.Controls.Add(btnSave); this.Width = 450; } private void Form1_Load(object sender, EventArgs e) { // Load事件只做数据加载,不要在这里初始化控件 Database db = HostApplicationServices.WorkingDatabase; using (Transaction tr = db.TransactionManager.StartTransaction()) { Entity ent = tr.GetObject(_targetEntId, OpenMode.ForRead) as Entity; if (ent == null) return; ResultBuffer xdata = ent.XData; if (xdata == null) { MessageBox.Show("所选实体无扩展数据"); return; } // 遍历Xdata填充到ListView foreach (TypedValue tv in xdata) { ListViewItem item = new ListViewItem(tv.TypeCode.ToString()); item.SubItems.Add(tv.Value?.ToString() ?? ""); // 原始TypedValue存在Tag属性中,保存时直接读取做类型转换 item.Tag = tv; listViewXdata.Items.Add(item); } tr.Commit(); } } // 保存修改回写Xdata private void BtnSave_Click(object sender, EventArgs e) { Database db = HostApplicationServices.WorkingDatabase; // 写操作前必须锁文档,否则会抛权限异常 using (DocumentLock docLock = Application.DocumentManager.MdiActiveDocument.LockDocument()) using (Transaction tr = db.TransactionManager.StartTransaction()) { Entity ent = tr.GetObject(_targetEntId, OpenMode.ForWrite) as Entity; if (ent == null) return; List<TypedValue> newXdata = new List<TypedValue>(); foreach (ListViewItem item in listViewXdata.Items) { TypedValue oldTv = (TypedValue)item.Tag; // 可根据实际组码类型做对应类型转换,示例默认按字符串存储 newXdata.Add(new TypedValue(oldTv.TypeCode, item.SubItems[1].Text)); } ent.XData = new ResultBuffer(newXdata.ToArray()); tr.Commit(); } MessageBox.Show("扩展数据保存成功"); this.Close(); } }
注:原生ListView默认仅支持编辑第一列内容,如果需要直接编辑第二列的值,需要额外写双击事件挂载输入框,嫌实现麻烦可以直接替换为DataGridView控件,原生支持多列编辑,整体逻辑和上述代码一致
其他类调用Form1的正确方式
不要声明全局静态的窗体实例,在需要弹出窗体的逻辑中直接新建实例传入参数即可,AutoCAD环境下必须用模态方式弹出,避免和CAD主窗口抢焦点导致锁死:
// 你的自定义命令类中的触发方法 [CommandMethod("EditXdata")] public void EditXdataCommand() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; using (Transaction tr = db.TransactionManager.StartTransaction()) { // 让用户选择需要编辑Xdata的实体 PromptEntityOptions peo = new PromptEntityOptions("请选择要编辑扩展数据的实体"); PromptEntityResult per = doc.Editor.GetEntity(peo); if (per.Status != PromptStatus.OK) return; // 新建窗体实例,传入选中实体的ID Form1 xdataForm = new Form1(per.ObjectId); // 用AutoCAD官方API模态弹出窗体,不要直接调用Show()方法 Application.ShowModalDialog(xdataForm); tr.Commit(); } }
注意事项
- 所有涉及AutoCAD实体写操作的逻辑,必须先锁文档、以写模式打开实体,否则会抛出eNotOpenForWrite类异常
- 不要在自行开启的子线程中操作窗体控件或AutoCAD数据库对象,避免跨线程报错
- 如果Xdata包含复杂类型数据(比如句柄、坐标、二进制数据),保存时需要根据组码对应的数据类型做转换,不要统一按字符串存储
内容的提问来源于stack exchange,提问作者jasontho
相关产品推荐
相关产品推荐

