在CAD文件中存储自定义的数据,XData,XRecord,DataTable
基础知识:
根据CAD官方的资料看来,Database作为一个CAD文件数据库的根对象,其包含10个子对象:九大符号表(SymbolTable)和命名对象词典(NamedObjectsDictionary)。
这10个子对象属于整个数据库内的最高层级,直属于Database。除这10个子对象以外,所有DBObject对象都必须有一个所有者(Owner),这个所有者可以是其它的DBObject,或者是这10个子对象之一。因此,整个数据库是一个树形结构,存在于数据库中的对象都直接或间接地属于这10个子对象。
根据数据库的树形结构,所有DBObject对象根据其自身类别,分别属于不同对象。比如:BlockTable中包含多个BlockTableRecord,某个BlockTableRecord又包含多个Entity,其中每个Entity的存在都依赖于其BlockTableRecord的存在。各种信息就是这样分门别类地存储在数据库中。内存中New()生成的对象要加入数据库中,需要调用数据库中已存在的某个对象对应的方法才行,比如;blockTableRecord.AppendEntity(Entity entity);加入后要使用Database.AddNewlyCreatedDBObject(DBObject obj,bool add)方法进行确认。
存储自定义数据的方式:
为便于扩展,Autodesk设计了多种方式存储用户自定义的数据。“这些数据由开发者自己进行解释 ,CAD不管其含义,扩展数据以吸附物的形式吸附在实体上”。其中资料最多的是XData形式,其次有XRecord和DataTable。几种方式各有异同。
在了解这三种方法之前,先要了解两个基础知识:ResultBuffer类和DBDictionary类。
ResultBuffer是一个键值对集合,key是一个DxfCode(short),value是对应的某个类型的object,其本质就是一个Dxf组码表,其中的数据必须按照Dxf组码类型表进行存储。一个ResultBuffer对象最高存储128K的数据。
DBDictionary也是一个键值对集合,key是一个开发者自行指定的string,value是一个DBObject实例。
DBDictionary可以作为附加数据依附在一个DBObject实例上,存储和这个DBObject相关的数据,访问方式为DBObject.ExtensionDictionary。依附于一个DBObject的DBDictionary会随着这个DBObject的删除而删除。类似的,DBDictionary也可以依附于Database存储全局数据;访问方式为Database.NamedObjectsDictionaryId。
所有的Entity只能存储在BlockTableRecord下,因此Entity不能直接存入DBDictionary。
不可以把已经添加到Database中的DBObcect添加进DBDictionary,添加操作完成后要使用Database.AddNewlyCreatedDBObject(DBObject obj,bool add)方法进行确认。
由于九大符号表无法满足所有的数据存储需求,特别是新版本增加功能的同时还要保证向下兼容,很多数据存储是借助于Database的命名对象词典完成的,可以自行遍历求证,以下是我自己遍历的代码和结果。

1 public static void TraverseNamedObjectsDictionaryDemo() 2 { 3 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 4 if (doc == null) return; 5 var db = doc.Database; 6 var ed = doc.Editor; 7 using (var trans = db.TransactionManager.StartTransaction()) 8 { 9 var dict = trans.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead) as DBDictionary; 10 foreach (var entry in dict) 11 { 12 string s = $"[{$"{entry.Key}]",-50}=> "; 13 var value = trans.GetObject(entry.Value, OpenMode.ForRead); 14 s+=value.GetType().ToString()+"\n"; 15 ed.WriteMessage(s); 16 } 17 ed.WriteMessage("已遍历完NamedObjectsDictionary。\n"); 18 } 19 } 20 //以下是遍历结果,我的环境是AutoCAD2019,没有测试其他版本 21 //[ACAD_ASSOCPERSSUBENTMANAGER]=> Autodesk.AutoCAD.DatabaseServices.ImpDBObject 22 //[ACAD_BACKGROUND]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 23 //[ACAD_CIP_PREVIOUS_PRODUCT_INFO]=> Autodesk.AutoCAD.DatabaseServices.Xrecord 24 //[ACAD_COLOR]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 25 //[ACAD_DATALINK]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 26 //[ACAD_DETAILVIEWSTYLE]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 27 //[ACAD_DGNDEFINITIONS]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 28 //[ACAD_DWFDEFINITIONS]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 29 //[ACAD_FIELDLIST]=> Autodesk.AutoCAD.DatabaseServices.ImpDBObject 30 //[ACAD_GROUP]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 31 //[ACAD_LAYOUT]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 32 //[ACAD_MATERIAL]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 33 //[ACAD_MLEADERSTYLE]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 34 //[ACAD_MLINESTYLE]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 35 //[ACAD_PDFDEFINITIONS]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 36 //[ACAD_PERSUBENTMGR]=> Autodesk.AutoCAD.DatabaseServices.ImpDBObject 37 //[ACAD_PLOTSETTINGS]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 38 //[ACAD_PLOTSTYLENAME]=> Autodesk.AutoCAD.DatabaseServices.DictionaryWithDefaultDictionary 39 //[ACAD_RENDER_ACTIVE_RAPIDRT_SETTINGS]=> Autodesk.AutoCAD.DatabaseServices.RapidRTRenderSettings 40 //[ACAD_RENDER_ACTIVE_SETTINGS]=> Autodesk.AutoCAD.DatabaseServices.RenderSettings 41 //[ACAD_SCALELIST]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 42 //[ACAD_SECTIONVIEWSTYLE]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 43 //[ACAD_TABLESTYLE]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 44 //[ACAD_VISUALSTYLE]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 45 //[AcDbVariableDictionary]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary 46 //[ASE_INDEX_DICTIONARY]=> Autodesk.AutoCAD.DatabaseServices.DBDictionary
XData:
XData是最简便的附加自定义数据的方式,使用的是ResultBuffer数据类型。
一个DBObject附加的XData最多只能存储16K的数据,不同的DxfCode对应的存储空间也有最大值限制。
XData中存储的某些数据可以跟随CAD环境或者Entity进行变化而无需开发者参与,其它语言也能轻松访问,这是使用XData的优势。
由于存储量的限制,XData方式只适合存储少量数据;并且其中Value的类型必须和DxfCode相对应,限制了它的灵活性。
大致流程如下:
1、new一个ResultBuffer,然后调用其Add(new TypedValue((int)code, Value))方法添加键值对。
2、写模式打开DBObject,赋值Obj.XData = ResultBuffer。
3、读:ResultBuffer resultBuffer=Obj.XData。
注意点如下:
1、为区分不同的用户(开发者),对于DBObject.XData赋值的时候要求:写入的ResultBuffer第一个键值对须使用new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName),以此来区分不同的用户(开发者);具有不同AppName开始的ResultBuffer在赋值的时候不会相互覆盖。需要读数据时,使用Obj.GetXDataForApplication(AppName)可以只读自己写入的那一段。
2、写入带有DxfCode.ExtendedDataRegAppName键值对的ResultBuffer的时候,要求AppName在Database中是注册过的。因此,在写入带AppName的ResultBuffer之前,应进行两个操作:将DBObject加入Database,在Database的RegAppTable中注册AppName。
3、写入的键值对要求DxfCode不小于DxfCode.ExtendedDataAsciiString(1000),可以去查看Dxf组码类型表,小于DxfCode.ExtendedDataAsciiString(1000)的是属于“原生”的组码,这些组码在CAD中有其专有用途,因此不对XData开放。大于等于的是属于“扩展”的,用于存储二次开发的信息。
4、除DxfCode.ExtendedDataRegAppName外,ResultBuffer中允许连续或间断写入相同的Dxf组码的内容。
5、写入的部分数据会跟随Entity的Transform而发生改变。利用这个设定,可以达到一些特定的目的,比如将Entity上的特殊点以XData的形式记录在Entity上,当客户利用CAD原生的Move、Rotate等命令操作此Entity时,这些点将跟随同步变化;再如,通过判断Scale是否发生改变来判断客户是否对该实体进行过缩放操作。需要注意的是,这些数据只对Entity整体的Transform敏感,客户的某些操作不会触发Transform操作,因此这些操作不会导致XData发生变化,比如客户使用STRETCH命令拉伸了多段线的部分节点。
6、XData的数据在写入和读出的时候可能发生类型转换,这很容易导致在验证之前存入的数据时发生意料之外的情况,具体可自行测试。
具体可参考如下代码:

1 public static class XDataDemo 2 { 3 static string AppName = "XDataDemoAppName"; 4 public static void WriteXDataDemo() 5 { 6 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 7 if (doc == null) return; 8 var db = doc.Database; 9 var ed = doc.Editor; 10 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 11 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 12 RegisterAppName(db, AppName); 13 RegisterAppName(db, AppName+"2"); 14 var xData = CreatNewXData(); 15 var xData2 = CreatNewXData2(); 16 using (var trans = db.TransactionManager.StartTransaction()) 17 { 18 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); 19 ent.XData = xData;//此处使用的赋值语句 20 //CAD内部好像进行了额外的处理 21 //比如验证AppName是否已注册、验证该实体的XData中是否已有相同的AppName的XData 22 ent.XData = xData2;//第二次赋值不会覆盖第一次的赋值,因为两个XData的AppName不相同 23 ed.WriteMessage("写入XData成功"+"\n"); 24 trans.Commit(); 25 } 26 } 27 public static void ReadXDataDemo() 28 { 29 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 30 if (doc == null) return; 31 var db = doc.Database; 32 var ed = doc.Editor; 33 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 34 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 35 using (var trans = db.TransactionManager.StartTransaction()) 36 { 37 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); 38 var xData = ent.XData;//获取全部XData 39 // xData=ent.GetXDataForApplication(AppName);//获取指定应用的XData 40 PrintXData(xData, ed); 41 } 42 } 43 public static void ClearXDataDemo() 44 { 45 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 46 if (doc == null) return; 47 var db = doc.Database; 48 var ed = doc.Editor; 49 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 50 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 51 RegisterAppName(db, AppName); 52 53 using (var trans = db.TransactionManager.StartTransaction()) 54 { 55 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); 56 //这两种方法不能清除XData 57 //ent.XData=null; 58 //ent.XData=new ResultBuffer(); 59 //测试通过的方法如下: 60 var xData = new ResultBuffer(); 61 xData.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName)); 62 //只能清除指定AppName的XData 63 //要清除全部XData,需要遍历所有AppName,并删除对应XData 64 ent.XData = xData; 65 ed.WriteMessage("清除XData成功。"+"\n"); 66 trans.Commit(); 67 } 68 } 69 private static void RegisterAppName(Database db, string appName) 70 { 71 using (var trans = db.TransactionManager.StartTransaction()) 72 { 73 var regAppTable = trans.GetObject(db.RegAppTableId, OpenMode.ForRead) as RegAppTable; 74 if (!regAppTable.Has(appName)) 75 { 76 regAppTable.UpgradeOpen(); 77 var regAppTableRecord = new RegAppTableRecord(); 78 regAppTableRecord.Name = appName; 79 regAppTable.Add(regAppTableRecord); 80 trans.AddNewlyCreatedDBObject(regAppTableRecord, true); 81 trans.Commit(); 82 } 83 } 84 } 85 private static ResultBuffer CreatNewXData() 86 { 87 var xData = new ResultBuffer(); 88 //插入了注册的应用名称,用于区分不同应用的XData 89 xData.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName)); 90 //插入了一个字符串,用于记录需要的信息 91 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Point_1")); 92 //插入了一个点的坐标,Entity发生Transform时,点的坐标也会发生变化 93 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(100, 200, 300))); 94 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Point_2")); 95 //使用相同的DxfCode插入了第二个点,两次插入的数据都会被保存在ResultBuffer中 96 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(400, 500, 600))); 97 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Vector_1")); 98 //插入了一个向量,此处需要将向量转化为Point3d数据类型 99 //向量对旋转的Transform敏感,对平移和缩放不敏感 100 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXDir, new Point3d(100, 0, 0))); 101 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "scale")); 102 //插入了一个缩放因子,对平移和旋转不敏感 103 xData.Add(new TypedValue((int)DxfCode.ExtendedDataScale, 1.0)); 104 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Vector_2")); 105 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXDir, new Point3d(0, 200, 0))); 106 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "RealNumber")); 107 //插入了一个实数,不跟随Entity的Transform变化 108 xData.Add(new TypedValue((int)DxfCode.ExtendedDataReal, 1.0)); 109 return xData; 110 } 111 private static ResultBuffer CreatNewXData2() 112 { 113 var xData = new ResultBuffer(); 114 xData.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName+"2")); 115 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "APP2_Point_1")); 116 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(100, 200, 300))); 117 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "APP2_Point_2")); 118 return xData; 119 } 120 private static void PrintXData(ResultBuffer xData, Editor ed) 121 { 122 if (ed==null) return; 123 if (xData == null) { ed.WriteMessage("没有XData"+"\n"); return; } 124 ed.WriteMessage("XData内容:"+"\n"); 125 foreach (TypedValue tv in xData) 126 { 127 string s = ""; 128 s+=$"{$"[{tv.TypeCode.ToString()}]",-8}"; 129 s+=$"{$"[{tv.Value.GetType().ToString()}]",-40}"; 130 s+=$"{$"[{tv.Value.ToString()}]",-60}"; 131 s+="\n"; 132 ed.WriteMessage(s); 133 } 134 //打印内容如下: 135 //[1001] [System.String] [XDataDemoAppName] 136 //[1000] [System.String] [Point_1] 137 //[1011] [Autodesk.AutoCAD.Geometry.Point3d] [(100,200,300)] 138 //[1000] [System.String] [Point_2] 139 //[1011] [Autodesk.AutoCAD.Geometry.Point3d] [(400,500,600)] 140 //[1000] [System.String] [Vector_1] 141 //[1013] [Autodesk.AutoCAD.Geometry.Point3d] [(100,0,0)] 142 //[1000] [System.String] [scale] 143 //[1042] [System.Double] [1] 144 //[1000] [System.String] [Vector_2] 145 //[1013] [Autodesk.AutoCAD.Geometry.Point3d] [(0,200,0)] 146 //[1000] [System.String] [RealNumber] 147 //[1040] [System.Double] [1] 148 //[1001] [System.String] [XDataDemoAppName2] 149 //[1000] [System.String] [APP2_Point_1] 150 //[1011] [Autodesk.AutoCAD.Geometry.Point3d] [(100,200,300)] 151 //[1000] [System.String] [APP2_Point_2] 152 } 153 }
XRecord:
如果说XData是标准Dxf组码的扩展,XRecord就是使用标准Dxf组码的专门用来存储数据的类型。
XRecord存储数据使用的也是ResultBuffer数据类型,也要遵循Dxf组码类型表的规定;有别于XData的是,XData使用的“扩展段”的组码表(DxfCode>=1000);XRecord使用的是“标准段”的组码表(DxfCode<1000),并且,通过其中的四个指向(HardOwner,SoftOwner,HardPointer,SoftPointer),一个XRecord对象可以再存储其它DBObject;因此,XRecord存储的内容比XData更丰富。XRecord的存储上限为2GB。
XRecord类是DBObject类的子类,通过DBDictionary的方式和Database或者DBObject实例绑定,加入Database后要进行Database.AddNewlyCreatedDBObject(DBObject obj,bool add)确认操作。
以下是一个简单的例子,仍有很多重要功能未经测试。有空的时候再补上。。
View Code
DataTable:
DataTable是Autodesk开发的另一个专门用来存储数据的类型;它实际是个表格,表头定义了每一列的名称和数据类型,数据被一行一行地添加进去。
DataTable支持多种数据类型,具体包括:Boolean, integer, double, char*, Point3d, ObjectId, AcDbHardOwnershipId, SoftOwnershipId, AcDbHardPointerId, AcDbSoftPointerId。
DataTable类是DBObject类的子类,通过DBDictionary的方式和Database或者DBObject实例绑定,加入Database后要进行Database.AddNewlyCreatedDBObject(DBObject obj,bool add)确认操作。
以下是一个简单的例子,仍有许多重要功能未经测试。有空的时候再补上。。。

1 public static class DataTableDemo 2 { 3 public static void WriteDataTableDemo() 4 { 5 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 6 if (doc == null) return; 7 var db = doc.Database; 8 var ed = doc.Editor; 9 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 10 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 11 var dataTable = CreatNewDataTable(); 12 using (var trans = db.TransactionManager.StartTransaction()) 13 { 14 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite) as Entity; 15 { 16 //如果ent.ExtensionDictionary没有创建,则创建 17 if (ent.ExtensionDictionary == ObjectId.Null) ent.CreateExtensionDictionary(); 18 var dicID = ent.ExtensionDictionary; 19 var dic = trans.GetObject(dicID, OpenMode.ForWrite) as DBDictionary; 20 dic.SetAt("DataTableDemo", dataTable); 21 trans.AddNewlyCreatedDBObject(dataTable, true); 22 ed.WriteMessage("写入DataTable成功"+"\n"); 23 } 24 trans.Commit(); 25 } 26 } 27 private static DataTable CreatNewDataTable() 28 { 29 var dataTable = new DataTable(); 30 dataTable.AppendColumn(CellType.CharPtr, "Name"); 31 dataTable.AppendColumn(CellType.Integer, "Index"); 32 dataTable.AppendColumn(CellType.Bool, "HavePoint"); 33 dataTable.AppendColumn(CellType.Point, "PointPosition"); 34 35 var row = new DataCellCollection(); 36 var cell1 = new DataCell(); 37 cell1.SetString("NameABC"); 38 var cell2 = new DataCell(); 39 cell2.SetInteger(1); 40 var cell3 = new DataCell(); 41 cell3.SetBool(true); 42 var cell4 = new DataCell(); 43 cell4.SetPoint(new Point3d(12, 23, 34)); 44 row.Add(cell1); 45 row.Add(cell2); 46 row.Add(cell3); 47 row.Add(cell4); 48 //AppendRow()方法的第二个参数是是否对加入的这一行进行检查 49 //具体指数量检查和类型检查 50 dataTable.AppendRow(row, true); 51 52 var row2 = new DataCellCollection(); 53 var row2cell1 = new DataCell(); 54 row2cell1.SetString("Row2NameABC"); 55 var row2cell2 = new DataCell(); 56 row2cell2.SetInteger(2); 57 var row2cell3 = new DataCell(); 58 row2cell3.SetBool(false); 59 var row2cell4 = new DataCell(); 60 row2cell4.SetPoint(new Point3d(0, 0, 0)); 61 row2.Add(row2cell1); 62 row2.Add(row2cell2); 63 row2.Add(row2cell3); 64 row2.Add(row2cell4); 65 dataTable.AppendRow(row2, true); 66 return dataTable; 67 68 } 69 public static void ReadDataTableDemo() 70 { 71 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 72 if (doc == null) return; 73 var db = doc.Database; 74 var ed = doc.Editor; 75 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 76 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 77 using (var trans = db.TransactionManager.StartTransaction()) 78 { 79 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite) as Entity; 80 { 81 if (ent.ExtensionDictionary == ObjectId.Null) { ed.WriteMessage("没有ExtensionDictionary"+"\n"); return; }; 82 var dicID = ent.ExtensionDictionary; 83 var dic = trans.GetObject(dicID, OpenMode.ForRead) as DBDictionary; 84 if (dic.Contains("DataTableDemo")) 85 { 86 var dataTableID = dic.GetAt("DataTableDemo"); 87 var dataTable = trans.GetObject(dataTableID, OpenMode.ForRead) as DataTable; 88 PrintDataTable(dataTable, ed); 89 } 90 else 91 { 92 ed.WriteMessage("没有DataTableDemo"+"\n"); 93 } 94 } 95 trans.Commit(); 96 } 97 } 98 private static void PrintDataTable(DataTable dataTable, Editor ed) 99 { 100 if (ed == null) return; 101 if (dataTable == null) { ed.WriteMessage("没有DataTable"+"\n"); return; } 102 ed.WriteMessage("DataTable内容:"+"\n"); 103 ed.WriteMessage("列数:"+dataTable.NumColumns.ToString()+"\n"); 104 ed.WriteMessage("行数:"+dataTable.NumRows.ToString()+"\n"); 105 //获取表格内容必须使用dataTable.GetCellAt(i, j)方法,使用GetRowAt()方法获取的行对象无法正确获取单元格内容 106 for (int i = 0; i < dataTable.NumRows; i++) 107 { 108 for (int j = 0; j < dataTable.NumColumns; j++) 109 { 110 var cell = dataTable.GetCellAt(i, j); 111 string s = ""; 112 s += $"[{cell.CellType.ToString()}] "; 113 s+= $"[{cell.Value.GetType().ToString()}] "; 114 s+= $"[{cell.Value.ToString()}]\n"; 115 ed.WriteMessage(s); 116 } 117 } 118 //打印内容如下: 119 //DataTable内容: 120 //列数:4 121 //行数:2 122 //[CharPtr][System.String][NameABC] 123 //[Integer][System.Int32] [1] 124 //[Bool][System.Boolean][True] 125 //[Point][Autodesk.AutoCAD.Geometry.Point3d][(12, 23, 34)] 126 //[CharPtr][System.String][Row2NameABC] 127 //[Integer][System.Int32] [2] 128 //[Bool][System.Boolean][False] 129 //[Point][Autodesk.AutoCAD.Geometry.Point3d][(0, 0, 0)] 130 } 131 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)