DocumentChanged事件只读时,如何设置元素参数(排除IUpdater)
可行实现思路(排除
IUpdater) 首先明确:你在DocumentChanged事件里直接启动事务修改元素会失败,因为这个事件触发时,文档正处于只读事务上下文(属于原变更事务的一部分),不允许启动新的修改事务。以下是两种无需IUpdater的可行方案:
方法一:利用Idling事件延迟执行修改
Idling是Revit空闲时触发的事件,此时文档处于可修改状态。我们可以在DocumentChanged中先收集需要修改的元素ID,等Idling触发时再批量处理。
代码示例
// 类级变量,暂存需要修改的元素ID(线程安全) private readonly HashSet<ElementId> _pendingElementIds = new HashSet<ElementId>(); public Result OnStartup(UIControlledApplication application) { try { application.ControlledApplication.DocumentChanged += Application_DocumentChanged; // 注册Idling事件 application.Idling += Application_Idling; } catch (Exception) { return Result.Failed; } return Result.Succeeded; } private void Application_DocumentChanged(object sender, DocumentChangedEventArgs e) { var addedIds = e.GetAddedElementIds(); if (addedIds.Any()) { lock (_pendingElementIds) { foreach (var id in addedIds) { _pendingElementIds.Add(id); } } } } private void Application_Idling(object sender, IdlingEventArgs e) { lock (_pendingElementIds) { if (_pendingElementIds.Count == 0) return; var uiApp = sender as UIApplication; var doc = uiApp.ActiveUIDocument?.Document; if (doc == null) return; using (var trans = new Transaction(doc, "Set TCP Parameters")) { try { trans.Start(); var paramGuid = new Guid("6558f207-e777-0758-2023-2f34e722cb01"); foreach (var elId in _pendingElementIds) { var elem = doc.GetElement(elId); var param = elem.get_Parameter(paramGuid); // 必须检查参数是否可修改,避免报错 if (param != null && !param.IsReadOnly) { param.Set(200); } } trans.Commit(); } catch (Exception ex) { TaskDialog.Show("Error", ex.Message); trans.RollBack(); } finally { _pendingElementIds.Clear(); } } } }
方法二:监听TransactionCompleted事件
TransactionCompleted事件在文档的事务提交完成后触发,此时文档已经脱离只读状态,可以安全启动新事务修改元素。
代码示例
public Result OnStartup(UIControlledApplication application) { try { // 监听文档打开事件,为每个文档绑定事务完成事件 application.ControlledApplication.DocumentOpened += Application_DocumentOpened; } catch (Exception) { return Result.Failed; } return Result.Succeeded; } private void Application_DocumentOpened(object sender, DocumentOpenedEventArgs e) { var doc = e.Document; // 为当前文档注册TransactionCompleted事件 doc.TransactionCompleted += Doc_TransactionCompleted; } private void Doc_TransactionCompleted(object sender, TransactionCompletedEventArgs e) { var doc = sender as Document; if (doc == null) return; // 获取当前事务新增的元素ID var addedIds = e.GetAddedElementIds(); if (!addedIds.Any()) return; using (var trans = new Transaction(doc, "Set TCP Parameters")) { try { trans.Start(); var paramGuid = new Guid("6558f207-e777-0758-2023-2f34e722cb01"); foreach (var elId in addedIds) { var elem = doc.GetElement(elId); var param = elem.get_Parameter(paramGuid); if (param != null && !param.IsReadOnly) { param.Set(200); } } trans.Commit(); } catch (Exception ex) { TaskDialog.Show("Error", ex.Message); trans.RollBack(); } } }
注意事项
- 无论哪种方法,都要检查参数的
IsReadOnly属性,避免修改系统或不可编辑参数导致报错。 - 多文档场景下,
Idling方法的集合需要加锁保证线程安全;TransactionCompleted绑定到单个文档,无需额外处理并发。 - 两种方案都能覆盖所有文档变更场景,完全不需要依赖
IUpdater。
内容的提问来源于stack exchange,提问作者Aishe
相关产品推荐
相关产品推荐

