图层与块
图层和块是 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();
}
}
}