You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:19:54