Автоматизация Excel: закрытие события отсутствует - PullRequest
5 голосов
/ 04 мая 2010

Еще привет всем,

Я делаю автоматизацию Excel через Interop в C # и хочу получать информацию, когда рабочая книга закрыта. Однако в книге нет события Close и события Quit в приложении.

Кто-нибудь делал это раньше? Как я могу написать кусок кода, который реагирует на закрытие рабочей книги (которая выполняется, только если рабочая книга действительно закрыта)? В идеале это должно произойти после закрытие книги, поэтому я могу рассчитывать на файл, чтобы отразить все изменения.

Подробности о том, что я нашел до сих пор:

Существует событие BeforeClose (), но если есть несохраненные изменения, это событие вызывается до того, как у пользователя спрашивают, сохранять ли его, поэтому в настоящий момент я могу обработать событие, у меня нет окончательного файла и Я не могу выпустить объекты COM, обе вещи, которые мне нужно иметь / делать. Я даже не знаю, будет ли книга на самом деле закрыта, поскольку пользователь может отменить закрытие.

Затем происходит событие BeforeSave (). Таким образом, если пользователь выбирает «Да» для сохранения несохраненных изменений, то BeforeSave () выполняется после BeforeClose (). Однако если пользователь выбирает «Abort», а затем нажимает «file-> save», выполняется точно такой же порядок событий. Кроме того, если пользователь выбирает «Нет», BeforeSave () вообще не выполняется. То же самое верно, пока пользователь не нажимает ни один из этих параметров.

Ответы [ 3 ]

4 голосов
/ 05 мая 2010

Я создал хак с использованием подхода, подобного опросу, и он работает:

Учитывая рабочую книгу для наблюдения, я создаю поток, который периодически пытается найти эту книгу в коллекции рабочих книг.

(Класс DisposableCom - мое текущее решение для правильной очистки COM-объектов .)

Excel.Application app = wbWorkbook.Application;
string sWorkbookName = wbWorkbook.Name;

Thread overseeWorkbooksThread = new Thread(new ThreadStart(
    delegate()
    {
        bool bOpened = false;

        Excel.Workbooks wbsWorkbooks = app.Workbooks;
        using (new DisposableCom<Excel.Workbooks>(wbsWorkbooks))
        {
            while (true)
            {
                Thread.Sleep(1000);

                if (wbsWorkbooks.ContainsWorkbookProperly(sWorkbookName))
                    bOpened = true;
                else
                    if (bOpened)
                        // Workbook was open, so it has been closed.
                        break;
                    else
                    {
                        // Workbook simply not finished opening, do nothing
                    }
            }

            // Workbook closed
            RunTheCodeToBeRunAfterWorkbookIsClosed();
        }
    }));

overseeWorkbooksThread.Start();

Методы расширения «ContainsWorkbookProperly» выглядят так:

public static bool ContainsWorkbookProperly(this Excel.Workbooks excelWbs,
    string sWorkbookName)
{
    Excel.Workbook wbTemp = null;
    try
        wbTemp = excelWbs.Item(sWorkbookName);
    catch (Exception)
    {
        // ignore
    }

    if (wbTemp != null)
    {
        new DisposableCom<Excel.Workbook>(wbTemp).Dispose();
        return true;
    }

    return false;
}

Тем не менее мне было бы интересно, есть ли более простое или лучшее решение.

3 голосов
/ 02 июня 2016

Это не мой код, но это сработало для меня:

https://gist.github.com/jmangelo/301884

Копировать вставить:

using System;
using Excel = Microsoft.Office.Interop.Excel;

namespace Helpers.Vsto
{
    public sealed class WorkbookClosedMonitor
    {
        internal class CloseRequestInfo
        {
            public CloseRequestInfo(string name, int count)
            {
                this.WorkbookName = name;
                this.WorkbookCount = count;
            }

            public string WorkbookName { get; set; }

            public int WorkbookCount { get; set; }
        }

        public WorkbookClosedMonitor(Excel.Application application)
        {
            if (application == null)
            {
                throw new ArgumentNullException("application");
            }

            this.Application = application;

            this.Application.WorkbookActivate += Application_WorkbookActivate;
            this.Application.WorkbookBeforeClose += Application_WorkbookBeforeClose;
            this.Application.WorkbookDeactivate += Application_WorkbookDeactivate;
        }

        public event EventHandler<WorkbookClosedEventArgs> WorkbookClosed;

        public Excel.Application Application { get; private set; }

        private CloseRequestInfo PendingRequest { get; set; }

        private void Application_WorkbookDeactivate(Excel.Workbook wb)
        {
            if (this.Application.Workbooks.Count == 1)
            {
                // With only one workbook available deactivating means it will be closed
                this.PendingRequest = null;

                this.OnWorkbookClosed(new WorkbookClosedEventArgs(wb.Name));
            }
        }

        private void Application_WorkbookBeforeClose(Excel.Workbook wb, ref bool cancel)
        {
            if (!cancel)
            {
                this.PendingRequest = new CloseRequestInfo(
                    wb.Name, 
                    this.Application.Workbooks.Count);
            }
        }

        private void Application_WorkbookActivate(Excel.Workbook wb)
        {
            // A workbook was closed if a request is pending and the workbook count decreased
            bool wasWorkbookClosed = true
                && this.PendingRequest != null
                && this.Application.Workbooks.Count < this.PendingRequest.WorkbookCount;

            if (wasWorkbookClosed)
            {
                var args = new WorkbookClosedEventArgs(this.PendingRequest.WorkbookName);

                this.PendingRequest = null;

                this.OnWorkbookClosed(args);
            }
            else
            {
                this.PendingRequest = null;
            }
        }

        private void OnWorkbookClosed(WorkbookClosedEventArgs e)
        {
            var handler = this.WorkbookClosed;

            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

    public sealed class WorkbookClosedEventArgs : EventArgs
    {
        internal WorkbookClosedEventArgs(string name)
        {
            this.Name = name;
        }

        public string Name { get; private set; }
    }
}

Когда я использовал его, я изменил его с возврата названия рабочей книги на ссылку на рабочую книгу.

0 голосов
/ 04 мая 2010

Можете ли вы использовать оба события? В BeforeClose () установите флаг, затем BeforeSave () посмотрите, установлен ли флаг. Вам потребуется способ его сброса, однако, в случае, если BeforeClose () запущен, а BeforeSave () нет. Не уверен, что есть что-то еще, что могло бы помочь с этим.

Редактировать: Похоже, вы уже рассмотрели это "точно такой же порядок событий выполняется". Но если вы можете найти способ сбросить его (другое событие «Отмена»?), Это может сработать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...