// // (C) Copyright 2003-2019 by Autodesk, Inc. All rights reserved. // // Permission to use, copy, modify, and distribute this software in // object code form for any purpose and without fee is hereby granted // provided that the above copyright notice appears in all copies and // that both that copyright notice and the limited warranty and // restricted rights notice below appear in all supporting // documentation. // // AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL ITS FAULTS. // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // // Use, duplication, or disclosure by the U.S. Government is subject to // restrictions set forth in FAR 52.227-19 (Commercial Computer // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. using System; using System.Collections.Generic; using System.Linq; using System.Text; using Autodesk.Revit.UI; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Events; using Autodesk.Revit.UI.Events; namespace Revit.SDK.Samples.PostCommandWorkflow.CS { /// /// This class has the capabilities to monitor when a document is saved, and prompt the user to /// add a revision before save takes place. /// class PostCommandRevisionMonitor { /// /// Constructs a new revision monitor for the given document. /// /// The document. public PostCommandRevisionMonitor(Document doc) { document = doc; } /// /// Activates the revision monitor for the stored document. /// public void Activate() { // Save the number of revisions as an initial count. storedRevisionCount = GetRevisionCount(document); // Setup event for saving. document.DocumentSaving += OnSavingPromptForRevisions; } /// /// Deactivates the revision monitor for the stored document. /// public void Deactivate() { // Remove the event for saving. document.DocumentSaving -= OnSavingPromptForRevisions; } #region RevitAPI event callbacks /// /// The DocumentSaving callback. This callback checks if at least one new revision has been added, and if not /// shows instructions to the user to deal with the situation. /// /// /// private void OnSavingPromptForRevisions(object sender, DocumentSavingEventArgs args) { Document doc = (Document)sender; UIApplication uiApp = new UIDocument(doc).Application; if (doc.IsModified) { // Compare number of revisions with saved count int revisionCount = GetRevisionCount(doc); if (revisionCount <= storedRevisionCount) { // Show dialog with explanation and options TaskDialog td = new TaskDialog("Revisions not created."); td.MainIcon = TaskDialogIcon.TaskDialogIconWarning; td.MainInstruction = "Changes have been made to this document, but no new revision has been created."; td.ExpandedContent = "Because the document has been released, it is typically required to issue a new " + "revision number with any change."; td.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Add revision now"); td.AddCommandLink(TaskDialogCommandLinkId.CommandLink2, "Cancel save"); td.AddCommandLink(TaskDialogCommandLinkId.CommandLink3, "Proceed with save (not recommended)."); td.TitleAutoPrefix = false; td.AllowCancellation = false; TaskDialogResult result = td.Show(); switch (result) { case TaskDialogResult.CommandLink1: // Add revision now { // cancel first save args.Cancel(); // add event to hide the default "Document not saved" dialog uiApp.DialogBoxShowing += HideDocumentNotSaved; // post command for editing revisions PromptToEditRevisionsAndResave(uiApp); break; } case TaskDialogResult.CommandLink2: // Cancel save { // cancel saving only args.Cancel(); break; } case TaskDialogResult.CommandLink3: // Proceed with save { // do nothing break; } } } else { storedRevisionCount = revisionCount; } } } /// /// The DialogBoxShowing callback to cancel display of the "Document not saved" message when saving is cancelled. /// /// /// private void HideDocumentNotSaved(object sender, DialogBoxShowingEventArgs args) { TaskDialogShowingEventArgs tdArgs = args as TaskDialogShowingEventArgs; // The "Document not saved" dialog does not have a usable id, so we are forced to look at the text instead. if (tdArgs != null && tdArgs.Message.Contains("not saved")) args.OverrideResult(0x0008); } /// /// The BeforeExecuted callback for the command, to setup the post action after the revision command is run. /// /// /// private void ReactToRevisionsAndSchedulesCommand(object sender, BeforeExecutedEventArgs args) { if (externalEvent != null) externalEvent.Raise(); } /// /// The external event raised to finalize the workflow after the user has been prompted to enter the /// Sheet Revisions/Issues command. /// class PostCommandRevisionMonitorEvent : IExternalEventHandler { #region IExternalEventHandler Members /// /// The external event callback to finalize the workflow. /// /// public void Execute(UIApplication app) { monitor.CleanupAfterRevisionEdit(app); } /// /// The external event name. /// /// public string GetName() { return "PostCommandRevisionMonitorEvent"; } /// /// The constructor for the event instance. /// /// The instance of the command. public PostCommandRevisionMonitorEvent(PostCommandRevisionMonitor monitor) { this.monitor = monitor; } /// /// Handle to the revision monitor. /// private PostCommandRevisionMonitor monitor; #endregion } #endregion /// /// Prompts to edit the revision and resave. /// /// private void PromptToEditRevisionsAndResave(UIApplication application) { // Setup external event to be notified when activity is done externalEvent = ExternalEvent.Create(new PostCommandRevisionMonitorEvent(this)); // Setup event to be notified when revisions command starts (this is a good place to raise this external event) RevitCommandId id = RevitCommandId.LookupPostableCommandId(PostableCommand.SheetIssuesOrRevisions); if (binding == null) { binding = application.CreateAddInCommandBinding(id); } binding.BeforeExecuted += ReactToRevisionsAndSchedulesCommand; // Post the revision editing command application.PostCommand(id); } /// /// The function that is called to finish the execution of the workflow. Cleans up subscribed events, /// and posts the save command. /// /// private void CleanupAfterRevisionEdit(UIApplication uiApp) { // Remove dialog box showing uiApp.DialogBoxShowing -= HideDocumentNotSaved; if (binding != null) binding.BeforeExecuted -= ReactToRevisionsAndSchedulesCommand; externalEvent = null; // Repost the save command uiApp.PostCommand(RevitCommandId.LookupPostableCommandId(PostableCommand.Save)); } /// /// Storage to remember the number of revisions when last checked. /// private int storedRevisionCount = 0; /// /// The handle to the external event instance to be invoked after the revision editing completes. /// private Autodesk.Revit.UI.ExternalEvent externalEvent = null; /// /// The binding to the revision command. /// private AddInCommandBinding binding = null; /// /// The document. /// private Document document; /// /// Counts the number of revision elements in the document. /// /// The document. /// The number of elements found. private static int GetRevisionCount(Document doc) { // Find revision objects FilteredElementCollector collector = new FilteredElementCollector(doc); collector.OfCategory(BuiltInCategory.OST_Revisions); return collector.ToElementIds().Count; } } }