前言
UI讲完,回到DB
这块儿。在Document那篇,提到增删改查操作都是在Document上,是对Documet进行操作。
看到“增删改查”这四个,想到什么了没有?
数据库(DB)嘛~话说那本经典的红皮数据库的书叫啥来着?算了算了,数据库也忘得差不多了😶。
回到Revit API,“删”我们在Document那篇就讲过了,用Delete方法就行了。“查”就是过滤器,上篇的选择器也能沾一点儿边。还有“增”和“改”这俩个。
数据库中的“增”,是增加新的数据,对应Revit就是增加新模型、创建新实例,我们将会在涉及到Creation
时讲。
数据库中的“改”,是对原有数据进行修改。在Revit中,就比较宽泛了,或许应该称之为“变化”,变化了就是改了。这块儿,体现在事件上,后面专门些一篇关于Events
的,再提改这部分。
回到本篇。
在数据库中,有事务的概念,Revit 的这个功能上差不多,但没有那么细致。
这篇,涉及的主要类如下图
一、Transaction(事务)
其实在第一篇Namespace与Attributes中,就提到事务,只是那会儿没有展开。
[Transaction(TransactionMode.Manual)] // 开启事务
现在我们开始讲事务。
Revit的事务是做什么的,当我们要进行的操作会改变Document时,就必须在事务中进行,比如:删除元素,调整元素参数,设置元素显影。
Revit中,事务类有3个,分别是:
Transaction
:事务TransactionGroup
:事务组,可以在事务组里创建新的事务SubTransaction
:子事务,必须在事务中,子事务可嵌套
1.1. 三种事务的对比
来看看方法对比
Transaction | TransactionGroup | SubTransaction |
---|---|---|
Start(..) x2 | Start(..) x2 | Start |
Commit(..) x2 | Assimilate Commite | Commite |
RollBack(..) x2 | Rollback | Rollback |
Dispose | Dispose | Dispose |
Get/SetName | Get/SetName | – |
GetStatus HasStarted HasEnded | GetStatus HasStarted HasEnded | GetStatus HasStarted HasEnded |
GetFailureHandlingOptions SetFailureHandlingOptions | – | – |
差异:事务名称,故障处理 | 差异:事务组名称,打包提交 | |
瞧,就这么丢丢的差异。
事务:可以在事务上添加故障处理程序。
事务组:可以将多个事务组织成一个事务提交(Assimilate
),也可一次性提交(Commite
)。
子事务:只能在事务中开启,本身可嵌套。没自己的名称。
1.2. 事务组的2种提交方式
事务组两种提交方式的区别。
对于下面的代码,将事务改为子事务,事务组改为事务,也是可以的。
public void TestTransactionGroup(UIDocument uIDoc)
{View view = uIDoc.ActiveView;List<ElementId> wallIds = this.GetElementByCategory(uIDoc.Document, BuiltInCategory.OST_Walls);wallIds = wallIds.Take(5).ToList(); // 取5个// 事务组using (TransactionGroup transactionGroup = new TransactionGroup(uIDoc.Document, "TransactionGroup-隐藏-所有墙体")){transactionGroup.Start();foreach (ElementId wallId in wallIds){// 事务using (Transaction transaction = new Transaction(uIDoc.Document, $"Transaction-隐藏-{wallId.IntegerValue}")){transaction.Start();view.HideElements([wallId]);transaction.Commit();}}//transactionGroup.Assimilate(); // 将5个事务打包成一个,提交transactionGroup.Commit(); // 5个事务一次性提交,还是5个}
}
1.3. 子事务
上面说了,子事务必须要在事务中才能创建,不然就会报错。
子事务一般在什么时候使用呢?一般是作为独立的小的操作步骤,插入到有具体业务的事务的逻辑中。
但有时我们需要进行一些操作了,却不能确定当前是否在事务中呢?
这个问题是有意义的,因为事务是不可嵌套的,我们无法在一个事务中开启另一个事务,又无法在非事务中创建子事务,所以需要根据当前的状态,来选择性处理。
还记Document的IsModified
属性吗,True
表示文档正处于修改中,也就是已开启了事务。
使用
document.IsModified
来判断当前事务环境。
根据环境不同,来决定是创建 事务 还是 子事务
二、Failures(故障处理)
事务执行失败了怎么办?我们可以回滚Rollback
。
不想回滚呢?那就try-catch处理问题吧。
但这里的说的故障处理可不是程序上的错误,而是Revit发出的问题。
Revit里有许多的约束,当文档变化时,就会进行校验,判断是否允许更改,常见的问题有“不满足约束”“无法剪切图元”等,在遇到这些问题时,Revit会有弹框,并暂定程序的执行,待用户选择操作后才会继续处理。
这种由Revit发出的警告或错误,是无法通过try-catch处理的,得提供专门的故障处理方式,即实现IFailuresPreprocessor
接口,并将其提供给事务。
2.1. IFailuresPreprocessor
IFailuresPreprocessor
需要实现一个方法,也只有这个方法。
public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
方法有个参数FailuresAccessor
,文档中是这么介绍的:“是获取文档故障信息的唯一可以接口,虽然可以在故障处理期间读取文档,但在处理期间修改文档的唯一方法是通过此类提供的方法”。
但在我的测试中,拿到Document进行一些操作,没有效果也不报错,不明白🙃。
甚至只要我在故障处理方法力稍稍做些事儿,错误弹框就无法被跳过,还是会弹出来。
类成员,就不列了。
2.2. 取消Revit 警告/错误 弹框
注意,错误不应该直接ResolveFailure
,Revit可能会采用删除的方式处理。
internal class MyFailuresPreprocessor : IFailuresPreprocessor
{public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor){IList<FailureMessageAccessor> failures = failuresAccessor.GetFailureMessages();foreach (FailureMessageAccessor failure in failures){//ICollection<ElementId> additionalElements = failure.GetAdditionalElementIds(); // 获取与问题相关的其他元素//ICollection<ElementId> failingElementIds = failure.GetFailingElementIds(); // 获取引起问题的元素FailureSeverity failureSeverity = failure.GetSeverity(); // 获取失败的严重程度if (failureSeverity == FailureSeverity.Warning){failuresAccessor.DeleteWarning(failure); // 删除警告}else if (failureSeverity == FailureSeverity.DocumentCorruption){return FailureProcessingResult.ProceedWithRollBack; // 文档损坏,回滚}else if (failureSeverity == FailureSeverity.Error){//FailureDefinitionId failureId = failure.GetFailureDefinitionId(); // 获取失败的定义ID//if (failureId == BuiltInFailures.CutFailures.CannotCutInstanceOut) // 如果问题是无法剪切实例//{// failure.SetCurrentResolutionType(FailureResolutionType.MoveElements); // 尝试采用移动物体的方式处理//}failuresAccessor.ResolveFailure(failure); // 解决错误}}return FailureProcessingResult.ProceedWithCommit; // 提交 // 可能会删除部分模型//return FailureProcessingResult.Continue;}
}
上面的代码,会不显示所有的警告弹框。对于下面的错误弹框,也会消失。
当然,这不意味着就可以剪切了,具体的效果就是取消了剪切。
三、通过代码进行历史操作回退
在Revit左上角快捷栏里,有事务历史,我们当然可以通过交互的方式快速回退到之前的文档。其实这个操作也是可以通过代码进行的。
我们需要引入库UIFrameworkServices.dll
,其中有类QuickAccessToolBarService
。
// 找到历史操作记录
public static ObservableCollection<string> collectUndoRedoItems(bool bForUndo);// 回退或前进,步数
public static void performMultipleUndoRedoOperations(bool bForUndo, int iNumOperations)
总结
写到这儿,一看最上方的导图,好像还有个事件,算了,不写了🙄。