了解AutoCAD对象层次结构 —— 6 ——块表记录
块表记录是包裹实体对象的最后一层包装了,接下来让我们继续利用MgdDbg工具查看上一小节创建的块定义内的对象有哪些。
操作步骤如下:选择块表记录TestBlock,在右侧列表中找到“Entities within block”,选中该行并单击鼠标左键,弹出Snoop Objects对话框,列表中显示出了创建块定义时选择的两个对象:圆和直线。(下图中BED、BEE分别为实体对象的句柄(Handle))。
图 4‑10 TestBlock块表记录中的实体对象
如果刚才创建块时,您选择了删除所选对象,此时模型空间应该是不存在实体对象的,如果您选择*Model_Space块表记录,可以发现Entities within block为常规字体,没有加粗,点击时不会弹出对话框。
如果你选择的是保留所选对象,此时模型空间是存在一个圆和一条直线的,*Model_Space块表记录中的实体对象与TestBlock块表记录中的实体对象一致。(注意截图中选中的块表记录与上图不同)
图 4‑11 *Model_Space块表记录中的实体对象
如果你选择的是转换为块,此时模型空间中有一个块参照,*Model_Space块表记录中的实体对象为块参照。
图 4‑12 *Model_Space块表记录中的实体对象
再次回想之前我们利用命令Block创建块的过程:
操作 |
块表变化 |
块表记录变化 |
|
模型空间创建实体 |
无变化 |
*Model_Space内增加实体 |
|
创建块定义 |
增加块表记录TestBlock |
TestBlock内增加实体 |
|
创建块时的不同选项 |
保留 |
无变化 |
不变 |
转换为块 |
无变化 |
*Model_Space中实体被删除,增加块参照 |
|
删除 |
无变化 |
*Model_Space中实体被删除 |
注:1.表中的实体指的是用于创建块的实体,例如之前示例中的圆和直线。
2.如果用命令Bedit来创建块,上述块表及块表记录的变化会有多不同。
接下来就让我看一下如何采用代码块表、块表记录及块表记录内部实体的查询。继续4.1.2 节的项目,在MyCommands类中添加以下代码:
01 [CommandMethod("MyGroup", "ListEnts", CommandFlags.Modal)] 02 public void ListEnts() 03 { 04 Document doc = Application.DocumentManager.MdiActiveDocument; //活动文档 05 Editor ed = doc.Editor; //获取编辑器 06 Database db = doc.Database; //获取数据库 07 //Database db = HostApplicationServices.WorkingDatabase; //另一种方法 08 ObjectId blockTblId = db.BlockTableId; //块表Id 09 using (Transaction tr = db.TransactionManager.StartTransaction()) 10 { 11 BlockTable bt = blockTblId.GetObject( //块表 12 OpenMode.ForRead) as BlockTable; 13 foreach (ObjectId btrId in bt) //遍历块表记录 14 { 15 BlockTableRecord btr = btrId.GetObject( 16 OpenMode.ForRead) as BlockTableRecord; 17 ed.WriteMessage("\n块表记录:{0}", btr.Name); //输出块表记录名称 18 foreach (ObjectId entId in btr) //遍历实体 19 { 20 DBObject obj = entId.GetObject(OpenMode.ForRead); 21 ed.WriteMessage("\n实体类型为:{0}\t句柄:{1}", 22 obj.GetType().Name, obj.Handle); //类型名称及句柄 23 } 24 } 25 tr.Commit(); 26 } 27 }
这段代码输出了每条块表记录的名称,并遍历块表记录的实体,输出实体类型与句柄。代码第6行通过活动文档获取数据库,第7行(注释掉的一行)采用另一种方法获取了当前正在操作的数据库(注意:这种方法获取的数据库并不一定是活动文档的数据库,您需要查阅更多资料);代码第8行通过数据库获得块表的Id;代码第11、12行打开块表;代码第13行针对块表中的每一个ObjectId进行循环操作,这个Id是块表记录的Id;代码第15、16行代开块表记录;代码第13行针对块表记录中的每一个ObjectId进行循环操作,这个Id是数据库对象(实体)的Id。阅读这段代码,您应该把重点放在理解对象之间层级关系上,至于如何打开对象、获取对象属性等操作并不是重点。
代码第9行中涉及到的事务(Transaction)、第11、12行涉及到的由ObjectId获取Object后续章节中详细讲述。
再次回想一下刚才的操作过程:获取文档、获取数据库、从数据库中找到块表、块表内部是块表记录、块表记录内是实体。一层又一层的包装箱,如果您能清楚的知道刚才拆了几层包装箱,说明您对数据库之间的层级关系也就明白了。
编译、加载项目,打开之前的dwg文件,输入命令ListEnts,看到的结果与下图类似;如果您是在其他图形文件,例如用_AutoCAD Civil 3D (Metric) NCS.dwt样板文件创建的图形,这里列出的块表记录会很多。
图 4‑13 代码运行结果
(注:前后几个截图中实体句柄不同是因为我并不是利用一个图形文件完成的测试)
您可能早就想到这样一个问题:如何直接获取模型空间或某个图纸空间的呢?
在MyCommands类中添加以下代码:
01 [CommandMethod("MyGroup", "ListBlkRcd", CommandFlags.Modal)] 02 public void ListBlkRcd() 03 { 04 string[] btrNames = new string[]{ 05 "*Model_Space","*model_space","*Paper_Space", 06 "*Paper_Space0", "*Paper_Space1", "*Paper_Space2", 07 "Line","TestBlock","Something", 08 BlockTableRecord.ModelSpace,BlockTableRecord.PaperSpace}; 09 Document doc = Application.DocumentManager.MdiActiveDocument; //活动文档 10 Editor ed = doc.Editor; //获取编辑器 11 Database db = doc.Database; //获取数据库 12 //Database db = HostApplicationServices.WorkingDatabase; 13 ObjectId blockTblId = db.BlockTableId; //块表Id 14 using (Transaction tr = db.TransactionManager.StartTransaction()) 15 { 16 BlockTable bt = blockTblId.GetObject( //块表 17 OpenMode.ForRead) as BlockTable; 18 foreach (string btrName in btrNames) //遍历每一名称 19 { 20 if (bt.Has(btrName)) //判断是否存在 21 { 22 BlockTableRecord btr = bt[btrName].GetObject( 23 OpenMode.ForRead) as BlockTableRecord; 24 ed.WriteMessage("\n块表记录{0}找到了!\t句柄:{1}", 25 btr.Name, btr.Handle); //输出名称及句柄 26 } 27 else 28 { 29 ed.WriteMessage("\n块表记录{0}不存在!", btrName); 30 } 31 } 32 tr.Commit(); 33 } 34 }
因为目前没有涉及到输入操作,因此代码第4-8行用字符串数组存储了若干块表记录名称,注意其大小写,代码第8行中的两个“字符串”是类BlockTableRecord中的两个静态属性,用于返回模型空间及图纸空间的名称。代码第18行对每一块表记录名称进行循环操作;代码第20-30行根据是否存在相应的块表记录输出不同的信息。
代码第22行bt[btrName]用于获取与名称btrName对应的块表记录的ObjectId,是此段代码的核心。也就是说,我们可以通过名称来获取块表记录的Id,之后就可以进行其他操作。
经过测试,名称字符串的大小写并不敏感,"*Model_Space"、"*model_space"都能顺利获取到模型空间。
图 4‑14 块表记录查询结果
图 4‑15