We already took a couple of looks at the Failure API in the past, e.g.:
Here is a slightly more convoluted situation that motivates revisiting the topic from a new angle, raised and solved by Stephen Faust of Revolution Design, Inc. together with Arnošt Löbel of the Revit API development team, who invested significant time and research in the issue:
Question: I am generating some floor objects from rooms in a project. In certain cases the sketch is not valid because of some oddities in the rooms. This is OK if I can just delete or not create the floors and hopefully not show a dialogue to the user. Without any failure handling it gives the user the 'can't make extrusion' error, but you can hit delete elements and be on your way. I thought I could do this with a failures preprocessor, but it doesn't seem to be working the way I hoped. Here is the functional part of my class that inherits IFailuresPreProcessor:
IList<FailureMessageAccessor> fmas = failuresAccessor.GetFailureMessages(); if( fmas.Count == 0 ) { return FailureProcessingResult.Continue; } foreach( FailureMessageAccessor fma in fmas ) { failuresAccessor.ResolveFailure( fma ); } return FailureProcessingResult.ProceedWithCommit;
When I use this with the transaction, it still gives me an error dialog, only this time it shows (Deleted) next to all of the elements. The only thing I can do is cancel, which is actually worse, because then you don't get any of the floors. What am I doing wrong here? What do I need to do to just handle the dialog and tell it to remove those floors that aren't valid?
Answer: What you probably need to do is to set the resolution on each failure.
There are also some checks required to avoid attempting to resolve a failure that may not be resolved (anymore).
I suggest the following starting point for an approach, plus further experimenting with it:
- Test if invalid elements (floors) can actually be deleted by calling FailuresAccessors.IsElementsDeletionPermitted.
- Possibly, for each failure, it may be tested if its resolution by deleting the invalid elements is permitted. The method to do so is FailuresAccessors.IsFailureResolutionPermitted taking a FailureResolutionType.DeleteElements argument.
- Assuming you can, failures should be resolved by FailuresAccessors.ResolveFailures.
- I am also guessing that before returning ProceedWitCommit, FailuresAccessors.IsTransactionBeingCommitted is tested.
- Return ProceedWitCommit.
Response: Thanks for the tips.
Here is what I ended up doing that seems to be working:
if( failuresAccessor.GetTransactionName() == "Create Temporary Roofs" ) { IList<FailureMessageAccessor> fmas = failuresAccessor.GetFailureMessages(); if( fmas.Count == 0 ) { return FailureProcessingResult.Continue; } foreach( FailureMessageAccessor fma in fmas ) { FailureSeverity s = fma.GetSeverity(); if( s == FailureSeverity.Warning ) { failuresAccessor.DeleteWarning( fma ); } else if( s == FailureSeverity.Error ) { if( failuresAccessor .IsElementsDeletionPermitted( fma.GetFailingElementIds().ToList() ) ) { failuresAccessor.DeleteElements( fma.GetFailingElementIds().ToList() ); } failuresAccessor.ResolveFailure( fma ); } } return FailureProcessingResult.ProceedWithCommit; } else { return FailureProcessingResult.Continue; }
Basically I separated out warnings from errors. Then warnings can be deleted, errors can be processed and delete elements can be directly called for specific elements causing the error.