2010-05-04 7 views
5

Otro hola a todos,Excel automatización: Primer caso faltante

estoy haciendo automatización de Excel a través de interoperabilidad en C#, y yo quiero ser informado cuando se cierra un libro. Sin embargo, no hay ningún evento Cerrar en el libro de trabajo ni un evento Salir en la aplicación.

¿Alguien ha hecho eso antes? ¿Cómo puedo escribir un fragmento de código que reacciona al libro de trabajo cerrado (que solo se ejecuta si el libro está realmente cerrado)? Idealmente, eso debería suceder después de cerrar el libro de trabajo, por lo que puedo confiar en el archivo para reflejar todos los cambios.

detalles acerca de lo que he encontrado hasta ahora:

hay un evento BeforeClose(), pero si hay cambios sin guardar se eleva este evento antes de que el usuario le pide si desea guardar ellos, por lo que en el momento en que pueda procesar el evento, no tengo el archivo final y no puedo liberar los objetos COM, las dos cosas que necesito tener/hacer. Ni siquiera sé si el libro de trabajo se cerrará realmente, ya que el usuario puede optar por cancelar el cierre.

Luego hay un evento BeforeSave(). Por lo tanto, si el usuario elige "Sí" para guardar los cambios no guardados, entonces BeforeSave() se ejecuta después de BeforeClose(). Sin embargo, si el usuario elige "Abortar", luego pulsa "archivo-> guardar", se ejecuta exactamente el mismo orden de eventos. Además, si el usuario elige "No", el BeforeSave() no se ejecuta en absoluto. Lo mismo es válido siempre que el usuario no haga clic en ninguna de estas opciones.

Respuesta

4

He creado un corte usando un enfoque de sondeo similar, y funciona:

Dado el libro de observar, creo un hilo que periódicamente intenta encontrar ese libro en la colección de libros de trabajo.

(La clase DisposableCom es mi solución actual a properly cleanup COM objects.)

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(); 

Los "ContainsWorkbookProperly" métodos de extensión es similar a esto:

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; 
} 

Todavía estaría interesado si hay una simple o mejor solución.

-1

¿Puede utilizar ambos eventos? En BeforeClose() establece un indicador, entonces BeforeSave() ve si el indicador está establecido. Sin embargo, necesitaría una forma de restablecerlo en caso de que se active BeforeClose() y BeforeSave() no. No estoy seguro de si hay algo más que pueda ayudar con eso.

Editar: Parece que ya cubrió esto con "se ejecuta exactamente el mismo orden de eventos". Pero si puede encontrar una manera de restablecerlo (¿otro evento "Cancelar"?), Puede funcionar.

+0

Hola Nelson: sí, ya lo he cubierto. El problema es que la parte "y BeforeSave() no es" parte de su propuesta es semidecidable. Si no se llama, puedo esperar pero nunca decidir si esto significa que abortó, eligió no, o simplemente no ha hecho clic en ningún botón todavía. – chiccodoro

3

Ésta no es mi código, pero esto funcionaba un lujo para mí:

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

Copiar:

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; } 
    } 
} 

Cuando lo usé me cambiaron de vuelta el nombre de la libro de trabajo a una referencia al libro de trabajo.

Cuestiones relacionadas