The IFailuresProcessor provides a way to define custom failures and handle them within some particular transactions.
It has two methods needing to be implemented, Dismiss() and ProcessFailures(). The latter is the core and provides all necessary information about the failure(s) through the FailuresAccessor argument and returns a FailureProcessingResult for the transaction to act accordingly.
The method behaves much the same way as the one of the IFailuresPreprocessor interface. The difference is that instead of removing the involved Revit native failure it generally resolves the custom failure by calling the ResolveFailure method.
Some additional work comparing to the implementation of the IFailuresPreprocessor interface includes creating a custom FailureDefinitionId, defining a custom FailureDefinition through the method CreateFailureDefinition, and setting up the resolution type through the methods AddResolutionType and SetDefaultResolutionType.
Imports System.Text
Imports System.Xml
Imports System.Linq
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.DB.Analysis
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
Public Class FailureProcessor1
Implements Autodesk.Revit.DB.IFailuresProcessor
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)("TransactionTest1", Autodesk.Revit.DB.FailureProcessingResult.ProceedWithCommit))
list.Add(New KeyValuePair(Of String, Autodesk.Revit.DB.FailureProcessingResult)("TransactionTest2", Autodesk.Revit.DB.FailureProcessingResult.ProceedWithCommit))
Return list
End Get
End Property
Public Shared FailureProcessor1_FailureDefinition As New Autodesk.Revit.DB.FailureDefinitionId(New Guid("a806b634-e1f5-41e5-9b69-2280836d552e"))
Public Shared ReadOnly Property FailureDefinitionOfFailureProcessor1_FailureDefinition() As Autodesk.Revit.DB.FailureDefinition
Get
Dim failDef As Autodesk.Revit.DB.FailureDefinition = Autodesk.Revit.DB.FailureDefinition.CreateFailureDefinition(FailureProcessor1_FailureDefinition, Autodesk.Revit.DB.FailureSeverity.Warning, "FailureProcessor1_FailureDefinition")
failDef.AddResolutionType(Autodesk.Revit.DB.FailureResolutionType.DeleteElements, "DeleteElements", GetType(Autodesk.Revit.DB.DeleteElements))
failDef.SetDefaultResolutionType(Autodesk.Revit.DB.FailureResolutionType.DeleteElements)
Return failDef
End Get
End Property
Public Shared Sub Register()
Dim processor As New FailureProcessor1()
RvtApplication.RegisterFailuresProcessor(processor)
End Sub
' A delegate for the caller to define and pass back to the method
Public Delegate Function ElementHandling(ByVal doc As Document, ByRef ids As List(Of Autodesk.Revit.DB.ElementId)) As Boolean
' A helper method for the caller to call
Public Shared Sub ProcessFailuresInTransaction(ByVal doc As Document, ByVal transName As String, ByVal action As ElementHandling)
Dim trans As New Autodesk.Revit.DB.Transaction(doc, transName)
trans.Start()
Dim ids As New List(Of Autodesk.Revit.DB.ElementId)()
Dim success As Boolean = action(doc, ids)
If Not success Then
Dim failMsg As New Autodesk.Revit.DB.FailureMessage(FailureProcessor1_FailureDefinition)
Dim failRsl As Autodesk.Revit.DB.FailureResolution = Autodesk.Revit.DB.DeleteElements.Create(doc, ids)
failMsg.AddResolution(Autodesk.Revit.DB.FailureResolutionType.DeleteElements, failRsl)
doc.PostFailure(failMsg)
End If
trans.Commit()
End Sub
#Region "IFailuresProcessor Members"
Public Sub Dismiss(ByVal document As Document) Implements Autodesk.Revit.DB.IFailuresProcessor.Dismiss
End Sub
Public Function ProcessFailures(ByVal failuresAccessor As Autodesk.Revit.DB.FailuresAccessor) As Autodesk.Revit.DB.FailureProcessingResult Implements Autodesk.Revit.DB.IFailuresProcessor.ProcessFailures
Dim fpResult As Autodesk.Revit.DB.FailureProcessingResult = Autodesk.Revit.DB.FailureProcessingResult.ProceedWithCommit
'NOTE: Change the default result if necessary.
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 msgAccessor.GetFailureDefinitionId().Guid.ToString().Equals(FailureProcessor1_FailureDefinition.Guid.ToString()) Then
failuresAccessor.ResolveFailure(msgAccessor)
'NOTE: Can be changed to any other resolution actions.
fpResult = kvp.Value
End If
Next
Return fpResult
End Function
#End Region
End Class
The custom failure definition has been given a name and a GUID. The FailureDefinitionBadElement property creates the custom FailureDefinition, specifies the FailureSeverity and description, adds resolution type (FailureResolutionType), and sets the default resolution type.
The Register() help method can be used to register the IFailuresProcessor somewhere when necessary.
The TransNameFailureResultPairList defines a list of pair of transaction name and failure processing result (FailureProcessingResult).
The FailureSeverityList property specifies what kind of FailureSeverity to address in the IFailuresProcessor implementation.
The PreprocessFailuresInTransaction help method is supposed to be used to hook up the IFailuresProcessor implementation with a Revit document, a transaction, and an action callback through the ElementHandling delegate.
The ProcessFailures method is the core method of the IFailuresProcessor interface. All necessary information is provided in the FailuresAccessor argument, and the method implementation can access to involved transaction names and failure definition ids, for example, through the FailuresAccessor.
Personally, it is a good idea to register the processor in the OnStartup() of an external application only once:
FailureProcessor1.Register()
Now it is time to create the call back methods and hook them up with some documents and transactions in some exernal commands:
…
FailureProcessor1.ProcessFailuresInTransaction(CachedDoc, "TransactionTest1", AddressOf CallbackOfTransactionTest1)
FailureProcessor1.ProcessFailuresInTransaction(CachedDoc, "TransactionTest2", AddressOf CallbackOfTransactionTest2)
…
Private Function GetLevel(ByVal doc As Document) As Autodesk.Revit.DB.Level
' 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)
Return lvl
End Function
Private Function CallbackOfTransactionTest1(ByVal doc As Document, ByRef ids As List(Of Autodesk.Revit.DB.ElementId)) As Boolean
Dim line As Line = doc.Application.Create.NewLineBound(New Autodesk.Revit.DB.XYZ(0, 0, 0), New Autodesk.Revit.DB.XYZ(10, 0, 0))
ids = New List(Of Autodesk.Revit.DB.ElementId)
Dim id As Autodesk.Revit.DB.ElementId = doc.Create.NewWall(line, GetLevel(doc), False).Id
ids.Add(id)
Return True
End Function
Private Function CallbackOfTransactionTest2(ByVal doc As Document, ByRef ids As List(Of Autodesk.Revit.DB.ElementId)) As Boolean
Dim line As Line = doc.Application.Create.NewLineBound(New Autodesk.Revit.DB.XYZ(0, 10, 0), New Autodesk.Revit.DB.XYZ(10, 10, 0))
ids = New List(Of Autodesk.Revit.DB.ElementId)
Dim id As Autodesk.Revit.DB.ElementId = doc.Create.NewWall(line, GetLevel(doc), False).Id
ids.Add(id)
Return False
End Function
A few words about the transaction callback may be worth of mentioning. Its Boolean return type indicates if the element just created is good or bad and the reference ElementId points to the element, good or bad.
As can be noted, there are quite some details that need to be addressed. The good news is that all these VB.NET code can be created through a few clicks by the RevitAddinWizard automatically.
Recent Comments