明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
12
返回列表 发新帖
楼主: wang2006zhi

[事件] 图元修改事件的封装-关联修改

[复制链接]
发表于 2025-11-6 12:04:38 来自手机 | 显示全部楼层
tranque 发表于 2025-11-6 09:49
最后登录时间 1970-1-1 ennnnnnnnnnnnnnn

看看我的也是哦(_)
回复 支持 反对

使用道具 举报

发表于 2025-11-6 12:43:12 | 显示全部楼层
Bao_lai 发表于 2025-11-6 12:04
看看我的也是哦(_)

有点酷,怎么做到的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-11-12 09:04:48 | 显示全部楼层
你有种再说一遍 发表于 2025-11-6 01:43
作者怎么被封啦???也没有看他刷帖啊

贴了别人的代码回复
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-11-12 09:14:36 | 显示全部楼层
  1. namespace HTJ.Arx;

  2. /// <summary>
  3. /// 关联动作体封装
  4. /// BY2025.11;wang2006zhi
  5. /// </summary>
  6. /// <remarks>
  7. /// 使用示例:
  8. /// <code>
  9. /// var logic = new Dictionary{
  10. ///     [textId] = trans => { /* 文字更新逻辑 */ },
  11. ///     [circleId] = trans => { /* 圆更新逻辑 */ }
  12. /// };
  13. /// AssocActionEx.Create(logic);
  14. /// </code>
  15. /// </remarks>
  16. public class AssocActionEx : AssocActionBody
  17. {
  18.     #region 私有字段

  19.     private ObjectId _assocActionId = ObjectId.Null;
  20.     private readonly List<ObjectId> _sourceObjectIds = [];
  21.     private readonly Dictionary<ObjectId, Action<AssocObjectTransaction>> _objectUpdateActions = [];
  22.     private readonly List<Action<AssocObjectTransaction>> _globalUpdateActions = [];
  23.     private Func<AssocActionEx>? _cloneFactory;
  24.     private bool _cloneFlag;

  25.     #endregion

  26.     #region 公共接口

  27.     /// <summary>
  28.     /// 创建关联动作
  29.     /// </summary>
  30.     /// <param name="objectSpecificLogic">
  31.     /// 对象特定逻辑字典,Key为对象ID(自动作为依赖对象),Value为对应的更新逻辑
  32.     /// </param>
  33.     /// <param name="cloneFactory">深克隆时的创建工厂,用于对象复制时创建新的关联动作实例</param>
  34.     /// <returns>创建的关联动作实例</returns>
  35.     /// <exception cref="ArgumentNullException">当逻辑字典为空时抛出</exception>
  36.     /// <exception cref="ArgumentException">当逻辑字典为空时抛出</exception>
  37.     public static AssocActionEx Create(Dictionary<ObjectId, Action<AssocObjectTransaction>> objectSpecificLogic,
  38.         Func<AssocActionEx>? cloneFactory = null)
  39.     {
  40.         if (objectSpecificLogic == null) throw new ArgumentNullException(nameof(objectSpecificLogic));
  41.         if (objectSpecificLogic.Count == 0) throw new ArgumentException(@"对象特定逻辑字典不能为空", nameof(objectSpecificLogic));

  42.         var action = new AssocActionEx();

  43.         if (cloneFactory != null) action._cloneFactory = cloneFactory;

  44.         // 直接从字典的键获取依赖对象,并设置更新逻辑
  45.         action.SetupDependenciesAndLogic(objectSpecificLogic);

  46.         return action;
  47.     }

  48.     /// <summary>
  49.     /// 添加全局更新逻辑(对所有依赖对象生效)
  50.     /// </summary>
  51.     /// <param name="logic">全局更新逻辑,会在每次评估时执行</param>
  52.     /// <returns>当前实例以支持链式调用</returns>
  53.     /// <exception cref="ArgumentNullException">当逻辑为空时抛出</exception>
  54.     public AssocActionEx AddGlobalLogic(Action<AssocObjectTransaction> logic)
  55.     {
  56.         _globalUpdateActions.Add(logic ?? throw new ArgumentNullException(nameof(logic)));
  57.         return this;
  58.     }

  59.     /// <summary>
  60.     /// 设置深克隆工厂函数
  61.     /// </summary>
  62.     /// <param name="factory">创建新实例的工厂函数,在对象被复制时调用</param>
  63.     /// <returns>当前实例以支持链式调用</returns>
  64.     /// <exception cref="ArgumentNullException">当工厂函数为空时抛出</exception>
  65.     public AssocActionEx SetCloneFactory(Func<AssocActionEx> factory)
  66.     {
  67.         _cloneFactory = factory ?? throw new ArgumentNullException(nameof(factory));
  68.         return this;
  69.     }

  70.     #endregion

  71.     #region 内部实现

  72.     /// <summary>
  73.     /// 设置依赖关系并创建关联动作
  74.     /// </summary>
  75.     /// <param name="objectSpecificLogic">对象特定逻辑字典</param>
  76.     private void SetupDependenciesAndLogic(Dictionary<ObjectId, Action<AssocObjectTransaction>> objectSpecificLogic)
  77.     {
  78.         var ids = objectSpecificLogic.Keys.ToList();
  79.         // 设置依赖关系
  80.         SetupDependencies(ids);
  81.         // 设置对象特定逻辑
  82.         foreach (var kv in objectSpecificLogic) _objectUpdateActions[kv.Key] = kv.Value;
  83.     }

  84.     /// <summary>
  85.     /// 获取需要处理的对象列表
  86.     /// </summary>
  87.     /// <param name="transaction">关联事务</param>
  88.     /// <returns>需要更新的对象ID列表</returns>
  89.     private List<ObjectId> GetPendingObjects(AssocObjectTransaction transaction)
  90.     {
  91.         var objectIdCollection = GetDependencies(true, true);
  92.         var pendingObjectIds = new List<ObjectId>(objectIdCollection.Count);

  93.         // 遍历所有依赖项,检查哪些需要更新
  94.         foreach (ObjectId dependencyId in objectIdCollection)
  95.         {
  96.             using var dependency =
  97.                 (AssocDependency)transaction.GetDBObject(dependencyId, OpenMode.ForRead, false, true);

  98.             // 如果依赖项状态不是"已更新",则添加到待处理列表
  99.             if (dependency.Status != AssocStatus.IsUpToDateAssocStatus)
  100.             {
  101.                 pendingObjectIds.Add(dependency.DependentOnObject);
  102.             }
  103.         }

  104.         return pendingObjectIds;
  105.     }

  106.     /// <summary>
  107.     /// 获取或创建关联动作对象
  108.     /// </summary>
  109.     /// <param name="owningObjectId">所属对象ID</param>
  110.     /// <param name="tr">数据库事务</param>
  111.     /// <returns>关联动作对象</returns>
  112.     private AssocAction GetOrCreateAssocAction(ObjectId owningObjectId, DBTrans tr)
  113.     {
  114.         // 如果已经存在关联动作,直接返回
  115.         if (!_assocActionId.IsNull) return tr.GetObject<AssocAction>(_assocActionId, OpenMode.ForWrite)!;

  116.         var database = tr.Database;

  117.         // 获取或创建关联网络
  118.         var networkObjectId = AssocNetwork.GetInstanceFromObject(owningObjectId, true, true, "");
  119.         var network = tr.GetObject<AssocNetwork>(networkObjectId, OpenMode.ForWrite)!;

  120.         // 创建关联动作
  121.         var assocAction = new AssocAction();
  122.         var actionBodyObjectId = database.AddDBObject(this);

  123.         // 注册新创建的对象到事务中
  124.         tr.Transaction.AddNewlyCreatedDBObject(this, true);
  125.         _assocActionId = database.AddDBObject(assocAction);
  126.         assocAction.ActionBody = actionBodyObjectId;
  127.         network.AddAction(_assocActionId, true);

  128.         return assocAction;
  129.     }

  130.     /// <summary>
  131.     /// 评估逻辑 - 执行所有注册的更新动作
  132.     /// </summary>
  133.     /// <remarks>
  134.     /// 这是关联动作的核心方法,当依赖对象发生变化时自动调用
  135.     /// </remarks>
  136.     public override void EvaluateOverride()
  137.     {
  138.         try
  139.         {
  140.             var assocEvaluationCallback = currentEvaluationCallback();

  141.             // 检查评估模式是否为"修改对象"模式
  142.             if (assocEvaluationCallback.EvaluationMode() != AssocEvaluationMode.ModifyObjectsAssocEvaluationMode)
  143.             {
  144.                 Status = AssocStatus.FailedToEvaluateAssocStatus;
  145.                 return;
  146.             }

  147.             // 检查是否存在被删除或损坏的依赖项
  148.             if (HasAnyErasedOrBrokenDependencies())
  149.             {
  150.                 Status = AssocStatus.ErasedAssocStatus;
  151.                 return;
  152.             }

  153.             using var transaction = new AssocObjectTransaction(this);

  154.             // 获取需要更新的对象
  155.             var pendingObjects = GetPendingObjects(transaction);
  156.             if (pendingObjects.Count == 0)
  157.             {
  158.                 Status = AssocStatus.IsUpToDateAssocStatus;
  159.                 return;
  160.             }

  161.             // 评估依赖项状态
  162.             EvaluateDependencies();

  163.             // 执行对象特定的更新逻辑
  164.             ExecuteObjectSpecificLogic(pendingObjects, transaction);

  165.             // 执行全局更新逻辑
  166.             ExecuteGlobalLogic(transaction);

  167.             // 标记状态为已更新
  168.             Status = AssocStatus.IsUpToDateAssocStatus;
  169.         }
  170.         catch (Exception ex)
  171.         {
  172.             // 记录异常并标记为评估失败
  173.             $"关联动作评估失败: {ex.Message}".Print();
  174.             Status = AssocStatus.FailedToEvaluateAssocStatus;
  175.         }
  176.     }

  177.     /// <summary>
  178.     /// 执行对象特定的更新逻辑
  179.     /// </summary>
  180.     /// <param name="pendingObjects">待处理对象列表</param>
  181.     /// <param name="transaction">关联事务</param>
  182.     private void ExecuteObjectSpecificLogic(List<ObjectId> pendingObjects, AssocObjectTransaction transaction)
  183.     {
  184.         foreach (var objectId in pendingObjects)
  185.         {
  186.             if (!_objectUpdateActions.TryGetValue(objectId, out var objectLogic)) continue;
  187.             try
  188.             {
  189.                 objectLogic(transaction);
  190.             }
  191.             catch (Exception ex)
  192.             {
  193.                $"对象特定逻辑执行失败 {objectId}: {ex.Message}".Print();;
  194.             }
  195.         }
  196.     }

  197.     /// <summary>
  198.     /// 执行全局更新逻辑
  199.     /// </summary>
  200.     /// <param name="transaction">关联事务</param>
  201.     private void ExecuteGlobalLogic(AssocObjectTransaction transaction)
  202.     {
  203.         foreach (var globalLogic in _globalUpdateActions)
  204.         {
  205.             try
  206.             {
  207.                 globalLogic(transaction);
  208.             }
  209.             catch (Exception ex)
  210.             {
  211.                 $"全局逻辑执行失败: {ex.Message}".Print();
  212.             }
  213.         }
  214.     }

  215.     /// <summary>
  216.     /// 深克隆处理 - 当关联对象被复制时调用
  217.     /// </summary>
  218.     /// <param name="idMap">对象ID映射关系</param>
  219.     /// <param name="additionalObjectsToClone">额外需要克隆的对象集合</param>
  220.     /// <remarks>
  221.     /// 此方法确保当依赖对象被复制时,关联动作也会被正确复制
  222.     /// </remarks>
  223.     public override void AddMoreObjectsToDeepCloneOverride(IdMapping idMap, ObjectIdCollection additionalObjectsToClone)
  224.     {
  225.         if (_cloneFlag) return;
  226.         _cloneFlag = true;
  227.         try
  228.         {
  229.             using var tr = new DBTrans();
  230.             // 收集已克隆的对象ID
  231.             var (clonedIds, originalIds) = CollectClonedIds(idMap);
  232.             // 克隆缺失的依赖对象
  233.             CloneMissingDependencies(clonedIds, originalIds, tr);
  234.             // 创建新的关联动作实例并复制逻辑
  235.             CreateClonedActionBody(clonedIds, idMap);
  236.         }
  237.         finally
  238.         {
  239.             _cloneFlag = false;
  240.         }
  241.     }

  242.     /// <summary>
  243.     /// 收集已克隆的对象ID
  244.     /// </summary>
  245.     /// <param name="idMap">ID映射表</param>
  246.     /// <returns>克隆后的ID列表和原始ID列表</returns>
  247.     private (List<ObjectId> clonedIds, List<ObjectId> originalIds) CollectClonedIds(IdMapping idMap)
  248.     {
  249.         var length = idMap.GetValues().Count;
  250.         var clonedIds = new List<ObjectId>(length);
  251.         var originalIds = new List<ObjectId>(length);

  252.         foreach (IdPair idPair in idMap)
  253.         {
  254.             clonedIds.Add(idPair.Value);
  255.             originalIds.Add(idPair.Key);
  256.         }

  257.         return (clonedIds, originalIds);
  258.     }

  259.     /// <summary>
  260.     /// 克隆缺失的依赖对象
  261.     /// </summary>
  262.     /// <param name="clonedIds">已克隆的ID列表</param>
  263.     /// <param name="originalIds">原始ID列表</param>
  264.     /// <param name="tr">数据库事务</param>
  265.     private void CloneMissingDependencies(List<ObjectId> clonedIds, List<ObjectId> originalIds, DBTrans tr)
  266.     {
  267.         // 检查是否有缺失的依赖对象需要克隆
  268.         if (clonedIds.Count >= _sourceObjectIds.Count) return;

  269.         var missingIds = _sourceObjectIds.Except(originalIds).ToArray();
  270.         if (missingIds.Length == 0) return;

  271.         // 执行深克隆操作
  272.         var objectIdCollection = new ObjectIdCollection(missingIds);
  273.         using var idMapping = new IdMapping();

  274.         tr.Database.DeepCloneObjects(objectIdCollection, tr.CurrentSpace.ObjectId, idMapping, false);

  275.         // 添加新克隆的对象ID到列表中
  276.         foreach (IdPair idPair in idMapping)
  277.         {
  278.             clonedIds.Add(idPair.Value);
  279.         }
  280.     }

  281.     private void CreateClonedActionBody(List<ObjectId> clonedIds, IdMapping idMap)
  282.     {
  283.         // 创建新的动作体实例
  284.         var newBody = _cloneFactory?.Invoke() ?? new AssocActionEx();

  285.         // 设置依赖关系(使用克隆后的ID列表)
  286.         newBody.SetupDependencies(clonedIds);

  287.         // 复制对象特定逻辑(映射到克隆后的对象ID)
  288.         foreach (var kv in _objectUpdateActions)
  289.         {
  290.             var originalId = kv.Key;
  291.             var logic = kv.Value;
  292.             var clonedId = FindClonedId(originalId, idMap);
  293.             if (clonedId.IsValid) newBody._objectUpdateActions[clonedId] = logic;
  294.         }

  295.         // 复制全局逻辑
  296.         newBody._globalUpdateActions.AddRange(_globalUpdateActions);
  297.     }

  298.     /// <summary>
  299.     /// 设置依赖关系
  300.     /// </summary>
  301.     /// <param name="ids">依赖对象ID列表</param>
  302.     private void SetupDependencies(IList<ObjectId> ids)
  303.     {
  304.         if (ids.Count == 0) return;

  305.         using var tr = new DBTrans();
  306.         var first = ids[0];
  307.         var database = first.Database;

  308.         // 验证数据库状态
  309.         if (database == null)
  310.         {
  311.             "数据库不可用".Print();
  312.             return;
  313.         }
  314.         // 获取或创建关联动作
  315.         var assocAction = GetOrCreateAssocAction(first, tr);

  316.         // 在事务中创建依赖关系
  317.         using var transaction = database.TransactionManager.StartTransaction();

  318.         for (int i = 0; i < ids.Count; i++)
  319.         {
  320.             var entityId = ids;
  321.             if (!IsObjectSuitableForAssociation(entityId, tr))
  322.             {
  323.                 $"对象 {entityId} 不适合作为关联依赖,已跳过".Print();
  324.                 continue;
  325.             }
  326.             _sourceObjectIds.Add(entityId);

  327.             // 创建依赖项
  328.             var dependency = new AssocDependency();
  329.             var dependencyId = database.AddDBObject(dependency);

  330.             // 配置依赖项属性
  331.             dependency.AttachToObject(new CompoundObjectId(entityId));
  332.             dependency.IsReadDependency = true;
  333.             dependency.IsWriteDependency = true;
  334.             dependency.Order = i;

  335.             // 将依赖项添加到关联动作
  336.             assocAction.AddDependency(dependencyId, true);
  337.         }

  338.         transaction.Commit();
  339.     }

  340.     /// <summary>
  341.     /// 检查对象是否适合作为关联依赖
  342.     /// </summary>
  343.     private bool IsObjectSuitableForAssociation(ObjectId objectId, DBTrans tr)
  344.     {
  345.         try
  346.         {
  347.             if (objectId.IsNull || !objectId.IsValid)
  348.                 return false;

  349.             using var obj = tr.Transaction.GetObject(objectId, OpenMode.ForRead, false, true);
  350.         
  351.             // 检查对象类型
  352.             if (obj is BlockReference || obj is BlockTableRecord)
  353.             {
  354.                 "块引用和块表记录不适合作为关联依赖".Print();
  355.                 return false;
  356.             }

  357.             // 检查对象是否已被擦除
  358.             if (obj.IsErased)
  359.             {
  360.                 "对象已被擦除".Print();
  361.                 return false;
  362.             }

  363.             return true;
  364.         }
  365.         catch
  366.         {
  367.             return false;
  368.         }
  369.     }
  370.     /// <summary>
  371.     /// 查找克隆后的对象ID
  372.     /// </summary>
  373.     /// <param name="originalId">原始对象ID</param>
  374.     /// <param name="idMap">ID映射表</param>
  375.     /// <returns>克隆后的对象ID,如果找不到则返回ObjectId.Null</returns>
  376.     private static ObjectId FindClonedId(ObjectId originalId, IdMapping idMap)
  377.     {
  378.         foreach (IdPair idPair in idMap)
  379.         {
  380.             if (idPair.Key == originalId) return idPair.Value;
  381.         }

  382.         return ObjectId.Null;
  383.     }

  384.     #endregion
  385. }
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-11-12 09:16:18 | 显示全部楼层
   [CommandMethod("tt8")]
    public void Tt8()
    {
        if (!Env.Editor.SelId(out ObjectId circleId, RxClassEx.Circle))
            return;
        if (!Env.Editor.SelId(out ObjectId textId, RxClassEx.DbText))
            return;
        //定义联动逻辑
        var objectLogic = new Dictionary<ObjectId, Action<AssocObjectTransaction>>
        {
            [circleId] = aTransaction =>
            {
                using var circle = aTransaction.GetDBObject(circleId, OpenMode.ForWrite, false, true) as Circle;
                using var text = aTransaction.GetDBObject(textId, OpenMode.ForWrite, false, true) as DBText;
                if (text is null)
                    return;
                if (circle is null)
                    return;
                text.TextString=$"{circle.Radius:F1}";
                text.ColorIndex = 2;
                text.Height = circle.Radius;
      
            },
            [textId] = aTransaction =>
            {
                using var text = aTransaction.GetDBObject(textId, OpenMode.ForWrite, false, true) as DBText;
                using var circle = aTransaction.GetDBObject(circleId, OpenMode.ForWrite, false, true) as Circle;
        
                if (text is null)
                    return;
                if (circle is null)
                    return;
                if (double.TryParse(text.TextString, out double radius))
                    circle.Radius = radius;
            }
        };
        //创建联动关联
        AssocActionEx.Create(objectLogic);

    }
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2025-11-30 11:34 , Processed in 0.160578 second(s), 18 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表