基础知识:
根据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。
由于九大符号表无法满足所有的数据存储需求,特别是新版本增加功能的同时还要保证向下兼容,很多数据存储是借助于Database的命名对象词典完成的,可以自行遍历求证。
所有的Entity只能存储在BlockTableRecord下,因此Entity不能直接存入DBDictionary。
不可以把已经添加到Database中的DBObcect添加进DBDictionary,添加操作完成后要使用Database.AddNewlyCreatedDBObject(DBObject obj,bool add)方法进行确认。
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 namespace MichSpace.Tests 2 { 3 public static class XDataDemo 4 { 5 static string AppName = "XDataDemoAppName"; 6 public static void WriteXDataDemo() 7 { 8 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 9 if (doc == null) return; 10 var db = doc.Database; 11 var ed = doc.Editor; 12 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 13 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 14 RegisterAppName(db, AppName); 15 RegisterAppName(db, AppName+"2"); 16 var xData = CreatNewXData(); 17 var xData2 = CreatNewXData2(); 18 using (var trans = db.TransactionManager.StartTransaction()) 19 { 20 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); 21 ent.XData = xData;//此处使用的赋值语句 22 //CAD内部好像进行了额外的处理 23 //比如验证AppName是否已注册、验证该实体的XData中是否已有相同的AppName的XData 24 ent.XData = xData2;//第二次赋值不会覆盖第一次的赋值,因为两个XData的AppName不相同 25 ed.WriteMessage("写入XData成功"+"\n"); 26 trans.Commit(); 27 } 28 } 29 public static void ReadXDataDemo() 30 { 31 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 32 if (doc == null) return; 33 var db = doc.Database; 34 var ed = doc.Editor; 35 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 36 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 37 using (var trans = db.TransactionManager.StartTransaction()) 38 { 39 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); 40 var xData = ent.XData;//获取全部XData 41 // xData=ent.GetXDataForApplication(AppName);//获取指定应用的XData 42 PrintXData(xData, ed); 43 } 44 } 45 public static void ClearXDataDemo() 46 { 47 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 48 if (doc == null) return; 49 var db = doc.Database; 50 var ed = doc.Editor; 51 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 52 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 53 RegisterAppName(db, AppName); 54 55 using (var trans = db.TransactionManager.StartTransaction()) 56 { 57 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite); 58 //这两种方法不能清除XData 59 //ent.XData=null; 60 //ent.XData=new ResultBuffer(); 61 //测试通过的方法如下: 62 var xData = new ResultBuffer(); 63 xData.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName)); 64 //只能清除指定AppName的XData 65 //要清除全部XData,需要遍历所有AppName,并删除对应XData 66 ent.XData = xData; 67 ed.WriteMessage("清除XData成功。"+"\n"); 68 trans.Commit(); 69 } 70 } 71 private static void RegisterAppName(Database db, string appName) 72 { 73 using (var trans = db.TransactionManager.StartTransaction()) 74 { 75 var regAppTable = trans.GetObject(db.RegAppTableId, OpenMode.ForRead) as RegAppTable; 76 if (!regAppTable.Has(appName)) 77 { 78 regAppTable.UpgradeOpen(); 79 var regAppTableRecord = new RegAppTableRecord(); 80 regAppTableRecord.Name = appName; 81 regAppTable.Add(regAppTableRecord); 82 trans.AddNewlyCreatedDBObject(regAppTableRecord, true); 83 trans.Commit(); 84 } 85 } 86 } 87 private static ResultBuffer CreatNewXData() 88 { 89 var xData = new ResultBuffer(); 90 //插入了注册的应用名称,用于区分不同应用的XData 91 xData.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName)); 92 //插入了一个字符串,用于记录需要的信息 93 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Point_1")); 94 //插入了一个点的坐标,Entity发生Transform时,点的坐标也会发生变化 95 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(100, 200, 300))); 96 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Point_2")); 97 //使用相同的DxfCode插入了第二个点,两次插入的数据都会被保存在ResultBuffer中 98 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(400, 500, 600))); 99 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Vector_1")); 100 //插入了一个向量,此处需要将向量转化为Point3d数据类型 101 //向量对旋转的Transform敏感,对平移和缩放不敏感 102 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXDir, new Point3d(100, 0, 0))); 103 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "scale")); 104 //插入了一个缩放因子,对平移和旋转不敏感 105 xData.Add(new TypedValue((int)DxfCode.ExtendedDataScale, 1.0)); 106 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "Vector_2")); 107 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXDir, new Point3d(0, 200, 0))); 108 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "RealNumber")); 109 //插入了一个实数,不跟随Entity的Transform变化 110 xData.Add(new TypedValue((int)DxfCode.ExtendedDataReal, 1.0)); 111 return xData; 112 } 113 private static ResultBuffer CreatNewXData2() 114 { 115 var xData = new ResultBuffer(); 116 xData.Add(new TypedValue((int)DxfCode.ExtendedDataRegAppName, AppName+"2")); 117 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "APP2_Point_1")); 118 xData.Add(new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(100, 200, 300))); 119 xData.Add(new TypedValue((int)DxfCode.ExtendedDataAsciiString, "APP2_Point_2")); 120 return xData; 121 } 122 private static void PrintXData(ResultBuffer xData, Editor ed) 123 { 124 if (ed==null) return; 125 if (xData == null) { ed.WriteMessage("没有XData"+"\n"); return; } 126 ed.WriteMessage("XData内容:"+"\n"); 127 foreach (TypedValue tv in xData) 128 { 129 string s = ""; 130 s+=string.Format("{0,-8}", $"[{tv.TypeCode.ToString()}]"); 131 s+=string.Format("{0,-40}", $"[{tv.Value.GetType().ToString()}]"); 132 s+=string.Format("{0,-60}", $"[{tv.Value.ToString()}]"); 133 s+="\n"; 134 ed.WriteMessage(s); 135 } 136 //打印内容如下: 137 //[1001] [System.String] [XDataDemoAppName] 138 //[1000] [System.String] [Point_1] 139 //[1011] [Autodesk.AutoCAD.Geometry.Point3d] [(100,200,300)] 140 //[1000] [System.String] [Point_2] 141 //[1011] [Autodesk.AutoCAD.Geometry.Point3d] [(400,500,600)] 142 //[1000] [System.String] [Vector_1] 143 //[1013] [Autodesk.AutoCAD.Geometry.Point3d] [(100,0,0)] 144 //[1000] [System.String] [scale] 145 //[1042] [System.Double] [1] 146 //[1000] [System.String] [Vector_2] 147 //[1013] [Autodesk.AutoCAD.Geometry.Point3d] [(0,200,0)] 148 //[1000] [System.String] [RealNumber] 149 //[1040] [System.Double] [1] 150 //[1001] [System.String] [XDataDemoAppName2] 151 //[1000] [System.String] [APP2_Point_1] 152 //[1011] [Autodesk.AutoCAD.Geometry.Point3d] [(100,200,300)] 153 //[1000] [System.String] [APP2_Point_2] 154 } 155 } 156 }
XRecord:
tobecontinued
DataTable:
tobecontinued