在 Word VSTO 中的 BeforeSave 事件中检测“不保存”

问题描述 投票:0回答:2

我正在为 Microsoft Word 开发 VSTO 加载项,但在处理 BeforeSave 事件时遇到困难。我希望在用户每次保存时编辑文档,但在某些情况下,当用户未保存文档时,BeforeSave 事件似乎会被触发。

假设用户打开一个空白Word文档,输入一些文本,然后尝试关闭该文档。对话框

显示。如果用户单击“不保存”,BeforeSave 事件仍会触发(该事件仅在“保存更改”对话框关闭后触发)。无论如何,是否可以检测单击“不保存”和单击“保存”之间的区别,或者防止在这种情况下触发“保存前”事件?

使用 Word.ApplicationEvents4_DocumentBeforeSaveEventHandler 分配事件处理程序 事件处理程序签名是 Application_DocumentBeforeSave(Word.Document doc, ref bool saveAsUI, ref bool cancel)

任何帮助都会非常感谢

c# ms-word vsto office-addins word-addins
2个回答
0
投票

Word 对象模型不为此提供任何事件属性。在这种情况下,您可以采取的最佳措施如下:

  1. 如果文档以前从未保存过,您可以查看Document.Saved属性。
  2. 通过将 Cancel 参数设置为
    true
    来取消
    Application.DocumentBeforeSave
    事件处理程序中的默认操作。因此,您可以显示自己的对话框,该对话框模仿默认的内置对话框,您可以在其中处理用户所做的每个操作。

0
投票

最终对我有用的解决方案基于https://theofficecontext.com/2013/04/26/updated-word-after-save-event/

的文章

作为参考,我正在使用 Office 2019,下面是对我有用的代码 - 一个稍微简化的版本,用于过滤掉单击“不保存”并触发“保存后”事件的用户。

    using System.Threading;
    using Word = Microsoft.Office.Interop.Word;

    /// <summary>
    /// The word save handler.
    /// </summary>
    public class WordSaveHandler
    {
        public delegate void AfterSaveDelegate(Word.Document doc, bool isClosed);

        // public events
        public event AfterSaveDelegate AfterSaveEvent;

        // module level
        private bool preserveBackgroundSave;
        private Word.Application oWord;
        private string closedFilename = string.Empty;

        /// <summary>
        /// Initializes a new instance of the <see cref="WordSaveHandler"/> class.
        /// CONSTRUCTOR  takes the Word application object to link to.
        /// </summary>
        /// <param name="oApp">Word Application.</param>
        public WordSaveHandler(Word.Application oApp)
        {
            this.oWord = oApp;

            // hook to before save
            this.oWord.DocumentBeforeSave += this.OWord_DocumentBeforeSave;
            this.oWord.WindowDeactivate += this.OWord_WindowDeactivate;
        }

        /// <summary>
        /// Gets public property to get the name of the file
        /// that was closed and saved.
        /// </summary>
        public string ClosedFilename
        {
            get
            {
                return this.closedFilename;
            }
        }

        /// <summary>
        /// WORD EVENT  fires before a save event.
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="saveAsUI"></param>
        /// <param name="cancel"></param>
        private void OWord_DocumentBeforeSave(Word.Document doc, ref bool saveAsUI, ref bool cancel)
        {
            // This could mean one of four things:
            // 1) we have the user clicking the save button
            // 2) Another add-in or process firing a resular Document.Save()
            // 3) A Save As from the user so the dialog came up
            // 4) Or an Auto-Save event
            // so, we will start off by first:
            // 1) Grabbing the current background save flag. We want to force
            //    the save into the background so that Word will behave
            //    asyncronously. Typically, this feature is on by default,
            //    but we do not want to make any assumptions or this code
            //    will fail.
            // 2) Next, we fire off a thread that will keep checking the
            //    BackgroundSaveStatus of Word. And when that flag is OFF
            //    no know we are AFTER the save event
            this.preserveBackgroundSave = this.oWord.Options.BackgroundSave;
            this.oWord.Options.BackgroundSave = true;

            // kick off a thread and pass in the document object
            bool uiSave = saveAsUI; // have to do this because the bool from Word is passed to us as ByRef
            new Thread(() =>
            {
                this.Handle_WaitForAfterSave(doc, uiSave);
            }).Start();
        }

        /// <summary>
        /// This method is the thread call that waits for the same to compelte.
        /// The way we detect the After Save event is to essentially enter into
        /// a loop where we keep checking the background save status. If the
        /// status changes we know the save is complete and we finish up by
        /// determineing which type of save it was:
        /// 1) UI
        /// 2) Regular
        /// 3) AutoSave.
        /// </summary>
        /// <param name="doc">Document being saved.</param>
        /// <param name="uiSave">Whether a SaveAs UI is displayed.</param>
        private void Handle_WaitForAfterSave(Word.Document doc, bool uiSave)
        {
            bool docSaved = false;
            try
            {
                // we have a UI save, so we need to get stuck
                // here until the user gets rid of the SaveAs dialog
                if (uiSave)
                {
                    while (this.IsBusy())
                    {
                        Thread.Sleep(1);
                    }
                }

                docSaved = doc.Saved;

                // check to see if still saving in the background
                // we will hang here until this changes.
                while (this.oWord.BackgroundSavingStatus > 0)
                {
                    Thread.Sleep(1);
                }
            }
            catch (ThreadAbortException)
            {
                // we will get a thread abort exception when Word
                // is in the process of closing, so we will
                // check to see if we were in a UI situation
                // or not
                if (uiSave)
                {
                    this.AfterSaveEvent(null, true);
                    return;
                }
                else
                {
                    // new, close, don't save - docSaved = FALSE
                    // open close don't save - docSaved = FALSE
                    // open close save - docSaved = TRUE
                    if (docSaved)
                    {
                        this.AfterSaveEvent(null, true);
                    }

                    return;
                }
            }
            catch
            {
                this.oWord.Options.BackgroundSave = this.preserveBackgroundSave;
                return; // swallow the exception
            }

            try
            {
                // if it is a UI save, the Save As dialog was shown
                // so we fire the after ui save event
                if (uiSave)
                {
                    // we need to check to see if the document is
                    // saved, because of the user clicked cancel
                    // we do not want to fire this event
                    try
                    {
                        if (doc.Saved == true)
                        {
                            this.AfterSaveEvent(doc, false);
                            // new, save
                            // new, save as
                            // open save as
                            // open, turn on autosave
                            // new, turn on autosave
                        }
                    }
                    catch
                    {
                        // DOC is null or invalid. This occurs because the doc
                        // was closed. So we return doc closed and null as the
                        // document
                        this.AfterSaveEvent(null, true);
                        // -- new, close, save
                    }
                }
                else
                {
                    // if the document is still dirty
                    // then we know an AutoSave happened
                    try
                    {

                        this.AfterSaveEvent(doc, false); // fire regular save event
                                                         // open, save
                                                         // open, autosave
                    }
                    catch
                    {
                        // DOC is closed
                        this.AfterSaveEvent(null, true);
                    }
                }
            }
            catch { }
            finally
            {
                // reset and exit thread
                this.oWord.Options.BackgroundSave = this.preserveBackgroundSave;
            }
        }

        /// <summary>
        /// WORD EVENT – Window Deactivate
        /// Fires just before we close the document and it
        /// is the last moment to get the filename.
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="wn"></param>
        private void OWord_WindowDeactivate(Word.Document doc, Word.Window wn)
        {
            this.closedFilename = doc.FullName;
        }

        /// <summary>
        /// Determines if Word is busy  essentially that the File Save
        /// dialog is currently open
        /// </summary>
        /// <param name="oApp"></param>
        /// <returns></returns>
        private bool IsBusy()
        {
            try
            {
                // if we try to access the application property while
                // Word has a dialog open, we will fail
                object o = this.oWord.ActiveDocument.Application;
                return false; // not busy
            }
            catch
            {
                // so, Word is busy and we return true
                return true;
            }
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.