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}");
        }
    }
}

}

posted @ 2026-03-14 02:02  LOSOB  阅读(1)  评论(0)    收藏  举报