应用程序:取消保存

Revit平台:所有

Revit版本:2011.0

首次发布用于:2010.0

编程语言:C#

技能等级:初级

类别:基础

类型:外部应用
主题:取消文档保存过程

摘要:此示例演示如何通过DocumentSaving/DocumentSavingAs事件的参数取消文档保存过程。

相关类:

Autodesk.Revit.UI.IExternalApplication

Autodesk.Revit.ApplicationServices.ControlledApplication

Autodesk.Revit.DB.ProjectInfo

项目文件:

CancelSave.cs此文件包含一个实现IExternalApplication接口的类CancelSave。一旦用户保存了项目,它就会检查“项目状态”值。如果值已更新,则接受保存,否则取消保存并通知用户。

LogManager.cs此文件包含一个类LogManager,其中将创建一个日志文件用于跟踪引发的事件。

功能:

项目保存后,必须更新“项目信息”对话框中的“项目状态”,否则取消保存并通知用户。此示例显示如何通过API事件机制实现此功能。

-通过DocumentOpened和DocumentCreated事件,一旦用户完成打开或创建项目,此示例将保留原始的“项目状态”值。

-通过DocumentSaving和DocumentSavingAs,此示例将当前项目状态值与原始值进行比较。如果它们相同,则取消保存并通过消息框通知用户,否则传递保存并更新原始值,为下一次保存做准备。

实施:

1.打开Revit.exe 2.在Revit UI中创建一个项目,然后保存它(因为项目状态未更新,保存将被取消)。

3.运行菜单命令“设置\项目信息”,然后在该对话框中更新项目状态,然后单击“确定”按钮保存更新。

4.另存为此项目(因为项目状态已更新,所以允许保存)。

1.由于族文档不支持项目信息,因此此示例不应在族文档上运行。

完整的源代码请加入QQ群649037449,在群文件中下载RevitSDK.exe,解压后在文件夹中搜索本文中应用程序名称即可获得完整源码

CancelSave.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// 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 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.IO;
using System.Collections.Generic;
using System.Text;

using Autodesk.Revit;
using Autodesk.Revit.DB.Events;
using System.Windows.Forms;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Autodesk.RevitAddIns;

namespace Revit.SDK.Samples.CancelSave.CS
{
    /// <summary>
    /// This class is an external application which checks whether "Project Status" is updated 
    /// once the project is about to be saved. If updated pass the save else cancel the save and inform user with one message.
    /// </summary>
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
    [Autodesk.Revit.Attributes.Journaling(Autodesk.Revit.Attributes.JournalingMode.NoCommandData)]
    public class CancelSave : IExternalApplication
    {
        #region Memeber Variables
        
        const string thisAddinFileName = "CancelSave.addin";
        // The dictionary contains document hashcode and its original "Project Status" pair.
        Dictionary<int, string> documentOriginalStatusDic = new Dictionary<int, string>();
        int hashcodeofCurrentClosingDoc;

        #endregion

        #region IExternalApplication Members

        /// <summary>
        /// Implement OnStartup method of IExternalApplication interface.
        /// This method subscribes to DocumentOpened, DocumentCreated, DocumentSaving and DocumentSavingAs events.
        /// The first two events are used to reserve "Project Status" original value; 
        /// The last two events are used to check whether "Project Status" has been updated, and re-reserve current value as new original value for next compare.
        /// </summary>
        /// <param name="application">Controlled application to be loaded to Revit process.</param>
        /// <returns>The status of the external application</returns>
        public Autodesk.Revit.UI.Result OnStartup(UIControlledApplication application)
        {
            // subscribe to DocumentOpened, DocumentCreated, DocumentSaving and DocumentSavingAs events
            application.ControlledApplication.DocumentOpened   += new EventHandler<DocumentOpenedEventArgs>(ReservePojectOriginalStatus);
            application.ControlledApplication.DocumentCreated  += new EventHandler<DocumentCreatedEventArgs>(ReservePojectOriginalStatus);
            application.ControlledApplication.DocumentSaving   += new EventHandler<DocumentSavingEventArgs>(CheckProjectStatusUpdate);
            application.ControlledApplication.DocumentSavingAs += new EventHandler<DocumentSavingAsEventArgs>(CheckProjectStatusUpdate);
            application.ControlledApplication.DocumentClosing += new EventHandler<DocumentClosingEventArgs>(MemClosingDocumentHashCode);
            application.ControlledApplication.DocumentClosed += new EventHandler<DocumentClosedEventArgs>(RemoveStatusofClosedDocument);

            return Autodesk.Revit.UI.Result.Succeeded;
        }

        /// <summary>
        /// Implement OnShutdown method of IExternalApplication interface. 
        /// </summary>
        /// <param name="application">Controlled application to be shutdown.</param>
        /// <returns>The status of the external application.</returns>
        public Autodesk.Revit.UI.Result OnShutdown(UIControlledApplication application)
        {
            // unsubscribe to DocumentOpened, DocumentCreated, DocumentSaving and DocumentSavingAs events
            application.ControlledApplication.DocumentOpened   -= new EventHandler<DocumentOpenedEventArgs>(ReservePojectOriginalStatus);
            application.ControlledApplication.DocumentCreated  -= new EventHandler<DocumentCreatedEventArgs>(ReservePojectOriginalStatus);
            application.ControlledApplication.DocumentSaving   -= new EventHandler<DocumentSavingEventArgs>(CheckProjectStatusUpdate);
            application.ControlledApplication.DocumentSavingAs -= new EventHandler<DocumentSavingAsEventArgs>(CheckProjectStatusUpdate);

            // finalize the log file.
            LogManager.LogFinalize();

            return Autodesk.Revit.UI.Result.Succeeded;
        }

        #endregion

        #region EventHandler

        /// <summary>
        /// Event handler method for DocumentOpened and DocumentCreated events.
        /// This method will reserve "Project Status" value after document has been opened or created.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="args">Event arguments that contains the event data.</param>
        private void ReservePojectOriginalStatus(Object sender, RevitAPIPostDocEventArgs args)
        {
            // The document associated with the event. Here means which document has been created or opened.
            Document doc = args.Document;

            // Project information is unavailable for Family document.
            if (doc.IsFamilyDocument)
            {
                return;
            }

            // write log file. 
            LogManager.WriteLog(args, doc);
            
            // get the hashCode of this document.
            int docHashCode = doc.GetHashCode();

            // retrieve the current value of "Project Status". 
            string currentProjectStatus = RetrieveProjectCurrentStatus(doc);
            // reserve "Project Status" current value in one dictionary, and use this project's hashCode as key.
            documentOriginalStatusDic.Add(docHashCode, currentProjectStatus);

            // write log file. 
            LogManager.WriteLog("   Current Project Status: " + currentProjectStatus);
        }

        /// <summary>
        /// Event handler method for DocumentSaving and DocumentSavingAs events.
        /// This method will check whether "Project Status" has been updated, and reserve current value as original value.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="args">Event arguments that contains the event data.</param>
        private void CheckProjectStatusUpdate(Object sender, RevitAPIPreDocEventArgs args)
        {
            // The document associated with the event. Here means which document is about to save / save as.
            Document doc = args.Document;

            // Project information is unavailable for Family document.
            if (doc.IsFamilyDocument)
            {
                return;
            }

            // write log file.
            LogManager.WriteLog(args, doc);

            // retrieve the current value of "Project Status". 
            string currentProjectStatus = RetrieveProjectCurrentStatus(args.Document);

            // get the old value of "Project Status" for one dictionary.
            string originalProjectStatus = documentOriginalStatusDic[doc.GetHashCode()];

            // write log file.
            LogManager.WriteLog("   Current Project Status: " + currentProjectStatus + "; Original Project Status: " + originalProjectStatus);

            // project status has not been updated.
            if ((string.IsNullOrEmpty(currentProjectStatus) && string.IsNullOrEmpty(originalProjectStatus)) || 
                (0 == string.Compare(currentProjectStatus, originalProjectStatus,true)))
            {
                DealNotUpdate(args);
                return;
            }

            // update "Project Status" value reserved in the dictionary.
            documentOriginalStatusDic.Remove(doc.GetHashCode());
            documentOriginalStatusDic.Add(doc.GetHashCode(), currentProjectStatus);
        }

        private void MemClosingDocumentHashCode(Object sender, DocumentClosingEventArgs args)
        {
           hashcodeofCurrentClosingDoc = args.Document.GetHashCode();
        }

        private void RemoveStatusofClosedDocument(Object sender, DocumentClosedEventArgs args)
        {
           if (args.Status.Equals(RevitAPIEventStatus.Succeeded) && (documentOriginalStatusDic.ContainsKey(hashcodeofCurrentClosingDoc)))
           {
              documentOriginalStatusDic.Remove(hashcodeofCurrentClosingDoc);
           }
        }
        #endregion

        /// <summary>
        /// Deal with the case that the project status wasn't updated.
        /// If the event is Cancellable, cancel it and inform user else just inform user the status.
        /// </summary>
        /// <param name="args">Event arguments that contains the event data.</param>
        private static void DealNotUpdate(RevitAPIPreDocEventArgs args)
        {
            string mainMessage;
            string additionalText;
            TaskDialog taskDialog = new TaskDialog("CancelSave Sample");

            if (args.Cancellable)
            {
                args.Cancel(); // cancel this event if it is cancellable. 

                mainMessage = "CancelSave sample detected that the Project Status parameter on Project Info has not been updated. The file will not be saved."; // prompt to user.              
            }
            else
            {
                // will not cancel this event since it isn't cancellable. 
               mainMessage = "The file is about to save. But CancelSave sample detected that the Project Status parameter on Project Info has not been updated."; // prompt to user.              
            }

            // taskDialog will not show when do regression test.
            if (!LogManager.RegressionTestNow)
            {
               additionalText = "You can disable this permanently by uninstaling the CancelSave sample from Revit. Remove or rename CancelSave.addin from the addins directory.";

               // use one taskDialog to inform user current situation.     
               taskDialog.MainInstruction = mainMessage;
               taskDialog.MainContent     = additionalText;
               taskDialog.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Open the addins directory");
               taskDialog.CommonButtons = TaskDialogCommonButtons.Close;
               taskDialog.DefaultButton = TaskDialogResult.Close;
               TaskDialogResult tResult = taskDialog.Show();
               if (TaskDialogResult.CommandLink1 == tResult)
               {
                  System.Diagnostics.Process.Start("explorer.exe", DetectAddinFileLocation(args.Document.Application));
               }
            }

            // write log file.
            LogManager.WriteLog("   Project Status is not updated, taskDialog informs user: " + mainMessage);
        }

        /// <summary>
        /// Retrieve current value of Project Status.
        /// </summary>
        /// <param name="doc">Document of which the Project Status will be retrieved.</param>
        /// <returns>Current value of Project Status.</returns>
        private static string RetrieveProjectCurrentStatus(Document doc)
        {
            // Project information is unavailable for Family document.
            if (doc.IsFamilyDocument)
            {
                return null;
            }

            // get project status stored in project information object and return it.
            return doc.ProjectInformation.Status;
        }

        private static string DetectAddinFileLocation(Autodesk.Revit.ApplicationServices.Application applictaion)
        {
           string addinFileFolderLocation = null;
           IList<RevitProduct> installedRevitList = RevitProductUtility.GetAllInstalledRevitProducts();

           foreach (RevitProduct revit in installedRevitList)
           {
              if (revit.Version.ToString().Contains(applictaion.VersionNumber))
              {
                 string allUsersAddInFolder = revit.AllUsersAddInFolder;
                 string currentUserAddInFolder = revit.CurrentUserAddInFolder;

                 if (File.Exists(Path.Combine(allUsersAddInFolder, thisAddinFileName)))
                 {
                    addinFileFolderLocation = allUsersAddInFolder;
                 }
                 else if (File.Exists(Path.Combine(currentUserAddInFolder, thisAddinFileName)))
                 {
                    addinFileFolderLocation = currentUserAddInFolder;
                 }

                 break;                
              }
           }

           return addinFileFolderLocation;
        }
    }
}

LogManager.cs

//
// (C) Copyright 2003-2019 by Autodesk, Inc.
//
// 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 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.Text;
using System.Diagnostics;
using System.IO;
using Autodesk.Revit.DB;

namespace Revit.SDK.Samples.CancelSave.CS
{
    /// <summary>
    /// One log file will be created by this class for tracking events raise.
    /// </summary>
    public static class LogManager
    {
        // a trace listener for the output log of CancelSave sample
        private static TraceListener TxtListener;

        // the directory where this assembly in.
        private static string AssemblyLocation = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

        /// <summary>
        /// Retrieval if doing regression test now.
        /// If the ExpectedOutPut.log exists in the assembly folder returns true, else returns false.
        /// </summary>
        public static bool RegressionTestNow
        {
            get 
            {
                string expectedLogFile = Path.Combine(AssemblyLocation, "ExpectedOutPut.log");
                if (File.Exists(expectedLogFile))
                {
                    return true;
                }

                return false;
            }
        }

        /// <summary>
        /// Static constructor which creates a log file.
        /// </summary>
        static LogManager()
        {
            // Create CancelSave.log .
            string actullyLogFile = Path.Combine(AssemblyLocation, "CancelSave.log");

            // if already existed, delete it.
            if (File.Exists(actullyLogFile))
            {
                File.Delete(actullyLogFile);
            }

            TxtListener = new TextWriterTraceListener(actullyLogFile);
            Trace.Listeners.Add(TxtListener);
            Trace.AutoFlush = true;
        }

        /// <summary>
        /// Finalize and close the output log.
        /// </summary>
        public static void LogFinalize()
        {
            Trace.Flush();
            TxtListener.Close();
            Trace.Close();
            Trace.Listeners.Remove(TxtListener);
        }

        /// <summary>
        /// Write log to file: which event occurred in which document.
        /// </summary>
        /// <param name="args">Event arguments that contains the event data.</param>
        /// <param name="doc">document in which the event is occur.</param>
        public static void WriteLog(EventArgs args, Document doc)
        {
            Trace.WriteLine("");
            Trace.WriteLine("[Event] " + GetEventName(args.GetType()) + ": " + TitleNoExt(doc.Title));
        }

        /// <summary>
        /// Write specified message into log file.
        /// </summary>
        /// <param name="message">the message which will be written into the log file. </param>
        public static void WriteLog(string message)
        {
            Trace.WriteLine(message);
        }

        /// <summary>
        /// Get event name from its EventArgs, without namespace prefix
        /// </summary>
        /// <param name="type">Generic event arguments type.</param>
        /// <returns>the event name</returns>
        private static string GetEventName(Type type)
        {
            String argName = type.ToString();
            String tail = "EventArgs";
            String head = "Autodesk.Revit.DB.Events.";
            int firstIndex = head.Length;
            int length = argName.Length - head.Length - tail.Length;
            String eventName = argName.Substring(firstIndex, length);
            return eventName;
        }

        /// <summary>
        /// This method will remove the extension name of file name(if have).
        /// 
        /// Document.Title will return title of project depends on OS setting:
        /// If we choose show extension name by IE:Tools\Folder Options, then the title will end with accordingly extension name.
        /// If we don't show extension, the Document.Title will only return file name without extension.
        /// </summary>
        /// <param name="orgTitle">Origin file name to be revised.</param>
        /// <returns>New file name without extension name.</returns>
        private static string TitleNoExt(String orgTitle)
        {
            // return null directly if it's null
            if (String.IsNullOrEmpty(orgTitle))
            {
                return "";
            }

            // Remove the extension 
            int pos = orgTitle.LastIndexOf('.');
            if (-1 != pos)
            {
                return orgTitle.Remove(pos);
            }
            else
            {
                return orgTitle;
            }
        }
    }
}