通常、ポストされたエラーはトランザクションの終了時に Revit の標準エラー解決 UI で処理されます(特に、Transaction.Commit()メソッドや Transaction.Rollback()メソッドが呼び出された場合)。エラーを処理するための情報とオプションがユーザに提示されます。
ドキュメント上のある操作(または一連の操作)に、特定のエラーに対する Revit アドインからの何らかの特別な処理が必要な場合、このような解決を実行するようエラー処理をカスタマイズできます。次の場合にカスタムのエラー処理を設定できます。
最後に、API には IFailuresProcessor インタフェースを使用して標準エラー処理ユーザ インタフェースを完全に置き換える機能があります。多くの場合、エラー処理には最初の 2 つのメソッドで十分ですが、より効果的なエラー処理 UI を提供したり、アプリケーションを Revit の最上位のフロントエンドとして使用するような特別な場合に、最後のオプションを使用できます。
Transaction.Commit()への呼び出しと実際のエラー処理の間には、多数の処理が行われることに注意してください。自動結合、重なりのチェック、グループのチェック、ワークセットの編集可能性のチェックなどはその一部です。これらのチェックや変更により、一部のエラーが消失することもありますが、さらに新しいエラーがポストされることも多くあります。したがって、Transaction.Commit()が呼び出されたときには、処理すべきエラーの状態が完全には把握できていません。エラーを正しく処理するには、実際のエラー処理メカニズムに接続する必要があります。
エラー処理が始まると、トランザクションで作成されるはずであったドキュメントに対するすべての変更が行われ、すべてのエラーがポストされます。したがって、エラー処理時には、ドキュメントに対するコントロールされていない変更は許可されません。FailuresAccessor が提供する限定されたインタフェースでは、エラー解決の能力に限界があります。この場合は、すべてのトランザクション後のチェックとエラー処理を繰り返す必要があります。そのため、トランザクションの終わりにはエラー解決サイクルが数回発生する場合があります。
各エラー処理サイクルには次の 3 つの手順が含まれます。
これらの 3 つの手順は、異なる FailureProcessingResults を返すことによって、次に発生する処理をコントロールすることができます。次のオプションがあります。
トランザクションにポストされたエラーの重大度と、トランザクションがコミットされるかロールバックされるかに応じて、これらの 3 つの手順のそれぞれがエラーを解決するためのオプションを持つことができます。ドキュメントにポストされるエラーに関するすべての情報、エラーを解決するための特定の操作を実行するための機能に関する情報、そのような操作を実行する API については、FailuresAccessor クラスによって提供されます。ドキュメントを使用すると追加情報を入手できますが、FailuresAccessor 以外の方法で変更することはできません。
FailuresAccessor オブジェクトはエラー処理の各ステップに引数として渡され、エラーに関する情報をドキュメントに取得するために使用可能な唯一のインタフェースです。エラー処理中のドキュメントからの読み取りは許可されていますが、エラー解決中にドキュメントを修正する唯一の方法はこのクラスによって提供されるメソッドのみです。エラー処理から戻ると、クラスのインスタンスが非アクティブになり、それ以降は使用できなくなります。
FailuresAccessor オブジェクトは次のような一般情報を提供します。
FailuresAccessor オブジェクトも、GetFailuresMessages()メソッドを介して特定のエラーに関する情報を提供します。
FailuresAccessor オブジェクトには次のようなエラーを解決するための方法がいくつか備わっています。
IFailuresPreprocessor インタフェースを使用すると、特定のトランザクションのみに対して、カスタムのエラー処理を提供できます。IFailuresPreprocessor は前処理手順を実行するために使用できるインタフェースであり、予想されるトランザクション エラーをフィルタしたり、新しいエラーをポストできます。エラーは次のようにしてフィルタにより除外されます。
エラー解決処理中は、IFailuresPreprocessor インタフェースが最初にコントロールを取得します。ポストこれは、すべてのエラーのポストが保証されるか、関係のないすべてのエラーが削除された後、IFailuresPreprocessor が適切なタイミングでコントロールを取得する点を除き、トランザクションが終了する前にエラーのチェックと解決を行うのとほぼ同等です。
IFailuresPreprocessor はトランザクションごとに 1 つのみとなり、既定のエラー プリプロセッサはありません。トランザクションにアタッチされていない場合(エラー処理オプションを介して)、このエラー処理の最初の手順は単純に省略されます。
コード領域 26-3: IFailuresPreprocessor からエラー処理 |
public class SwallowTransactionWarning : IExternalCommand { public Autodesk.Revit.UI.Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { Autodesk.Revit.ApplicationServices.Application app = commandData.Application.Application; Document doc = commandData.Application.ActiveUIDocument.Document; UIDocument uidoc = commandData.Application.ActiveUIDocument; FilteredElementCollector collector = new FilteredElementCollector(doc); ICollection<Element> elementCollection = collector.OfClass(typeof(Level)).ToElements(); Level level = elementCollection.Cast<Level>().ElementAt<Level>(0); Transaction t = new Transaction(doc); t.Start("room"); FailureHandlingOptions failOpt = t.GetFailureHandlingOptions(); failOpt.SetFailuresPreprocessor(new RoomWarningSwallower()); t.SetFailureHandlingOptions(failOpt); doc.Create.NewRoom(level, new UV(0, 0)); t.Commit(); return Autodesk.Revit.UI.Result.Succeeded; } } public class RoomWarningSwallower : IFailuresPreprocessor { public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor) { IList<FailureMessageAccessor> failList = new List<FailureMessageAccessor>(); // Inside event handler, get all warnings failList = failuresAccessor.GetFailureMessages(); foreach (FailureMessageAccessor failure in failList) { // check FailureDefinitionIds against ones that you want to dismiss, FailureDefinitionId failID = failure.GetFailureDefinitionId(); // prevent Revit from showing Unenclosed room warnings if (failID == BuiltInFailures.RoomFailures.RoomNotEnclosed) { failuresAccessor.DeleteWarning(failure); } } return FailureProcessingResult.Continue; } } |
FailuresProcessing イベントは、セッション全体や多数の関係のないトランザクションに対してユーザ インタフェースなしにカスタムのエラー処理を提供するアプリケーションに最適です。次は、このイベントを介したエラー処理の使用例です。
FailuresProcessing イベントは IFailuresPreprocessor(ある場合)が終了した後に発生します。ハンドラをいくつでも持つことができ、すべてが呼び出されます。イベント ハンドラには値を返す方法がないため、イベントの引数の SetProcessingResult()を使用してステータスを伝える必要があります。設定できるのは、Continue、ProceedWithRollback、ProceedWithCommit のみです。
次の例は、FailuresProcessing イベントのイベント ハンドラを表しています。
コード領域 26-4: FailuresProcessing イベントを処理 |
private void CheckWarnings(object sender, FailuresProcessingEventArgs e) { FailuresAccessor fa = e.GetFailuresAccessor(); IList<FailureMessageAccessor> failList = new List<FailureMessageAccessor>(); failList = fa.GetFailureMessages(); // Inside event handler, get all warnings foreach (FailureMessageAccessor failure in failList) { // check FailureDefinitionIds against ones that you want to dismiss, FailureDefinitionId failID = failure.GetFailureDefinitionId(); // prevent Revit from showing Unenclosed room warnings if (failID == BuiltInFailures.RoomFailures.RoomNotEnclosed) { fa.DeleteWarning(failure); } } } |
FailuresProcessing イベントが処理された後に、IFailuresProcessor インタフェースは最後にコントロールを取得します。Revit セッションでアクティブな IFailuresProcessor は 1 つだけです。エラー プロセッサを登録するには、IFailuresProcessor からクラスを派生させて、Application.RegisterFailuresProcessor()メソッドを使用して登録します。登録済みのエラー プロセッサがある場合は破棄されます。Revit アドインが Revit 用のエラー プロセッサを登録する場合は、そのプロセッサがセッションのすべての Revit エラーの既定のエラー ハンドラとなり、標準の Revit エラー ダイアログは表示されません。エラー プロセッサが設定されていない場合は、Revit UI にある既定のエラー プロセッサがすべての通常の Revit エラー ダイアログを呼び出します。FailuresProcessor は、既存の Revit エラー UI をカスタムのエラー解決ハンドラ(インタラクティブまたはユーザ インタフェースを持たないもの)に置き換える場合にのみオーバーライドされます。
RegisterFailuresProcessor()メソッドに null を渡した場合、エラーのあるトランザクションはすべてサイレントに中止されます(エラー処理の最初の 2 つの手順でエラーが解決されない場合)。
IFailuresProcessor.ProcessFailures()メソッドは、トランザクションを保留にしておく WaitForUserInput を返すことができます。この場合、FailuresProcessor は最終的に保留中のトランザクションをコミットまたはロールバックする UI を画面上に残すと想定されます。UI が残されない場合、保留中の状態が無制限に続き、実質的にドキュメントがフリーズすることになります。
IFailuresProcessor 実装の次の例では、エラーを確認し、エラーが発生している要素を削除し、ユーザ用の適切なメッセージを設定します。
コード領域 26-5: IFailuresProcessor |
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Automatic)] public class MyFailuresUI : IExternalApplication { static AddInId m_appId = new AddInId(new Guid("9F179363-B349-4541-823F-A2DDB2B86AF3")); public Autodesk.Revit.UI.Result OnStartup(UIControlledApplication uiControlledApplication) { IFailuresProcessor myFailUI = new FailUI(); Autodesk.Revit.ApplicationServices.Application.RegisterFailuresProcessor(myFailUI); return Result.Succeeded; } public Autodesk.Revit.UI.Result OnShutdown(UIControlledApplication application) { return Result.Succeeded; } public class FailUI : IFailuresProcessor { public void Dismiss(Document document) { // This method is being called in case of exception or document destruction to // dismiss any possible pending failure UI that may have left on the screen } public FailureProcessingResult ProcessFailures(FailuresAccessor failuresAccessor) { IList<FailureResolutionType> resolutionTypeList = new List<FailureResolutionType>(); IList<FailureMessageAccessor> failList = new List<FailureMessageAccessor>(); // Inside event handler, get all warnings failList = failuresAccessor.GetFailureMessages(); string errorString = ""; bool hasFailures = false; foreach (FailureMessageAccessor failure in failList) { // check how many resolutions types were attempted to try to prevent // entering infinite loop resolutionTypeList = failuresAccessor.GetAttemptedResolutionTypes(failure); if (resolutionTypeList.Count >= 3) { TaskDialog.Show("Error", "Cannot resolve failures - transaction will be rolled back."); return FailureProcessingResult.ProceedWithRollBack; } errorString += "IDs "; foreach (ElementId id in failure.GetFailingElementIds()) { errorString += id + ", "; hasFailures = true; } errorString += "\nWill be deleted because: " + failure.GetDescriptionText() + "\n"; failuresAccessor.DeleteElements( failure.GetFailingElementIds() as IList<ElementId>); } if (hasFailures) { TaskDialog.Show("Error", errorString); return FailureProcessingResult.ProceedWithCommit; } return FailureProcessingResult.Continue; } } } |