今天是本篇内容的第二节,在上一节的基础上继续排兵布阵,来扩充在CAD二次开发中可能用到的图块。这一节介绍一下属性块,属性块与普通的图块相比,多了一个字典作为一组属性来存储信息。我们在图纸中看到的这种表格就是一个属性块:
这是一个自控设计中代表一个可燃气体探测器标注信息的图块,其中TAG代表位号,EL是安装标高,JB是接线箱号,MED是检测介质,我们可以在增强属性编辑器中编辑对应的值来标注探测器的信息。这里的属性是一个字典,也就是一组键值对,这里的值属于AttributeDefinition类,它派生自DBText
它的构造函数里面:position代表该数值显示的位置;value是数值;tag是标签,即属性的名称;prompt是提示信息,可以再增强属性编辑器中起到提示作用;style是显示值的文字的特性。
这一节的目标是封装方法并且注册命令,来制作一个类似的属性块,可能代码比较多,限于篇幅,部分代码的解释详见注释。
首先封装方法,为块表记录添加属性,作为ObjectId的拓展方法,同时需要一个列表存储插入的属性,代码如下:
//为块表记录添加属性public static void AddAttsToBlock(this ObjectId blockId, List atts){ Database db = blockId.Database;//获取数据库对象 //打开块表记录为写的状态 BlockTableRecord btr = (BlockTableRecord)blockId.GetObject(OpenMode.ForWrite); //遍历属性定义对象列表 foreach (AttributeDefinition att in atts) { btr.AppendEntity(att);//为块表记录添加属性 db.TransactionManager.AddNewlyCreatedDBObject(att, true);//事务处理 } btr.DowngradeOpen();//为了安全,将块表记录的状态改为读}
老规矩,这里重载方法,使其可以接受很多属性而不需要转换为列表:
public static void AddAttsToBlock(this ObjectId blockId, params AttributeDefinition[] atts){ blockId.AddAttsToBlock(atts.ToList());}
接下来插入属性块参照的方法,上一节介绍的插入简单块的方法是一个没有返回值的方法,这里因为需要插入属性,所以需要块定义的ObjectId来传递,所以这里以ObjectId为返回类型,返回添加的块参照的Id以便之后的使用:
public static ObjectId InsertBlockReference(this ObjectId spaceId, string layer, string blockName, Point3d position, Scale3d scale, double rotateAngle){ ObjectId blockRefId;//存储要插入的块参照的Id Database db = spaceId.Database;//获取数据库对象 //以读的方式打开块表 BlockTable bt = (BlockTable)db.BlockTableId.GetObject(OpenMode.ForRead); //如果没有blockName表示的块,则程序返回 if (!bt.Has(blockName)) return ObjectId.Null; //以写的方式打开空间(模型空间或图纸空间) BlockTableRecord space = (BlockTableRecord)spaceId.GetObject(OpenMode.ForWrite); //创建一个块参照并设置插入点 BlockReference br = new BlockReference(position, bt[blockName]); br.ScaleFactors = scale;//设置块参照的缩放比例 br.Layer = layer;//设置块参照的层名 br.Rotation = rotateAngle;//设置块参照的旋转角度 ObjectId btrId = bt[blockName];//获取块表记录的Id //打开块表记录 BlockTableRecord record = (BlockTableRecord)btrId.GetObject(OpenMode.ForRead); //添加可缩放性支持 if (record.Annotative == AnnotativeStates.True) { ObjectContextCollection contextCollection = db.ObjectContextManager.GetContextCollection("ACDB_ANNOTATIONSCALES"); ObjectContexts.AddContext(br, contextCollection.GetContext("1:1")); } blockRefId = space.AppendEntity(br);//在空间中加入创建的块参照 db.TransactionManager.AddNewlyCreatedDBObject(br, true);//通知事务处理加入创建的块参照 space.DowngradeOpen();//为了安全,将块表状态改为读 return blockRefId;//返回添加的块参照的Id}
重载上面的方法,使之可以接受除以上信息外的属性键值对:
public static ObjectId InsertBlockReference(this ObjectId spaceId, string layer, string blockName, Point3d position, Scale3d scale, double rotateAngle, Dictionary<string, string> attNameValues){ Database db = spaceId.Database;//获取数据库对象 //以读的方式打开块表 BlockTable bt = (BlockTable)db.BlockTableId.GetObject(OpenMode.ForRead); //如果没有blockName表示的块,则程序返回 if (!bt.Has(blockName)) return ObjectId.Null; //以写的方式打开空间(模型空间或图纸空间) BlockTableRecord space = (BlockTableRecord)spaceId.GetObject(OpenMode.ForWrite); ObjectId btrId = bt[blockName];//获取块表记录的Id //打开块表记录 BlockTableRecord record = (BlockTableRecord)btrId.GetObject(OpenMode.ForRead); //创建一个块参照并设置插入点 BlockReference br = new BlockReference(position, bt[blockName]); br.ScaleFactors = scale;//设置块参照的缩放比例 br.Layer = layer;//设置块参照的层名 br.Rotation = rotateAngle;//设置块参照的旋转角度 space.AppendEntity(br);//为了安全,将块表状态改为读 //判断块表记录是否包含属性定义 if (record.HasAttributeDefinitions) { //若包含属性定义,则遍历属性定义 foreach (ObjectId id in record) { //检查是否是属性定义 AttributeDefinition attDef = id.GetObject(OpenMode.ForRead) as AttributeDefinition; if (attDef != null) { //创建一个新的属性对象 AttributeReference attribute = new AttributeReference(); //从属性定义获得属性对象的对象特性 attribute.SetAttributeFromBlock(attDef, br.BlockTransform); //设置属性对象的其它特性 attribute.Position = attDef.Position.TransformBy(br.BlockTransform); attribute.Rotation = attDef.Rotation; attribute.AdjustAlignment(db); //判断是否包含指定的属性名称 if (attNameValues.ContainsKey(attDef.Tag.ToUpper())) { //设置属性值 attribute.TextString = attNameValues[attDef.Tag.ToUpper()].ToString(); } //向块参照添加属性对象 br.AttributeCollection.AppendAttribute(attribute); db.TransactionManager.AddNewlyCreatedDBObject(attribute, true); } } } db.TransactionManager.AddNewlyCreatedDBObject(br, true); return br.ObjectId;//返回添加的块参照的Id}
这里我们还需要一个方法,用于修改属性块中的属性:
public static void UpdateAttributesInBlock(this ObjectId blockRefId, Dictionary<string, string> attNameValues){ //获取块参照对象 BlockReference blockRef = blockRefId.GetObject(OpenMode.ForRead) as BlockReference; if (blockRef != null) { //遍历块参照中的属性 foreach (ObjectId id in blockRef.AttributeCollection) { //获取属性 AttributeReference attref = id.GetObject(OpenMode.ForRead) as AttributeReference; //判断是否包含指定的属性名称 if (attNameValues.ContainsKey(attref.Tag.ToUpper())) { attref.UpgradeOpen();//切换属性对象为写的状态 //设置属性值 attref.TextString = attNameValues[attref.Tag.ToUpper()].ToString(); attref.DowngradeOpen();//为了安全,将属性对象的状态改为读 } } }}
接下来注册命令,开始先绘制块定义:
public ObjectId MakeInfoTable(){ ObjectId blockId;//用于储存ObjectId Database db = HostApplicationServices.WorkingDatabase;//数据库 //事务处理,用于绘制图形并且添加其到块定义中 using (Transaction trans = db.TransactionManager.StartTransaction()) { //绘制图形 Point3d pt1 = new Point3d(50, 0, 0); Point3d pt2 = new Point3d(50, 20, 0); Point3d pt3 = new Point3d(0, 20, 0); Point3d pt4 = new Point3d(-7.5, 15, 0); Point3d pt5 = new Point3d(8.5, 0, 0); Point3d pt6 = new Point3d(8.5, 20, 0); Point3d pt7 = new Point3d(0, 15, 0); Line line1 = new Line(Point3d.Origin, pt1); Line line2 = new Line(pt1, pt2); Line line3 = new Line(pt2, pt3); Line line4 = new Line(pt3, Point3d.Origin); Line line5 = new Line(pt4, pt7); Line line6 = new Line(pt5, pt6); Line line7 = new Line(pt7, new Point3d(50, 15, 0)); Line line8 = new Line(new Point3d(0, 10, 0), new Point3d(50, 10, 0)); Line line9 = new Line(new Point3d(0, 5, 0), new Point3d(50, 5, 0)); DBText dBText1 = new DBText(); dBText1.Position = new Point3d(2, 15.5, 0); dBText1.Height = 2.5; dBText1.TextString = "TAG"; DBText dBText2 = new DBText(); dBText2.Position = new Point3d(2, 10.5, 0); dBText2.Height = 2.5; dBText2.TextString = "EL"; DBText dBText3 = new DBText(); dBText3.Position = new Point3d(2, 5.5, 0); dBText3.Height = 2.5; dBText3.TextString = "JB"; DBText dBText4 = new DBText(); dBText4.Position = new Point3d(2, 0.5, 0); dBText4.Height = 2.5; dBText4.TextString = "MED"; line1.Color = Color.FromColorIndex(ColorMethod.ByColor, 2); //添加 blockId = db.AddBlockTableRecord("InfoTable", line1, line2, line3, line4, line5, line6, line7, line8, line9, dBText1, dBText2, dBText3, dBText4); trans.Commit(); } return blockId;}
将块定义加入到模型空间:
[CommandMethod("AddInfoTable")]public void AddInfoTable(){ Database db = HostApplicationServices.WorkingDatabase; ObjectId blockId = MakeInfoTable(); using (Transaction trans = db.TransactionManager.StartTransaction()) { //表示探测器的编号 AttributeDefinition attNum = new AttributeDefinition(Point3d.Origin, "1", "INFONUM", "图纸中的序号", ObjectId.Null); //设置属性定义的通用样式 SetStyleForAtt(attNum, false); //设置属性的对齐点 attNum.AlignmentPoint = new Point3d(-6, 15, 0); //表示探测器位号的属性定义 AttributeDefinition attTag = new AttributeDefinition(Point3d.Origin, "1", "INFOTAG", "仪表位号", ObjectId.Null); SetStyleForAtt(attTag, false); attTag.AlignmentPoint = new Point3d(10, 15.5, 0); //表示探测器标高的属性定义 AttributeDefinition attHeight = new AttributeDefinition(Point3d.Origin, "500", "INFOHEIGHT", "仪表标高", ObjectId.Null); SetStyleForAtt(attHeight, false); attHeight.AlignmentPoint = new Point3d(10, 10.5, 0); //表示探测器检测介质的属性定义 AttributeDefinition attType = new AttributeDefinition(Point3d.Origin, "可燃气体", "INFOMEDIUM", "检测器检测介质", ObjectId.Null); SetStyleForAtt(attType, false); attType.AlignmentPoint = new Point3d(10, 0.5, 0); //表示探测器所在接线箱的定义 AttributeDefinition attJB = new AttributeDefinition(Point3d.Origin, "0", "INFOJBOX", "检测器进的接线箱", ObjectId.Null); SetStyleForAtt(attJB, false); attJB.AlignmentPoint = new Point3d(10, 5.5, 0); blockId.AddAttsToBlock(attNum, attTag, attHeight, attType, attJB); trans.Commit(); }}
设置字体属性:
private void SetStyleForAtt(AttributeDefinition att, bool invisible){ att.Height = 2.5;//属性文字的高度 //属性文字的水平对齐方式为左对齐 att.HorizontalMode = TextHorizontalMode.TextLeft; //属性文字的垂直对齐方式为底部对齐 att.VerticalMode = TextVerticalMode.TextBottom; att.Invisible = invisible; //属性文字的可见性}
插入属性块参照:
[CommandMethod("InsertInfoTable")]public void InsertInfoTable(){ Database db = HostApplicationServices.WorkingDatabase; ObjectId spaceId = db.CurrentSpaceId;//获取当前空间(模型空间或图纸空间) using (Transaction trans = db.TransactionManager.StartTransaction()) { Dictionary<string, string> atts = new Dictionary<string, string>(); //属性 atts.Add("NUM", "1"); //插入块参照 spaceId.InsertBlockReference("0", "InfoTable", new Point3d(100, 0, 0), new Scale3d(1), 0, atts); //提交事务处理 trans.Commit(); }}
代码执行效果如下面的视频所示:
好了,这一节的内容就是这样,代码比较多,但是最后插入块参照的命令代码缺比较简洁明确,这刚好体现了面向对象编程代码的复用性,下一节继续介绍有关图块的内容,如果有建议或是批评指教,欢迎在公众号内留言,谢谢!