using Autodesk.Revit.DB; using Autodesk.Revit.UI; using Xunit; namespace xUnitRevitUtils { /// /// Utility class with methods and properties used by the xUnit Revit plugin /// public static class xru { public static UIApplication Uiapp { get; set; } private static IList Queue { get; set; } private static ExternalEvent EventHandler { get; set; } public static SynchronizationContext UiContext { get; set; } public static void Initialize(UIApplication uiapp, SynchronizationContext uiContext, ExternalEvent eventHandler, IList queue) { Uiapp = uiapp; UiContext = uiContext; EventHandler = eventHandler; Queue = queue; } #region utility methods /// /// Returns the selected elements in the active document /// /// public static IList GetActiveSelection() { Assert.NotNull(Uiapp); if (Uiapp.ActiveUIDocument != null) return Uiapp.ActiveUIDocument.Selection.GetElementIds().Select(x => Uiapp.ActiveUIDocument.Document.GetElement(x)).ToList(); return new List(); } /// /// Opens and activates a document if not open already /// /// Path to the file to open public static Document OpenDoc(string filePath) { Assert.NotNull(Uiapp); Document doc = null; //OpenAndActivateDocument only works if run from the current context UiContext.Send(x => doc = Uiapp.OpenAndActivateDocument(filePath).Document, null); Assert.NotNull(doc); return doc; } /// /// Closes the provided Revit document /// /// Revit document to be closed /// If true, saves changes to document before closing /// /// Bool indicating whether or not the document was successfully closed /// public static bool CloseDoc(Document doc, bool saveChanges = false) { if (doc == null) return false; var result = false; UiContext.Send(x => result = doc.Close(saveChanges), null); return result; } /// /// Creates a new empty document /// /// Path to the project template /// Path where to save the new doc /// If true overwrites existing files with same name /// public static Document CreateNewDoc(string templatePath, string filePath, bool overwrite = true) { Assert.NotNull(Uiapp); Document doc = null; try { if (overwrite && File.Exists(filePath)) File.Delete(filePath); } catch { } //OpenAndActivateDocument only works if run from the current context UiContext.Send(x => { //if already open, just use it if (!File.Exists(filePath)) { doc = Uiapp.Application.NewProjectDocument(templatePath); doc.SaveAs(filePath); doc.Close(); } doc = Uiapp.OpenAndActivateDocument(filePath).Document; } , null); Assert.NotNull(doc); return doc; } /// /// Runs an Action in a Revit transaction, uses TaskCompletionSource to communicate when done /// /// Action to run /// Revit Document /// Transaction Name /// Enable to swallow all warnings generated by the transaction and prevent them from being raised within Revit /// public static Task RunInTransaction(Action action, Document doc, string transactionName = "transaction", bool ignoreWarnings = false) { var tcs = new TaskCompletionSource(); Queue.Add(new Action(() => { try { using var transaction = new Transaction(doc, transactionName); transaction.Start(); if (ignoreWarnings) { using var options = transaction.GetFailureHandlingOptions(); options.SetFailuresPreprocessor(new IgnoreAllWarnings()); transaction.SetFailureHandlingOptions(options); } action.Invoke(); transaction.Commit(); } catch (Exception e) { tcs.TrySetException(e); } tcs.TrySetResult(""); })); EventHandler.Raise(); return tcs.Task; } /// /// Runs an Action, uses TaskCompletionSource to communicate when done /// /// Action to run /// Revit Document /// public static Task Run(Action action, Document doc) { var tcs = new TaskCompletionSource(); Queue.Add(new Action(() => { try { action.Invoke(); } catch (Exception e) { tcs.TrySetException(e); } tcs.TrySetResult(""); })); EventHandler.Raise(); return tcs.Task; } /// /// A failures preprocesser that clears any failures that occur within a transaction /// internal class IgnoreAllWarnings : IFailuresPreprocessor { public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor) { var failList = failuresAccessor.GetFailureMessages(); foreach (FailureMessageAccessor failure in failList) { failuresAccessor.DeleteWarning(failure); } return FailureProcessingResult.Continue; } } #endregion } }