I'm currently inserting dynamic blocks via an ASP.NET Core 8 Web API (AutoCAD 2026, .NET 8). These blocks include attributes whose default values contain field expressions, referencing dynamic properties using BlockPlaceholder. I don't need or want to change this by default value. This is exactly what I need to show.
When I insert the blocks manually in AutoCAD, the attributes appear correctly and display the expected field content. However, when I insert them programmatically using BlockReference, the attributes do not appear visibly in the drawing — even though they exist — unless I run ATTSYNC manually.
Using ATTSYNC is a terrible idea (I'm working with a huge amount of blocks, 2000++).
I already try to add the attribute references for each newly created block reference and played around with some methods but nothing worked.
Could anybody please give me a hand with this?
This is my general code so far:
public object[,] DibujarDB(string blkName, string[] propsName, object[,] propsValue, ActualizadorFormulario actualizador)
{
var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
using (var trans = db.TransactionManager.StartTransaction())
{
try
{
// Obtener la tabla de bloques
var bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
if (!bt.Has(blkName))
{
ed.WriteMessage($"\nEl bloque '{blkName}' no existe.");
return null;
}
// Acceder al espacio modelo y a la tabla de capas
var btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
var lt = (LayerTable)trans.GetObject(db.LayerTableId, OpenMode.ForRead);
int rows = propsValue.GetLength(0);
string miEstado;
var blkId = bt[blkName];
// Mapeo nombre → índice de columnas en la tabla propsValue
var propIndices = propsName
.Select((name, index) => new { name, index })
.ToDictionary(p => p.name, p => p.index);
// Índices clave
int idxDibujar = propIndices["DIBUJAR"];
int idxHandler = propIndices["HANDLER"];
int idxCX = propIndices["CX"];
int idxCY = propIndices["CY"];
int idxCZ = propIndices["CZ"];
int idxANG = propIndices["ANG"];
int idxCAPA = propIndices["CAPA"];
int idxESCX = propIndices["ESCX"];
int idxESCY = propIndices["ESCY"];
int idxESCZ = propIndices["ESCZ"];
// Asegurar que todas las capas existan
RevisionCapas(trans, lt, idxCAPA, propsValue);
// Nombres de propiedades dinámicas (a partir de la columna 12)
string[] dynPropNames = propsName.Skip(11).ToArray();
// Obtener definiciones de atributos NO constantes del bloque base
var blkDef = (BlockTableRecord)trans.GetObject(blkId, OpenMode.ForRead);
var attDefs = blkDef
.Cast<ObjectId>()
.Where(id => id.ObjectClass.Name == "AcDbAttributeDefinition")
.Select(id => (AttributeDefinition)trans.GetObject(id, OpenMode.ForRead))
.Where(attDef => !attDef.Constant)
.ToArray();
// Avisar al usuario
miEstado = $"Dibujando {blkName}.";
actualizador.ActualizarEstado(miEstado);
for (int i = 0; i < rows; i++)
{
// Verificar si se debe dibujar esta fila
var dibujarVal = propsValue[i, idxDibujar]?.ToString();
if (!string.Equals(dibujarVal, "SI", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(dibujarVal, "TRUE", StringComparison.OrdinalIgnoreCase))
{
continue;
}
// Validar y convertir coordenadas, ángulo y escala
if (!double.TryParse(propsValue[i, idxCX]?.ToString(), out double cx) ||
!double.TryParse(propsValue[i, idxCY]?.ToString(), out double cy) ||
!double.TryParse(propsValue[i, idxCZ]?.ToString(), out double cz) ||
!double.TryParse(propsValue[i, idxANG]?.ToString(), out double ang) ||
!double.TryParse(propsValue[i, idxESCX]?.ToString(), out double escX) ||
!double.TryParse(propsValue[i, idxESCY]?.ToString(), out double escY) ||
!double.TryParse(propsValue[i, idxESCZ]?.ToString(), out double escZ))
{
ed.WriteMessage($"\nError: Datos inválidos en la fila {i}");
continue;
}
string capa = propsValue[i, idxCAPA]?.ToString() ?? "0";
// Crear instancia del bloque
var blkRef = new BlockReference(new Point3d(cx, cy, cz), blkId)
{
Rotation = ang,
ScaleFactors = new Scale3d(escX, escY, escZ),
Layer = capa
};
// Añadimos el bloque al espacio modelo
btr.AppendEntity(blkRef);
trans.AddNewlyCreatedDBObject(blkRef, true);
// Guardamos el handler en la tabla de valores
propsValue[i, idxHandler] = blkRef.Handle.ToString();
// Obtenemos los campos asociados a los atributos del bloque
var camposAtributos = InfoAtributos(blkRef, blkName);
foreach (var attDef in attDefs)
{
var attRef = new AttributeReference();
attRef.SetAttributeFromBlock(attDef, blkRef.BlockTransform);
attRef.TextString = "";
if (camposAtributos.TryGetValue(attDef.Tag, out Field campo))
{
attRef.SetField(campo);
campo.Evaluate(); // Forzamos la evaluación del campo
}
else if (attDef.Tag == "HANDLE") // texto plano
{
attRef.TextString = propsValue[i, idxHandler].ToString();
}
blkRef.AttributeCollection.AppendAttribute(attRef);
trans.AddNewlyCreatedDBObject(attRef, true);
}
// Asignar propiedades dinámicas si aplican
foreach (var propName in dynPropNames)
{
DynamicBlockReferenceProperty prop = blkRef.DynamicBlockReferencePropertyCollection
.Cast<DynamicBlockReferenceProperty>()
.FirstOrDefault(p => p.PropertyName == propName)!;
if (prop != null)
{
var valStr = propsValue[i, propIndices[propName]]?.ToString();
if (valStr != null && prop.Value.ToString() != valStr)
{
try
{
prop.Value = Convert.ChangeType(valStr, prop.Value.GetType());
}
catch
{
ed.WriteMessage($"\nAdvertencia: No se pudo asignar '{valStr}' a la propiedad '{propName}' en fila {i}");
}
}
}
}
// Reportar progreso
actualizador.ActualizarProgresoProceso();
}
// Confirmar la transacción
trans.Commit();
// Finalizar estado
miEstado = $"Terminamos con ({blkName})";
actualizador.ActualizarEstado(miEstado);
return propsValue;
}
catch (System.Exception ex)
{
ed.WriteMessage($"\nError: {ex.Message}");
}
return null;
}
}
private Dictionary<string, Field> InfoAtributos(BlockReference blkRef, string blkName)
{
var resultado = new Dictionary<string, Field>();
if (blkName == "BR_SLE_SG_V01")
{
// Campo correcto para propiedad BARRA_NO del mismo bloque dinámico
string fieldCode = @"%<\AcObjProp Object(%<\_BlkRef>%).BARRA_NO \f ""%s"">%";
var field = new Field
{
EvaluationOption = FieldEvaluationOptions.Automatic
};
field.SetFieldCode(fieldCode);
resultado.Add("000_000", field);
}
return resultado;
}