The IFailuresPreprocessor provides a way to suppress or handle some specific warnings or errors within a particular transaction.
It has a single core method PreprocessFailures() needing to be implemented which provides all necessary information about the failure(s) through the FailuresAccessor argument and returns a FailureProcessingResult for the transaction to take necessary actions accordingly.
There is nothing new about deriving an interface and implementing its methods, but in terms of the IFailuresPreprocessor interface it’s worth of a bit extra effort to carefully think about how to inspect the relevant failures (FailureDefinitionId collection), check against their severities (FailureSeverity), determine which transaction(s) to address, and what to do about the failure(s) at the end of the transaction(s).
In the following sample code, all of these will be wrapped into a single class. In addition, a static helper method will be provided for the caller to easily start up a transaction, attach the IFailuresPreprocessor instance to it (through the intermediate FailureHandlingOptions object, its SetFailuresPreprocessor method, and the SetFailureHandlingOptions method of the transaction), and do something real.
#Region "Namespaces"
Imports System.Text
Imports System.Linq
Imports System.Xml
Imports System.Reflection
Imports System.ComponentModel
Imports System.Collections
Imports System.Collections.Generic
Imports System.Windows
Imports System.Windows.Media.Imaging
Imports System.Windows.Forms
Imports System.IO
Imports Autodesk.Revit.ApplicationServices
Imports Autodesk.Revit.Attributes
Imports Autodesk.Revit.DB
Imports Autodesk.Revit.DB.Events
Imports Autodesk.Revit.DB.Architecture
Imports Autodesk.Revit.DB.Structure
Imports Autodesk.Revit.DB.Mechanical
Imports Autodesk.Revit.DB.Electrical
Imports Autodesk.Revit.DB.Plumbing
Imports Autodesk.Revit.UI
Imports Autodesk.Revit.UI.Selection
Imports Autodesk.Revit.UI.Events
Imports Autodesk.Revit.Collections
Imports Autodesk.Revit.Exceptions
Imports Autodesk.Revit.Utility
Imports RvtApplication = Autodesk.Revit.ApplicationServices.Application
Imports RvtDocument = Autodesk.Revit.DB.Document
#End Region
Public Class FailurePreprocessor1
Implements Autodesk.Revit.DB.IFailuresPreprocessor
Private ReadOnly Property FailureDefinitionIdList() As List(Of Autodesk.Revit.DB.FailureDefinitionId)
Get
Dim list As New List(Of Autodesk.Revit.DB.FailureDefinitionId)()
list.Add(BuiltInFailures.OverlapFailures.AreaBoundaryLinesOverlap)
list.Add(BuiltInFailures.OverlapFailures.CurvesOverlap)
list.Add(BuiltInFailures.OverlapFailures.DuplicateInstances)
list.Add(BuiltInFailures.OverlapFailures.DuplicatePoints)
list.Add(BuiltInFailures.OverlapFailures.DuplicateRebar)
list.Add(BuiltInFailures.OverlapFailures.LevelsOverlap)
list.Add(BuiltInFailures.OverlapFailures.RoomSeparationLinesOverlap)
list.Add(BuiltInFailures.OverlapFailures.SpaceSeparationLinesOverlap)
list.Add(BuiltInFailures.OverlapFailures.WallAreaBoundaryOverlap)
list.Add(BuiltInFailures.OverlapFailures.WallRoomSeparationOverlap)
list.Add(BuiltInFailures.OverlapFailures.WallsOverlap)
list.Add(BuiltInFailures.OverlapFailures.WallSpaceSeparationOverlap)
Return list
End Get
End Property
Private ReadOnly Property FailureSeverityList() As List(Of Autodesk.Revit.DB.FailureSeverity)
Get
Dim list As New List(Of Autodesk.Revit.DB.FailureSeverity)()
list.Add(Autodesk.Revit.DB.FailureSeverity.Warning)
list.Add(Autodesk.Revit.DB.FailureSeverity.Error)
Return list
End Get
End Property
Private ReadOnly Property TransNameFailureResultPairList() As List(Of KeyValuePair(Of String, Autodesk.Revit.DB.FailureProcessingResult))
Get
Dim list As New List(Of KeyValuePair(Of String, Autodesk.Revit.DB.FailureProcessingResult))()
list.Add(New KeyValuePair(Of String, Autodesk.Revit.DB.FailureProcessingResult)("TestTransaction1", Autodesk.Revit.DB.FailureProcessingResult.ProceedWithCommit))
list.Add(New KeyValuePair(Of String, Autodesk.Revit.DB.FailureProcessingResult)("TestTransaction2", Autodesk.Revit.DB.FailureProcessingResult.ProceedWithCommit))
Return list
End Get
End Property
' A delegate for the caller to define and pass back to the method
Public Delegate Sub ActionCallback(ByVal doc As Document)
' A helper method for the caller to call
Public Shared Sub PreprocessFailuresInTransaction(ByVal doc As Document, ByVal transName As String, ByVal action As ActionCallback)
Dim trans As New Autodesk.Revit.DB.Transaction(doc, transName)
Dim opts As Autodesk.Revit.DB.FailureHandlingOptions = trans.GetFailureHandlingOptions()
opts.SetFailuresPreprocessor(New FailurePreprocessor1())
trans.SetFailureHandlingOptions(opts)
trans.Start()
action(doc)
trans.Commit()
End Sub
#Region "IFailuresPreprocessor Members"
Public Function PreprocessFailures(ByVal failuresAccessor As Autodesk.Revit.DB.FailuresAccessor) As Autodesk.Revit.DB.FailureProcessingResult Implements Autodesk.Revit.DB.IFailuresPreprocessor.PreprocessFailures
Dim fpResult As Autodesk.Revit.DB.FailureProcessingResult = Autodesk.Revit.DB.FailureProcessingResult.Continue
Dim msgAccessorList As IList(Of Autodesk.Revit.DB.FailureMessageAccessor) = failuresAccessor.GetFailureMessages()
For Each msgAccessor As Autodesk.Revit.DB.FailureMessageAccessor In msgAccessorList
Dim tranName As String = failuresAccessor.GetTransactionName()
If Not TransNameFailureResultPairList.Exists(Function(e) e.Key.Equals(tranName)) Then
Continue For
End If
Dim kvp As KeyValuePair(Of String, Autodesk.Revit.DB.FailureProcessingResult) = TransNameFailureResultPairList.First(Function(e) e.Key.Equals(tranName))
If FailureDefinitionIdList.Exists(Function(e) e.Guid.ToString().Equals(msgAccessor.GetFailureDefinitionId().Guid.ToString())) AndAlso FailureSeverityList.Contains(msgAccessor.GetSeverity()) Then
failuresAccessor.DeleteWarning(msgAccessor)
fpResult = kvp.Value
End If
Next
Return fpResult
End Function
#End Region
End Class
As can be noticed, a few properties and methods have been created besides the interface method implementation to help make the work flow smoother.
The read-only property FailureDefinitionIdList defines a list of warnings or errors (indicated by some corresponding FailureDefinitionId of Revit API) that we’d like to handle in some transactions. In this example, we’d like to suppress all overlap warnings and errors such as overlap wall creation.
Another read-only property FailureSeverityList is to specify what kind of failures we concern about. Here, we will suppress not only overlap warnings but also errors.
The third read-only property TransNameFailureResultPairList tells what transactions the IFailurePreprocessor implementation will work with and what actions (Continue, ProceedWithCommit, ProceedWithRollBack or WaitForUserInput) the transactions will take when the specified failures happen.
The shared method PreprocessFailuresInTransaction() can be called in an external command to hook up the failure pre-processor with a particular document and transaction and a call-back method as the following:
…
FailurePreprocessor1.PreprocessFailuresInTransaction(CachedDoc, "TestTransaction1", AddressOf CallbackOfTestTransaction1)
…
The ActionCallback() is a simple and functional delegate for the transaction call-back method. It only accepts one argument, Revit Document, and returns nothing. Here is a sample implementation:
Private Sub CallbackOfTestTransaction1(ByVal doc As RvtDocument)
TaskDialog.Show("FailurePreprocessor Test By Spiderinnet", "Now we create two overlapped walls and there will be no warnings anymore!")
' Get the first level in the document.
Dim collector As New Autodesk.Revit.DB.FilteredElementCollector(doc)
Dim lvl = CType(collector.OfClass(GetType(Autodesk.Revit.DB.Level)).FirstElement(), Autodesk.Revit.DB.Level)
' Create two walls overlapping each other.
Dim p1 As New Autodesk.Revit.DB.XYZ(0, 0, 0)
Dim p2 As New Autodesk.Revit.DB.XYZ(10, 10, 0)
Dim l1 As Line = doc.Application.Create.NewLineBound(p1, p2)
doc.Create.NewWall(l1, lvl, False)
doc.Create.NewWall(l1, lvl, True)
End Sub
The good thing is there is no need to start and commit or roll back a transaction in the callback. All these have been taken care of in the helper method PreprocessFailuresInTransaction().
As commented in the code, if two overlapped walls are created in the call-back, no warnings will come up anymore since the WallsOverlap failure has been suppressed by the IFailuresPreprocessor implementation. Many times this may be just what is expected especially some temporary elements need to be created and be erased later.
With all the above handy, the implementation of the core PreprocessFailures() method will be pretty straightforward, checking whether the failure is of our concern, checking whether the transaction is of our concern, and if applicable suppressing the failure and returning the FailureProcessingResult as specified.
As can be noted, there are quite some details that need to be addressed regarding the IFailuresPreprocessor interface implementation regardless of programming languages such as VB.NET.
The good news is that all these can be taken care of automatically and quickly by the RevitAddinWizard.
Recent Comments