Skip to content
C#
图层与块

图层与块

图层和块是 AutoCAD 组织结构的核心。图层控制可见性和打印,块实现组件复用。


图层操作

图层表结构

LayerTable (容器,Database.LayerTableId 获取)
  └── LayerTableRecord (每条记录代表一个图层)
        ├── Name              图层名
        ├── IsOff / IsFrozen  开关/冻结
        ├── IsLocked          锁定
        ├── Color             颜色
        ├── LinetypeObjectId  线型
        ├── LineWeight        线宽
        └── IsPlottable       是否打印

创建图层

public ObjectId CreateLayer(Database db, Transaction tr, string layerName)
{
    LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);

    // 检查图层是否已存在
    if (lt.Has(layerName))
        return lt[layerName];

    // 创建新图层
    LayerTableRecord ltr = new LayerTableRecord();
    ltr.Name = layerName;
    ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, 1);  // 红色

    // 添加到图层表
    lt.UpgradeOpen();   // 图层表需要写权限才能添加
    ObjectId layerId = lt.Add(ltr);
    tr.AddNewlyCreatedDBObject(ltr, true);

    return layerId;
}

修改图层属性

public void SetLayerColor(Database db, Transaction tr, string layerName, short colorIndex)
{
    LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);

    if (!lt.Has(layerName)) return;

    LayerTableRecord ltr = (LayerTableRecord)tr.GetObject(
        lt[layerName], OpenMode.ForWrite);

    ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
    ltr.IsOff = false;
    ltr.IsFrozen = false;
    ltr.IsLocked = false;
}

遍历所有图层

public void ListAllLayers(Database db, Transaction tr, Editor ed)
{
    LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);

    foreach (ObjectId id in lt)
    {
        LayerTableRecord ltr = (LayerTableRecord)tr.GetObject(id, OpenMode.ForRead);
        ed.WriteMessage($"\n图层:{ltr.Name,-20} "
            + $"颜色:{ltr.Color.ColorIndex,-3} "
            + $"{(ltr.IsOff ? "关闭" : "打开")}");
    }
}

删除图层

public bool DeleteLayer(Database db, Transaction tr, string layerName)
{
    LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);

    if (!lt.Has(layerName)) return false;

    ObjectId layerId = lt[layerName];

    // 检查图层上是否有实体(粗略检查)
    // 注意:AutoCAD 不允许删除包含实体的图层和 0 层

    lt.UpgradeOpen();
    lt.Erase(layerId);   // 标记为删除
    return true;
}

🚨 0 层和 Defpoints 层是 AutoCAD 系统保留图层,不能删除。当前图层不能删除。包含实体的图层不能删除。


获取/设置当前图层

// 获取当前图层
string currentLayer = db.Clayer;

// 设置当前图层
using (Transaction tr = db.TransactionManager.StartTransaction())
{
    db.Clayer = GetLayerId(db, tr, "WALL");
    tr.Commit();
}

// 辅助方法
ObjectId GetLayerId(Database db, Transaction tr, string name)
{
    LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
    return lt.Has(name) ? lt[name] : db.LayerZero;   // fallback 到 0 层
}

块操作

块结构

BlockTable (容器,Database.BlockTableId 获取)
  ├── BlockTableRecord "*Model_Space"   模型空间(特殊的块表记录)
  ├── BlockTableRecord "*Paper_Space"   图纸空间
  └── BlockTableRecord "MyBlock"        用户定义的块定义
        ├── Line
        ├── Circle
        └── ...

BlockReference — 块定义的实例(插入到模型空间的块引用)

🔬 AutoCAD 中每个 DWG 都有一个 BlockTable,模型空间和图纸空间本身就是特殊的 BlockTableRecord。BlockTableRecord 存储"定义",BlockReference 是"实例"。

  • 模型空间和图纸空间本身也是块表记录,只是它们是特殊的、系统自带的
  • 它们和其他用户定义的块定义(如"MyBlock")平级,都在BlockTable里
  • 区别在于:模型空间/图纸空间里的内容是直接显示在屏幕上的,而用户定义的块定义只是"模具",需要实例化(块参照)才能看到

创建块定义

public ObjectId CreateBlockDefinition(Database db, Transaction tr, string blockName)
{
    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

    if (bt.Has(blockName))
        return bt[blockName];   // 已存在,直接返	回

    // 创建块表记录(块定义)
    BlockTableRecord btr = new BlockTableRecord();
    btr.Name = blockName;
    btr.Origin = Point3d.Origin;   // 插入基点

    // 向块定义中添加实体
    btr.AppendEntity(new Line(
        new Point3d(0, 0, 0),
        new Point3d(10, 0, 0)
    ));
    btr.AppendEntity(new Circle(
        new Point3d(5, 5, 0),
        Vector3d.ZAxis,
        5
    ));

    // 添加到块表
    bt.UpgradeOpen();
    ObjectId btrId = bt.Add(btr);
    tr.AddNewlyCreatedDBObject(btr, true);

    return btrId;
}

插入块引用

public ObjectId InsertBlock(Database db, Transaction tr,
    string blockName, Point3d position,
    double rotation = 0, double scale = 1.0)
{
    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

    if (!bt.Has(blockName))
        throw new ArgumentException($"块 '{blockName}' 不存在");

    // 创建块引用
    BlockReference br = new BlockReference(position, bt[blockName]);
    br.Rotation = rotation;
    br.ScaleFactors = new Scale3d(scale, scale, scale);

    // 添加到模型空间(或任意目标 BlockTableRecord)
    BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(
        bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
    ObjectId brId = modelSpace.AppendEntity(br);
    tr.AddNewlyCreatedDBObject(br, true);

    return brId;
}

带属性的块

// 1. 在块定义中创建属性定义
AttributeDefinition attDef = new AttributeDefinition();
attDef.Position = new Point3d(5, -10, 0);  // 属性文字显示的位置(相对于块插入点)
attDef.Tag = "DOOR_ID";						 // 标签名(内部标识,不可见)
attDef.Prompt = "输入门编号";				  // 插入时提示用户输入的文字
attDef.TextString = "D001";					// 默认值
attDef.Height = 3.0;						// 文字高度

btr.AppendEntity(attDef);    // 添加到块定义
//此时,块定义中多了一个“属性定义”的“槽位”,它本身不包含具体值,只是一个模板。

// 2. 插入块引用后,添加属性值
BlockReference br = new BlockReference(insertPoint, bt[blockName]);
modelSpace.AppendEntity(br);

// 遍历块定义中的属性定义,创建属性引用
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[blockName], OpenMode.ForRead);

foreach (ObjectId attDefId in btr)
{
    AttributeDefinition ad = tr.GetObject(attDefId, OpenMode.ForRead)
        as AttributeDefinition;
    if (ad != null)
    {
        // 创建属性引用(属性值对象)
        AttributeReference ar = new AttributeReference();
          // 用属性定义初始化位置、字体、标签等(继承块参照的变换)
        ar.SetAttributeFromBlock(ad, br.BlockTransform);
        // 设置具体的值(此处硬编码为"D042",实际可让用户输入)
        ar.TextString = "D042";    // 设置属性值
         // 将属性引用添加到块参照的属性集合中
        br.AttributeCollection.AppendAttribute(ar);
        tr.AddNewlyCreatedDBObject(ar, true);
    }
}

Note

  • 块定义(BlockTableRecord):好比一个“模具”,里面包含了几何图形(线、圆等)和属性定义(AttributeDefinition),后者定义了这个块“有哪些信息字段”。
  • 块参照(BlockReference):是“模具”生产出来的“产品”,每个产品都有具体的属性值(AttributeReference)

Note

关系图

[块定义(BlockTableRecord)] ├── 几何图形(Line, Circle …) ├── 属性定义1(AttributeDefinition) │ ├── Tag = “DOOR_ID” │ ├── Prompt = “输入门编号” │ └── TextString = “D001”(默认值) └── 属性定义2(可选)…

[块参照(BlockReference)] ← 插入到图纸 ├── 位置 (insertPoint) ├── 几何图形(从块定义复制) └── 属性引用1(AttributeReference) ├── Tag = “DOOR_ID”(从定义继承) └── TextString = “D042”(实际赋的值)


如何读取已有的带属性块参照?

如果你要获取一个已存在的块参照的属性值,可以这样:

BlockReference br = ...;
foreach (ObjectId attId in br.AttributeCollection)
{
    AttributeReference ar = tr.GetObject(attId, OpenMode.ForRead) as AttributeReference;
    if (ar != null)
    {
        string tag = ar.Tag;           // 标签
        string value = ar.TextString;  // 属性值
        ed.WriteMessage($"\n{tag} = {value}");
    }
}

遍历块引用

public void ListAllBlockReferences(Database db, Transaction tr, Editor ed)
{
    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
    BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(
        bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);

    var blockCounts = new Dictionary<string, int>();

    foreach (ObjectId id in modelSpace)
    {
        BlockReference br = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
        if (br != null)
        {
            BlockTableRecord btr = (BlockTableRecord)tr.GetObject(
                br.BlockTableRecord, OpenMode.ForRead);
            string blockName = btr.Name;

            if (blockCounts.ContainsKey(blockName))
                blockCounts[blockName]++;
            else
                blockCounts[blockName] = 1;
        }
    }

    foreach (var kvp in blockCounts.OrderByDescending(x => x.Value))
    {
        ed.WriteMessage($"\n块 {kvp.Key}:{kvp.Value} 个引用");
    }
}

读取属性值

public Dictionary<string, string> GetAttributeValues(BlockReference br, Transaction tr)
{
    var attributes = new Dictionary<string, string>();

    foreach (ObjectId attId in br.AttributeCollection)
    {
        AttributeReference ar = tr.GetObject(attId, OpenMode.ForRead)
            as AttributeReference;
        if (ar != null)
        {
            attributes[ar.Tag] = ar.TextString;
        }
    }

    return attributes;
}

导入外部 DWG 中的块

public void ImportBlockFromFile(Database destDb, string sourceFilePath, string blockName)
{
    using (Database sourceDb = new Database(false, true))
    {
        // 读取源文件(只读模式,不创建撤销日志)
        sourceDb.ReadDwgFile(sourceFilePath, FileOpenMode.OpenForReadAndAllShare, false, "");

        using (Transaction tr = destDb.TransactionManager.StartTransaction())
        {
            ObjectIdCollection blockIds = new ObjectIdCollection();

            BlockTable bt = (BlockTable)tr.GetObject(destDb.BlockTableId, OpenMode.ForRead);
            if (!bt.Has(blockName))
            {
                // 从源数据库获取块 ObjectId
                using (Transaction sourceTr = sourceDb.TransactionManager.StartTransaction())
                {
                    BlockTable sourceBt = (BlockTable)sourceTr.GetObject(
                        sourceDb.BlockTableId, OpenMode.ForRead);
                    if (sourceBt.Has(blockName))
                        blockIds.Add(sourceBt[blockName]);
                    sourceTr.Commit();
                }

                // 将块定义从源数据库克隆到目标数据库
                IdMapping mapping = new IdMapping();
                destDb.WblockCloneObjects(
                    blockIds,
                    destDb.BlockTableId,
                    mapping,
                    DuplicateRecordCloning.Replace,
                    false);
            }

            tr.Commit();
        }
    }
}