The IFailuresProcessor provides a way to define custom failures and handle them within some particular transactions.
It has two methods needing to be implemented. The Dismiss() and the 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.
public static FailureDefinitionId BadElement = new FailureDefinitionId(new Guid("6D65D49B-3B26-4d24-A81D-545DF80D2627"));
public static FailureDefinition FailureDefinitionBadElement
{
get
{
FailureDefinition failDef = FailureDefinition.CreateFailureDefinition(BadElement, FailureSeverity.Error, "Element is bad!");
failDef.AddResolutionType(FailureResolutionType.DeleteElements, "Delete Elements", typeof(DeleteElements));
failDef.SetDefaultResolutionType(FailureResolutionType.DeleteElements);
return failDef;
}
}
Another good to have thing is create a registration helper method so as to register the IFailuresProcessor somewhere when necessary.
public static void Register()
{
FailureProcessor processor = new FailureProcessor();
RvtApplication.RegisterFailuresProcessor(processor);
}
Here is the remaining part of the implementation:
public class FailureProcessor : IFailuresProcessor
{
private static List<KeyValuePair<string, FailureProcessingResult>> TransNameFailureResultPairList
{
get
{
List<KeyValuePair<string, FailureProcessingResult>> list = new List<KeyValuePair<string, FailureProcessingResult>>();
list.Add(new KeyValuePair<string, FailureProcessingResult>("XXXXX", FailureProcessingResult.ProceedWithCommit));
list.Add(new KeyValuePair<string, FailureProcessingResult>("YYYYY", FailureProcessingResult.ProceedWithCommit));
return list;
}
}
private List<FailureSeverity> FailureSeverityList
{
get
{
List<FailureSeverity> list = new List<FailureSeverity>();
list.Add(FailureSeverity.Warning);
list.Add(FailureSeverity.Error);
return list;
}
}
// A delegate for the caller to define and pass back to the method
public delegate bool ElementHandling(Document doc, ref ElementId id);
// A helper method for the caller to call
public static void PreprocessFailuresInTransaction(Document doc, string transName, ElementHandling action)
{
Transaction trans = new Transaction(doc, transName);
trans.Start();
ElementId id = ElementId.InvalidElementId;
bool success = action(doc, ref id);
if (!success)
{
FailureMessage failMsg = new FailureMessage(BadElement);
FailureResolution failRsl = DeleteElements.Create(doc, id);
failMsg.AddResolution(FailureResolutionType.DeleteElements, failRsl);
doc.PostFailure(failMsg);
}
trans.Commit();
}
#region IFailuresProcessor Members
public void Dismiss(Document document)
{
}
public FailureProcessingResult ProcessFailures(FailuresAccessor failuresAccessor)
{
FailureProcessingResult fpResult = FailureProcessingResult.ProceedWithCommit;
IList<FailureMessageAccessor> msgAccessorList = failuresAccessor.GetFailureMessages();
foreach (FailureMessageAccessor msgAccessor in msgAccessorList)
{
String tranName = failuresAccessor.GetTransactionName();
if (!TransNameFailureResultPairList.Exists(e => e.Key.Equals(tranName)))
{
continue;
}
KeyValuePair<string, FailureProcessingResult> kvp = TransNameFailureResultPairList.First(e => e.Key.Equals(tranName));
if (msgAccessor.GetFailureDefinitionId().Guid.ToString().Equals(BadElement.Guid.ToString()))
{
failuresAccessor.ResolveFailure(msgAccessor);
fpResult = kvp.Value;
}
}
return fpResult;
}
#endregion
}
Personally, it is a good idea to register the processor in the OnStartup() of an external application only once:
// Register the FailureProcessor
FailureProcessor.Register();
Now it is time to create the call back methods for involved transactions and hook them up into some exernal commands:
FailureProcessor.ProcessFailuresInTransaction(CachedDoc, "XXXXX", CallbackOfXXXXX);
FailureProcessor.ProcessFailuresInTransaction(CachedDoc, "YYYYY", CallbackOfYYYYY);
private bool CallbackOfXXXXX(Document doc, ref ElementId id)
{
Line line = doc.Application.Create.NewLineBound(new XYZ(0, 0, 0), new XYZ(10, 0, 0));
id = doc.Create.NewWall(line, GetLevel(), false).Id;
doc.Regenerate();
return true;
}
private bool CallbackOfYYYYY(Document doc, ref ElementId id)
{
Line line = doc.Application.Create.NewLineBound(new XYZ(0, 10, 0), new XYZ(10, 10, 0));
id = doc.Create.NewWall(line, GetLevel(), false).Id;
doc.Regenerate();
return false;
}
The ProcessFailuresInTransaction call can be put into the Execute() call back and the transaction callbacks into the same external command class. 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 can be done through a few clicks by the RevitAddinWizard automatically.
Links to some related articles:
Use RevitAddinWizard to Create IUpdater Derivatives of Revit API
Implement IFailuresPreprocessor of Revit API
Use RevitAddinWizard to Implement IFailuresPreprocessor of Revit API
Implement An IFailuresProcessor of Revit API
Use RevitAddinWizard to Implement IFailuresProcessor of Revit API
Command Availability And Revit Flavors/Categories of Revit API
Use RevitAddinWizard to Implement IExternalCommandavailability of Revit API
Implement ISelectionFilter of Revit API
Use RevitAddinWizard to Implement ISelectionFilter of Revit API
Recent Comments