如何在C#中无需ATTSYNC插入带属性的AutoCAD动态块
我用C#向AutoCAD程序化插入1000+个同类型动态块,每个块包含多个属性和动态特性(缩放、旋转、自定义值)。
不调用ATTSYNC时,插入效率很高——每秒约15个块。但插入后执行ATTSYNC会导致性能暴跌:总耗时增加7-10分钟,甚至会让AutoCAD冻结无响应。
我知道ATTSYNC是用来刷新属性保证显示正确的,但这已经成了严重瓶颈。想问下有没有办法在程序化插入动态块时避免用ATTSYNC?或者有没有更快更安全的方法让插入后的属性立即正确显示?求建议或变通方案。
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 { var bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead); if (!bt.Has(blkName)) { ed.WriteMessage($"\nEl bloque '{blkName}' no existe."); return null; } var btr = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); var lt = (LayerTable)trans.GetObject(db.LayerTableId, OpenMode.ForRead); int rows = propsValue.GetLength(0); int insertedCount = 0; string miEstado; var blkId = bt[blkName]; var propIndices = propsName .Select((name, index) => new { name, index }) .ToDictionary(p => p.name, p => p.index); 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"]; RevisionCapas(trans, lt, idxCAPA, propsValue); string[] dynPropNames = propsName.Skip(11).ToArray(); for (int i = 0; i < rows; i++) { var dibujarVal = propsValue[i, idxDibujar]?.ToString(); if (!string.Equals(dibujarVal, "SI", StringComparison.OrdinalIgnoreCase) && !string.Equals(dibujarVal, "TRUE", StringComparison.OrdinalIgnoreCase)) { continue; } 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"; var blkRef = new BlockReference(new Point3d(cx, cy, cz), blkId) { Rotation = ang, ScaleFactors = new Scale3d(escX, escY, escZ), Layer = capa }; btr.AppendEntity(blkRef); trans.AddNewlyCreatedDBObject(blkRef, true); propsValue[i, idxHandler] = blkRef.Handle.ToString(); 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}"); } } } } insertedCount++; actualizador.ActualizarProgresoProceso(); } trans.Commit(); miEstado = "Toca sincronizar el bloque y eso demora, pa'."; actualizador.ActualizarEstado(miEstado); ed.Command("._ATTSYNC", "_N", blkName); miEstado = "Mucho atributo, llave. Mucho hijueputa atributo."; actualizador.ActualizarEstado(miEstado); return propsValue; } catch (System.Exception ex) { ed.WriteMessage($"\nError: {ex.Message}"); } return null; } }
单个块插入后即时同步属性,替代批量ATTSYNC
不要等所有块插完再跑批量ATTSYNC,而是在每个块插入并设置完动态属性后,直接调用blkRef.SyncAttributes()方法(AutoCAD .NET API原生支持)。把大的同步操作拆成每个块的小操作,分散计算压力,避免一次性处理上千个块导致的卡顿。优化动态属性查找逻辑,减少重复计算
当前代码每次插入块都要遍历DynamicBlockReferencePropertyCollection查找属性,效率很低。可以提前在循环外获取块定义的属性列表:// 循环前缓存块定义的动态属性 var blkDef = (BlockTableRecord)trans.GetObject(blkId, OpenMode.ForRead); var cachedDynProps = blkDef.DynamicBlockReferencePropertyCollection .Cast<DynamicBlockReferenceProperty>() .ToDictionary(p => p.PropertyName, p => p);插入块时直接用这个缓存字典匹配属性,省掉每次的Cast和FirstOrDefault操作。
用API原生方法替代命令行调用ATTSYNC
别用ed.Command("._ATTSYNC")这种外部命令,改用db.AttSync(blkId, AttSyncFlags.SyncAll)(如果使用的AutoCAD版本支持),原生API比命令行交互高效得多,也不会因为命令行的上下文问题导致冻结。插入过程中关闭实时图形刷新
在插入块前调用ed.DisableGraphics()关闭AutoCAD界面刷新,所有块插入完成后再调用ed.EnableGraphics()开启。这样可以避免插入过程中频繁的界面重绘消耗性能,开启刷新后属性会自动正确显示,可能完全不需要ATTSYNC。检查动态块定义的合理性
如果动态块本身有复杂的参数联动、冗余的属性定义,会导致ATTSYNC时计算量暴增。可以简化块的动态逻辑,比如把不必要的参数联动去掉,或者拆分复杂块为多个简单块,降低同步时的负载。确保属性赋值时类型匹配,避免后续同步
当前代码用Convert.ChangeType转换值,可能存在类型不匹配的情况,导致属性显示异常需要同步。可以提前缓存每个属性的类型,赋值时直接转换为对应类型,确保属性值从一开始就是正确的,减少同步的必要性。
内容的提问来源于stack exchange,提问作者Julián Felipe Herrera Mejía

