C# 进行的CAD二次开发(炸开属性块)
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
//using Autodesk.AutoCAD.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IntelligentExpl
{
//智能炸开(炸开属性块后,所有的属性不能变)
public class IntelligentExpl_test
{
[CommandMethod("x1", CommandFlags.Modal | CommandFlags.NoBlockEditor)]
public void Intelligent()
{
// 提示用户
// System.Windows.Forms.MessageBox.Show("选择属性块进行炸开");
// 获取当前文档和编辑器
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
//// 步骤1:让用户指定交叉窗口的第一个角点
//PromptPointOptions ppo = new PromptPointOptions("\n请指定交叉窗口的第一个角点: ");
//PromptPointResult ppr = ed.GetPoint(ppo);
//if (ppr.Status != PromptStatus.OK)
// return;
//Point3d pt1 = ppr.Value;
//// 步骤2:让用户指定第二个角点
//ppo.Message = "\n请指定交叉窗口的第二个角点: ";
//ppo.BasePoint = pt1;
//ppo.UseBasePoint = true;
//ppr = ed.GetPoint(ppo);
//if (ppr.Status != PromptStatus.OK)
// return;
//Point3d pt2 = ppr.Value;
// 步骤3:创建过滤器,只选择块参照(INSERT)
//TypedValue[] filterList = new TypedValue[]
//{
// new TypedValue((int)DxfCode.Start, "INSERT")
//};
//SelectionFilter filter = new SelectionFilter(filterList);
//// 步骤4:执行交叉窗口选择
//PromptSelectionResult psr = ed.SelectCrossingWindow(pt1, pt2, filter);
//if (psr.Status != PromptStatus.OK)
//{
// ed.WriteMessage("\n没有找到符合条件的块。");
// return;
//}
//SelectionSet selectionSet = psr.Value;
//int blockCount = selectionSet.Count;
//if (blockCount == 0)
//{
// ed.WriteMessage("\n未选中任何块。");
// return;
//}
// ed.WriteMessage($"\n已选中 {blockCount} 个块,开始炸开...");
// //以上的选择为点选,蚂蚁线跟着过去,体验感不行
// 创建选择选项,用于定制交互行为
PromptSelectionOptions pso = new PromptSelectionOptions();
pso.MessageForAdding = "\n请指定交叉窗口选择属性块"; // 显示在选择时的提示
// 可以设置关键字(如果需要)
// pso.SetKeywords(new string[] { "窗口", "多边形" });
// 执行带预览的选择操作
PromptSelectionResult psr = ed.GetSelection(pso);
// 处理用户取消或未选择的情况
if (psr.Status != PromptStatus.OK)
{
if (psr.Status == PromptStatus.Cancel)
ed.WriteMessage("\n用户取消了选择。");
else if (psr.Status == PromptStatus.Error)
ed.WriteMessage("\n选择过程中发生错误。");
else if (psr.Status == PromptStatus.None)
ed.WriteMessage("\n没有选中任何对象。");
return;
}
// 获取选择集
SelectionSet selectionSet = psr.Value;
int blockCount = selectionSet.Count;
if (blockCount == 0)
{
ed.WriteMessage("\n未选中任何块。");
return;
}
ed.WriteMessage($"\n已选中 {blockCount} 个块,开始炸开...");
// 步骤5:开始事务处理,炸开每个块
using (Transaction tr = db.TransactionManager.StartTransaction()) //应用using 可以自动释放内存
{
// 获取当前要添加实体的空间(通常是模型空间)
// BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // as作为
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; //(BlockTable) 强制转换
BlockTableRecord currentSpace = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); //块表记录
int explodedCount = 0; // 统计带属性的块数量
int attrBlockCount = 0; // 统计带属性的块数量
List<string> failedBlocks = new List<string>();
foreach (ObjectId objId in selectionSet.GetObjectIds()) //遍历选择集(块中找属性块)
{
BlockReference blockRef = tr.GetObject(objId, OpenMode.ForWrite) as BlockReference; //定义块参照
if (blockRef == null) continue; //如果句柄为空 直接跳出循环
if (blockRef.AttributeCollection.Count == 0) // AttributeCollection 跳过没有属性的块
continue; //循环控制语句,用于跳过本次循环中剩下的语句,直接开始下一次循环
attrBlockCount++;
try //抛出异常
{
// 获取块定义名称(用于日志)
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(blockRef.BlockTableRecord, OpenMode.ForRead);
string blockName = btr.Name; //获取块名
ed.WriteMessage(blockName); //
// 炸开块参照,生成一组 DBObject 数组
DBObjectCollection explodedObjects = new DBObjectCollection();
/*这种语法是C# 7.0引入的元组特性,允许你快速创建包含多个不同类型元素的轻量级数据结构,
而无需定义专门的类。这里列表的元素是元组,每个元组有命名字段,方便访问。*/
List<(string Value, Point3d Position, double Height, double Rotation,
ObjectId TextStyleId, string Layer, Autodesk.AutoCAD.Colors.Color Color, double WidthFactor)> attValues =
new List<(string Value, Point3d Position, double Height, double Rotation, ObjectId TextStyleId,
string Layer, Autodesk.AutoCAD.Colors.Color Color, double WidthFactor)>();
foreach (ObjectId attId in blockRef.AttributeCollection) //遍历属性块的属性
{
AttributeReference attRef = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
if (attRef != null)
{
attValues.Add((
attRef.TextString, // 属性值,如 "P001"
attRef.Position, // 世界坐标位置(已包含块变换)
attRef.Height,
attRef.Rotation,
attRef.TextStyleId,
attRef.Layer,
attRef.Color,
// attRef.TextStyleName , //字体样式
attRef.WidthFactor //字体的宽度
));
}
}
// 2. 炸开块参照,生成一组对象(包含几何图形和属性定义)
// DBObjectCollection explodedObjects = new DBObjectCollection();
blockRef.Explode(explodedObjects);
// 3. 遍历爆炸产生的对象,仅添加几何实体(直线、圆等),跳过属性定义
foreach (DBObject obj in explodedObjects)
{
if (obj is Entity entity && !(entity is AttributeDefinition) && !(entity is DBText) && !(entity is MText))
{
// 注意:爆炸产生的属性定义(AttributeDefinition)不是 Entity,不会被添加
currentSpace.AppendEntity(entity);
tr.AddNewlyCreatedDBObject(entity, true);
}
else
{
obj.Dispose(); // 释放非实体对象(如 AttributeDefinition)
}
}
// 4. 手动创建属性值文字(DBText)
foreach (var val in attValues)
{
DBText valueText = new DBText
{
TextString = val.Value,
Position = val.Position,
Height = val.Height,
Rotation = val.Rotation,
TextStyleId = val.TextStyleId,
Layer = val.Layer,
Color = val.Color,
WidthFactor= val.WidthFactor,
// TextStyleName=val.TextStyleName
};
currentSpace.AppendEntity(valueText);
tr.AddNewlyCreatedDBObject(valueText, true);
}
// 5. 删除原块参照
blockRef.Erase();
explodedCount++;
ed.WriteMessage($"\n已炸开块: {blockName}");
}
catch (System.Exception ex)
{
failedBlocks.Add($"块ID: {objId}, 错误: {ex.Message}");
}
}
// 提交事务
tr.Commit();
// 输出结果
ed.WriteMessage($"\n========== 炸开完成 ==========");
ed.WriteMessage($"\n成功炸开: {explodedCount} 个块");
if (failedBlocks.Count > 0)
{
ed.WriteMessage($"\n失败数量: {failedBlocks.Count}");
foreach (string err in failedBlocks)
{
ed.WriteMessage($"\n {err}");
}
}
ed.WriteMessage($"\n================================");
}
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n错误: {ex.Message}");
}
}
}
}

浙公网安备 33010602011771号