VSTO:使用C#開發Excel、Word【12】

Excel對象模型中的事件
瞭解excel對象模型中的事件相當重要,由於這一般是代碼運行的主要方式。本章將檢查Excel對象模型中的全部事件,引起事件以及可能與這些事件關聯的代碼類型。編程

Excel對象模型中的許多事件在應用程序,工做簿和工做表對象上重複。此重複容許您決定是否要處理全部工做簿,特定工做簿或特定工做表的事件。例如,若是您想知道任何打開的工做簿中的任何工做表是否雙擊,您將處理Application對象的SheetBeforeDoubleClick事件。若是您想知道在特定工做簿中的任何工做表什麼時候被雙擊,您將處理該工做簿對象上的SheetBeforeDoubleClick事件。若是您想知道雙擊某個特定工做表時,您將處理該Worksheet對象上的BeforeDoubleClick事件。當在應用程序,工做簿和工做表對象上重複事件時,它一般首先在工做表上,而後是工做簿,最後是應用程序。app

新工做簿和工做表事件
當建立新的空白工做簿時,Excel的Application對象會引起NewWorkbook事件。當從模板或現有文檔建立新的工做簿時,不會引起此事件。在特定工做簿中建立新工做表時,Excel也會引起事件。一樣,這些事件僅在用戶首次建立新工做表時引起。在隨後打開工做簿時,他們不再會再提出。函數

如今討論的重點是提出新的工做簿和工做表事件的各類方式:工具

  • Application.NewWorkbook:建立新的空白工做簿時。 Excel將新的Workbook對象做爲參數傳遞給此事件。

         注: NewWorkbook是Application對象上的屬性和事件的名稱。 因爲此衝突,您將不會在Visual Studio的彈出菜單中看到與Application對象關聯的屬性,事件和方法的NewWorkbook事件。 此外,當您嘗試處理此事件時,會在編譯時顯示警告。 要使Visual Studio的彈出菜單工做,而且警告消失,您能夠將Application對象轉換到AppEvents_Event接口,如清單4-1所示。學習

  • Application.WorkbookNewSheet:在打開的工做簿中建立新工做表。 Excel將建立新工做表的Workbook對象做爲參數傳遞給此事件。 它也傳遞新的工做表對象。 由於工做簿能夠包含工做表和圖表工做表,因此新的工做表對象做爲對象傳遞。 而後,您能夠將其轉換爲工做表或圖表。
  • Workbook.NewSheet:在工做簿上建立了一個新的工做表。 Excel將新的工做表對象做爲參數傳遞給此事件。 新的工做表對象做爲一個對象傳遞,您能夠將其轉換爲工做表或圖表。

清單4-1顯示了一個處理Application對象的NewWorkbook和WorkbookNewSheet事件的控制檯應用程序。 它還建立一個新的工做簿,並處理新建立的工做簿的NewSheet事件。 控制檯應用程序處理工做簿的關閉事件,所以當您關閉工做簿時,控制檯應用程序將退出並退出Excel。 清單4-1顯示了其餘幾種經常使用技術。 對於做爲對象傳遞的工做表,咱們使用as操做符將對象轉換爲工做表或圖表。 而後,咱們將檢查結果,以驗證它不是null,以肯定演員是否成功。 這種方法證實比使用is運算符跟隨as運算符更有效率,由於後一種方法須要兩個轉換。ui

清單4-1  處理新工做簿和工做表事件的控制檯應用程序this

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

namespace ConsoleApplication
{
  class Program
  {
    static private Excel.Application app;
    static private Excel.Workbook workbook;
    static bool exit = false;

    static void Main(string[] args)
    {
      app = new Excel.Application();
      app.Visible = true;

      // We cast to AppEvents_Event because NewWorkbook
      // is the name of both a property and an event. 
      ((Excel.AppEvents_Event)app).NewWorkbook += new Excel.AppEvents_NewWorkbookEventHandler( App_NewWorkbook);

      app.WorkbookNewSheet +=new Excel.AppEvents_WorkbookNewSheetEventHandler( App_WorkbookNewSheet);

      workbook = app.Workbooks.Add(Type.Missing);
      workbook.NewSheet += new Excel.WorkbookEvents_NewSheetEventHandler( Workbook_NewSheet);

      workbook.BeforeClose +=new Excel.WorkbookEvents_BeforeCloseEventHandler( Workbook_BeforeClose);

      while (exit == false)
        System.Windows.Forms.Application.DoEvents();

      app.Quit();
    }

    static void App_NewWorkbook(Excel.Workbook workbook)
    {
      Console.WriteLine(String.Format( "Application.NewWorkbook({0})", workbook.Name));
    }

    static void App_WorkbookNewSheet(Excel.Workbook 
 workbook, object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;

      if (worksheet != null)
      {
        Console.WriteLine(String.Format( "Application.WorkbookNewSheet({0},{1})",  workbook.Name, worksheet.Name));
      }

      Excel.Chart chart = sheet as Excel.Chart;

      if (chart != null)
      {
        Console.WriteLine(String.Format( "Application.WorkbookNewSheet({0},{1})",  workbook.Name, chart.Name));
      }
    }

    static void Workbook_NewSheet(object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;

      if (worksheet != null)
      {
        Console.WriteLine(String.Format( "Workbook.NewSheet({0})", worksheet.Name));
      }

      Excel.Chart chart = sheet as Excel.Chart;

      if (chart != null)
      {
        Console.WriteLine(String.Format( "Workbook.NewSheet({0})", chart.Name));
      }
    }

    static void Workbook_BeforeClose(ref bool cancel)
    {
      exit = true;
    }
  }
}

當您考慮清單4-1中的代碼時,您可能會想知道如何記住複雜代碼行的語法,例如:spa

 app.WorkbookNewSheet += new Excel.AppEvents_WorkbookNewSheetEventHandler( App_WorkbookNewSheet);

幸運的是,Visual Studio 2005有助於自動生成大部分代碼行以及相應的事件處理程序。 若是您鍵入這行代碼,鍵入+ =後,Visual Studio將顯示一個彈出工具提示(參見圖4-1)。 若是您按Tab鍵兩次,Visual Studio會自動生成代碼行的其他部分和事件處理程序。3d

圖4-1  若是按Tab鍵,Visual Studio會爲您生成事件處理程序代碼excel

若是您使用Visual Studio 2005 Tools for Office(VSTO),還可使用「屬性」窗口將事件處理程序添加到工做簿或工做表類中。雙擊工做簿類的項目項(一般稱爲ThisWorkbook.cs)或您的工做表類之一(一般稱爲Sheet1.cs,Sheet2.cs等)。確保屬性窗口可見。若是不是,請從「視圖」菜單中選擇「屬性窗口」以顯示「屬性」窗口。確保在屬性窗口頂部的組合框中選擇了工做簿類(一般稱爲ThisWorkbook)或工做表類(一般稱爲Sheet1,Sheet2等)。而後單擊閃電圖標以顯示與工做簿或工做表相關的事件。在要處理的事件右側的編輯框中鍵入要用做事件處理程序的方法的名稱。

激活和停用事件
激活或停用各類對象時,Excel對象模型中的16個事件會被提高。一個對象被認爲是在其窗口接收到焦點時被激活,或者被選中或被激活的對象。例如,在工做簿中從一個工做表切換到另外一個工做表時,工做表將被激活和停用。單擊當前具備Sheet1的工做簿中Sheet3的選項卡,爲Sheet1(它正在失去焦點)提供一個Deactivate事件,併爲Sheet3啓動一個Activate事件(它正在獲得關注)。您能夠以相同的方式激活/禁用圖表。這樣作會引起激活和停用與激活或停用的圖表表相對應的圖表對象上的事件。

您也能夠激活/停用工做表。考慮您同時打開Book1和Book2的工做簿的狀況。若是您當前正在編輯Book1,並從「窗口」菜單中選擇Book2,則從Book1切換到Book2,則會引起Book1的「停用」事件,並提升Book2的Activate事件。

Windows是激活和停用的對象的另外一個例子。工做簿能夠打開多個窗口來顯示工做簿。考慮您打開Book1的工做簿Book1的狀況。若是從「窗口」菜單中選擇「新建窗口」,則在Excel中將打開兩個窗口來查看Book1。一個窗口的標題爲Book1:1,另外一個窗口的標題爲Book1:2。當您在Book1:1和Book1:2之間切換時,會爲工做簿引起WindowActivate事件。在Book1:1和Book1:2之間切換不會引起Workbook激活或停用事件,由於Book1仍然是活動的工做簿。

請注意,切換到Excel以外的應用程序,而後切換回Excel時,不會引起激活和停用事件。您可能但願若是您的Excel和Word並排在您的顯示器上,經過從Excel轉換爲Word切換焦點將提升Excel中的停用事件。這不是caseExcel不考慮切換到另外一個應用程序停用其任何工做簿,工做表或窗口。

如今的討論轉向激活和停用事件的各類方式:

  • 在Excel中激活工做簿時,將引起Application.WorkbookActivate。 Excel將做爲參數激活的Workbook對象傳遞給此事件。
  • Workbook.Activate是在激活的特定工做簿上引起的。 沒有參數傳遞給此事件,由於激活的工做簿是提升事件的Workbook對象。
  • 注:Activate是Workbook對象上的方法和事件的名稱。 因爲此衝突,您將不會在Visual Studio的彈出菜單中看到與Application對象關聯的屬性,事件和方法的Activate事件。 此外,當您嘗試處理此事件時,會在編譯時顯示警告。

    要使Visual Studio的彈出菜單工做並刪除警告,您能夠將Workbook對象轉換爲WorkbookEvents_Event界面,如清單4-1所示。

  • 每當在Excel中停用任何工做簿時,都會引起Application.WorkbookDeactivate。 Excel將做爲參數停用的Workbook對象傳遞給此事件。
  • Workbook.Deactivate是在停用的特定工做簿上引起的。沒有參數傳遞給此事件,由於已停用的工做簿是提升事件的Workbook對象。
  • 當Excel中激活工做表時,將引起Application.SheetActivate。 Excel將做爲參數激活的工做表對象傳遞給此事件。由於工做簿能夠包含工做表和圖表表,因此激活的工做表做爲對象傳遞。而後,您能夠將其轉換爲工做表或圖表。
  • Workbook.SheetActivate是在已激活工做表的工做簿上引起的。 Excel將做爲參數激活的工做表對象傳遞給此事件。由於工做簿能夠包含工做表和圖表表,因此激活的工做表做爲對象傳遞。而後,您能夠將其轉換爲工做表或圖表。
  • Worksheet.ActivateChart.Activate在激活的工做表或圖表表上提出。沒有參數傳遞給這些事件,由於激活的工做表是提升此事件的工做表或圖表對象。
  • 注:Activate是工做表和圖表對象上的方法和事件的名稱。 因爲此衝突,您將沒法在Visual Studio的與工做表或圖表對象關聯的屬性,事件和方法的彈出菜單中看到Activate事件。 此外,當您嘗試處理此事件時,會在編譯時顯示警告。 要使Visual Studio的彈出菜單工做而且警告消失,您能夠將Worksheet對象轉換爲DocEvents_Event接口,並將Chart對象轉換爲ChartEvents_Events界面,如清單4-2所示。

    很奇怪,您將Worksheet對象的界面稱爲DocEvents_Event。 這是因爲生成PIAs的方式COM對象上的事件接口工做表被稱爲DocEvents而不是WorksheetEvents。 Application對象發生一樣的不一致; 它有一個名爲AppEvents而不是ApplicationEvents的事件接口。

  • 每當在Excel中停用任何工做表時,都會引起Application.SheetDeactivate。 Excel將做爲參數停用的工做表對象傳遞給此事件。由於工做簿能夠包含工做表和圖表表,因此禁用的工做表做爲對象傳遞。而後,您能夠將其轉換爲工做表或圖表。
  • Workbook.SheetDeactivate是在已禁用工做表的工做簿上引起的。 Excel將做爲參數停用的工做表對象傳遞給此事件。由於工做簿能夠包含工做表和圖表表,因此禁用的工做表做爲對象傳遞。而後,您能夠將其轉換爲工做表或圖表。
  • Worksheet.DeactivateChart.Deactivate在禁用的工做表或圖表表上提出。沒有參數傳遞給這些事件,由於停用的工做表是提升此事件的工做表或圖表對象。
  • 當窗口在Excel中被激活時,將引起Application.WindowActivate。 Excel將與做爲參數激活的窗口對應的Workbook對象傳遞給此事件。 Excel還傳遞已激活的Window對象。
  • Workbook.WindowActivate是在已激活的窗口的工做簿上引起的。 Excel將做爲參數激活的Window對象傳遞給此事件。
  • 當窗口在Excel中停用時,Application.WindowDeactivate將被引起。 Excel將與停用的窗口對應的Workbook對象做爲參數傳遞給此事件。 Excel還會傳遞被禁用的Window對象。
  • Workbook.WindowDeactivate是在已禁用窗口的工做簿上引起的。 Excel將做爲參數停用的Window對象傳遞給此事件。

 

清單4-2顯示了一個處理全部這些事件的類。 它將Excel Application對象傳遞給其構造函數。 構造函數建立一個新的工做簿,並獲取工做簿中的第一個工做表。 而後它建立一個圖表表。 它處理在Application對象以及建立的工做簿,工做簿中的第一個工做表以及它添加到工做簿的圖表中引起的事件。 由於幾個事件做爲參數做爲一個表做爲對象傳遞,因此使用一個名爲ReportEvent-WithSheetParameter的輔助方法來肯定傳遞的工做表的類型,並向控制檯顯示一條消息。

清單4-2  處理激活和停用事件的類

 

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

namespace ActivationAndDeactivation
{
  public class TestEventHandler
  {
    private Excel.Application app;
    private Excel.Workbook workbook;
    private Excel.Worksheet worksheet;
    private Excel.Chart chart;

    public TestEventHandler(Excel.Application application)
    {
      this.app = application;
      workbook = application.Workbooks.Add(Type.Missing);
      worksheet = workbook.Worksheets.get_Item(1)   as Excel.Worksheet;

      chart = workbook.Charts.Add(Type.Missing, Type.Missing,Type.Missing, Type.Missing) as Excel.Chart;

      app.WorkbookActivate += new Excel.AppEvents_WorkbookActivateEventHandler( App_WorkbookActivate);

      ((Excel.WorkbookEvents_Event)workbook).Activate +=  new Excel.WorkbookEvents_ActivateEventHandler(Workbook_Activate);

      app.WorkbookDeactivate += new Excel.AppEvents_WorkbookDeactivateEventHandler(App_WorkbookDeactivate);

      workbook.Deactivate +=  new Excel.WorkbookEvents_DeactivateEventHandler( Workbook_Deactivate);

      app.SheetActivate +=  new Excel.AppEvents_SheetActivateEventHandler( App_SheetActivate);

      workbook.SheetActivate +=  new Excel.WorkbookEvents_SheetActivateEventHandler( Workbook_SheetActivate);

      ((Excel.DocEvents_Event)worksheet).Activate +=  new Excel.DocEvents_ActivateEventHandler(    Worksheet_Activate);

      ((Excel.ChartEvents_Event)chart).Activate +=    new Excel.ChartEvents_ActivateEventHandler(    Chart_Activate);

      app.SheetDeactivate +=   new Excel.AppEvents_SheetDeactivateEventHandler(    App_SheetDeactivate);

      workbook.SheetDeactivate +=  new Excel.WorkbookEvents_SheetDeactivateEventHandler(   Workbook_SheetDeactivate);

      worksheet.Deactivate +=    new Excel.DocEvents_DeactivateEventHandler(    Worksheet_Deactivate);

      chart.Deactivate +=    new Excel.ChartEvents_DeactivateEventHandler(    Chart_Deactivate);

      app.WindowActivate +=   new Excel.AppEvents_WindowActivateEventHandler(   App_WindowActivate);

      workbook.WindowActivate +=     new Excel.WorkbookEvents_WindowActivateEventHandler(   Workbook_WindowActivate);

      app.WindowDeactivate +=  new Excel.AppEvents_WindowDeactivateEventHandler(   App_WindowDeactivate);

      workbook.WindowDeactivate +=   new Excel.WorkbookEvents_WindowDeactivateEventHandler(   Workbook_WindowDeactivate);
    }

    void ReportEventWithSheetParameter(string eventName,object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;

      if (worksheet != null)
      {
        Console.WriteLine(String.Format("{0} ({1})", eventName, worksheet.Name));
      }

      Excel.Chart chart = sheet as Excel.Chart;

      if (chart != null)
      {
        Console.WriteLine(String.Format("{0} ({1})",  eventName, chart.Name));
      }
    }

    void App_WorkbookActivate(Excel.Workbook workbook)
    {
      Console.WriteLine(String.Format( "Application.WorkbookActivate({0})", workbook.Name));
    }

    void Workbook_Activate()
    {
      Console.WriteLine("Workbook.Activate()");
    }

    void App_WorkbookDeactivate(Excel.Workbook workbook)
    {
      Console.WriteLine(String.Format( "Application.WorkbookDeactivate({0})", workbook.Name));
    }

    void Workbook_Deactivate()
    {
      Console.WriteLine("Workbook.Deactivate()");
    }

    void App_SheetActivate(object sheet)
    {
      ReportEventWithSheetParameter( "Application.SheetActivate", sheet);
    }

    void Workbook_SheetActivate(object sheet)
    {
      ReportEventWithSheetParameter( "Workbook.SheetActivate", sheet);
    }

    void Worksheet_Activate()
    {
      Console.WriteLine("Worksheet.Activate()");
    }

    void Chart_Activate()
    {
      Console.WriteLine("Chart.Activate()");
    }

    void App_SheetDeactivate(object sheet)
    {
      ReportEventWithSheetParameter("Application.SheetDeactivate", sheet);
    }

    void Workbook_SheetDeactivate(object sheet)
    {
      ReportEventWithSheetParameter("Workbook.SheetDeactivate", sheet);
    }

    void Worksheet_Deactivate()
    {
      Console.WriteLine("Worksheet.Deactivate()");
    }

    void Chart_Deactivate()
    {
      Console.WriteLine("Chart.Deactivate()");
    }

    void App_WindowActivate(Excel.Workbook workbook,   Excel.Window window)
    {
      Console.WriteLine(String.Format( "Application.WindowActivate({0}, {1})", workbook.Name, window.Caption));
    }

    void Workbook_WindowActivate(Excel.Window window)
    {
      Console.WriteLine(String.Format("Workbook.WindowActivate({0})", window.Caption));
    }

    void App_WindowDeactivate(Excel.Workbook workbook,    Excel.Window window)
    {
      Console.WriteLine(String.Format("Application.WindowDeactivate({0}, {1})",workbook.Name, window.Caption));
    }

    void Workbook_WindowDeactivate(Excel.Window window)
    {
      Console.WriteLine(String.Format("Application.WindowActivate({1})", window.Caption));
    }
  }
}

雙擊和右鍵單擊事件
當雙擊或右鍵單擊工做表或圖表工做表(單擊鼠標右鍵)時,會引起幾個事件。雙擊事件時,雙擊工做表或圖表工做表中單元格的中心。若是雙擊單元格的邊框,則不會引起任何事件。若是您雙擊列標題或行標題,則不會引起任何事件。若是雙擊工做表中的對象(對象模型中的Shape對象)(如嵌入式圖表),則不會引起任何事件。雙擊Excel中的單元格後,Excel將進入編輯模式,該單元格光標顯示在單元格中,容許您鍵入單元格。若是在編輯模式下雙擊單元格,則不會引起任何事件。

當您右鍵單擊工做表或圖表工做表中的單元格時,會發生右鍵單擊事件。當您右鍵單擊列標題或行標題時,還會提示右鍵單擊事件。若是右鍵單擊工做表中的對象,例如嵌入式圖表,則不會引起任何事件。

用於圖表表的右鍵單擊和雙擊事件不會引起應用程序和工做簿對象上的事件。而是直接在Chart對象上引起BeforeDoubleClick和BeforeRightClick事件。

全部右鍵單擊和雙擊事件的名稱中都有「Before」。這是由於Excel在Excel執行默認行爲進行雙擊和右鍵單擊以前提升這些事件,例如,顯示上下文菜單或進入雙擊單元格的編輯模式。這些事件都有一個bool參數,它被一個引用稱爲cancel的參數傳遞,它容許您經過將cancel參數設置爲true來取消Excel的雙擊或右鍵單擊的默認行爲。

許多右鍵單擊和雙擊事件會傳遞一個Range對象做爲參數。 Range對象表示cellit的範圍能夠表示單個單元格或多個單元格。例如,若是選擇多個單元格,而後右鍵單擊所選單元格,則Range對象將傳遞給表示所選單元格的右鍵單擊事件。

雙擊並以各類方式提供右鍵單擊事件,以下所示:

  • 當Excel中任何工做表中的任何單元格被雙擊時,都會引起Application.SheetBeforeDoubleClick。 Excel將做爲雙擊的工做表做爲對象傳遞,雙擊的單元格範圍,以及經過引用傳遞的bool取消參數。您能夠經過事件處理程序將cancel參數設置爲TRue,以防止Excel執行其默認雙擊行爲。這是一個狀況,由於沒有傳遞圖表,工做表做爲對象傳遞確實沒有意義。您將始終將對象轉換爲工做表。
  • Workbook.SheetBeforeDoubleClick是在工做簿上引起的,該工做簿中的單元格已在工做表中雙擊。 Excel傳遞與應用程序級SheetBeforeDoubleClick相同的參數。
  • Worksheet.BeforeDoubleClick是在雙擊工做表上引起的。 Excel經過雙擊單元格範圍和經過引用傳遞的bool取消參數的範圍。事件處理程序能夠將cancel參數設置爲true,以防止Excel執行其默認雙擊行爲。
  • Chart.BeforeDoubleClick在雙擊的圖表表上生成。 Excel將做爲int元素傳遞一個元素ID和兩個稱爲arg1和arg2的參數。經過這三個參數的組合,您能夠肯定雙擊圖表中的哪些元素。 Excel也經過引用經過bool cancel參數。事件處理程序能夠將cancel參數設置爲true,以防止Excel執行其默認雙擊行爲。
  • 只要右鍵單擊Excel中任何工做表中的任何單元格,就會引起Application.SheetBeforeRightClick。 Excel將做爲對象右鍵單擊的工做表,右擊單元格範圍,以及經過引用傳遞的bool cancel參數做爲對象。 cancel參數能夠由事件處理程序設置爲TRue,以防止Excel執行其默認的右鍵單擊行爲。這是一個狀況,由於沒有傳遞圖表,所以工做表做爲對象傳遞確實沒有意義。您將始終將對象轉換爲工做表。
  • Workbook.SheetBeforeRightClick是在一個工做簿上生成的,該工做簿在右側工做表中有一個單元格。 Excel傳遞與應用程序級SheetBeforeRightClick相同的參數。
  • Worksheet.BeforeRightClick是在右鍵單擊的工做表上引起的。 Excel經過右鍵單元格範圍和經過引用傳遞的bool取消參數的範圍。您能夠經過事件處理程序將cancel參數設置爲true,以防止Excel執行其默認的右鍵單擊行爲。
  • Chart.BeforeRightClick是在右鍵單擊的圖表表上生成的。奇怪的是,Excel不會傳遞它傳遞給Chart.BeforeDoubleClickEvent的任何參數。 Excel經過引用傳遞一個bool cancel參數。您能夠經過事件處理程序將cancel參數設置爲true,以防止Excel執行其默認的右鍵單擊行爲。

清單4-3顯示了一個處理全部這些事件的VSTO Workbook類。此代碼假定您已將圖表表添加到工做簿,稱爲Chart1。在VSTO中,當處理由這些對象引起的事件時,您不須要保留對Workbook對象或Worksheet或Chart對象的引用,由於它們已被VSTO項目中生成的項目項目所保留。在處理由Application對象引起的事件時,您須要保留對Application對象的引用,由於它不會保留在VSTO項目的任何位置。

由VSTO生成的ThisWorkbook類派生自具備Excel工做簿對象的全部成員的類,所以能夠經過添加引用此代碼的代碼添加工做簿事件處理程序,如代碼清單4-3所示。咱們可使用this.Application獲取一個Application對象,由於Application是Workbook的一個屬性。由於返回的應用程序對象不被任何其餘代碼保留爲引用,因此咱們必須聲明一個類成員變量來保持此Application對象,以便咱們的事件處理程序能夠正常工做。第1章「辦公編程介紹」更詳細地討論了這個問題。

要獲取咱們VSTO項目中的圖表和工做表,咱們使用VSTO的Globals對象,它可讓咱們進入在其餘項目項目中聲明的類Chart1和Sheet1。咱們沒必要在類成員變量中保存這些對象,由於它們的生命週期與VSTO代碼的生命週期相匹配。

咱們還在清單4-3中聲明瞭兩個幫助函數。一個將做爲對象傳遞的工做錶轉換爲工做表,並返回工做表的名稱。另外一個獲取傳遞給許多事件的Range的地址做爲目標參數。

右鍵單擊事件的處理程序都將引用傳遞的bool cancel參數設置爲true。這將使得Excel不會在右鍵單擊時執行其默認行爲,一般會彈出菜單。

清單4-3 處理雙擊和右鍵單擊事件的VSTO工做簿自定義

using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;

namespace ExcelWorkbook1
{
  public partial class ThisWorkbook
  {
    private Excel.Application app;

    private void ThisWorkbook_Startup(object sender, EventArgs e)
    {
      app = this.Application;

      app.SheetBeforeDoubleClick +=  new Excel.AppEvents_SheetBeforeDoubleClickEventHandler(App_SheetBeforeDoubleClick);

      this.SheetBeforeDoubleClick += new Excel.WorkbookEvents_SheetBeforeDoubleClickEventHandler(ThisWorkbook_SheetBeforeDoubleClick);

      Globals.Sheet1.BeforeDoubleClick += new Excel.DocEvents_BeforeDoubleClickEventHandler(Sheet1_BeforeDoubleClick);

      Globals.Chart1.BeforeDoubleClick += new Excel.ChartEvents_BeforeDoubleClickEventHandler(Chart1_BeforeDoubleClick);

      app.SheetBeforeRightClick += new Excel.AppEvents_SheetBeforeRightClickEventHandler(App_SheetBeforeRightClick);

      this.SheetBeforeRightClick += new Excel.WorkbookEvents_SheetBeforeRightClickEventHandler(ThisWorkbook_SheetBeforeRightClick);

      Globals.Sheet1.BeforeRightClick +=  new Excel.DocEvents_BeforeRightClickEventHandler( Sheet1_BeforeRightClick);

      Globals.Chart1.BeforeRightClick +=  new Excel.ChartEvents_BeforeRightClickEventHandler( Chart1_BeforeRightClick);
    }

    private void ThisWorkbook_Shutdown(object sender, EventArgs e)
    {
    }

    private string RangeAddress(Excel.Range target)
    {
      return target.get_Address(missing, missing,  Excel.XlReferenceStyle.xlA1, missing, missing);
    }

    private string SheetName(object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;
      if (worksheet != null)
        return worksheet.Name;
      else
        return String.Empty;
    }

    void App_SheetBeforeDoubleClick(object sheet,  Excel.Range target, ref bool cancel)
    {
      MessageBox.Show(String.Format("Application.SheetBeforeDoubleClick({0},{1})",SheetName(sheet), RangeAddress(target)));
    }

    void ThisWorkbook_SheetBeforeDoubleClick(object sheet,  Excel.Range target, ref bool cancel)
    {
      MessageBox.Show(String.Format("Workbook.SheetBeforeDoubleClick({0}, {1})",SheetName(sheet), RangeAddress(target)));
    }

    void Sheet1_BeforeDoubleClick(Excel.Range target,  ref bool cancel)
    {
      MessageBox.Show(String.Format( "Worksheet.SheetBeforeDoubleClick({0})",  RangeAddress(target)));
    }

    void Chart1_BeforeDoubleClick(int elementID, int arg1,   int arg2, ref bool cancel)
    {
      MessageBox.Show(String.Format( "Chart.SheetBeforeDoubleClick({0}, {1}, {2})", elementID, arg1, arg2));
    }

    void App_SheetBeforeRightClick(object sheet,  Excel.Range target, ref bool cancel)
    {
      MessageBox.Show(String.Format( "Application.SheetBeforeRightClick({0},{1})",   SheetName(sheet), RangeAddress(target)));
      cancel = true;
    }

    void ThisWorkbook_SheetBeforeRightClick(object sheet,  Excel.Range target, ref bool cancel)
    {
      MessageBox.Show(String.Format(  "Workbook.SheetBeforeRightClick({0},{1})",  SheetName(sheet), RangeAddress(target)));
      cancel = true;
    }

    void Sheet1_BeforeRightClick(Excel.Range target,  ref bool cancel)
    {
      MessageBox.Show(String.Format( "Worksheet.SheetBeforeRightClick({0})",  RangeAddress(target)));
      cancel = true;
    }

    void Chart1_BeforeRightClick(ref bool cancel)
    {
      MessageBox.Show("Chart.SheetBeforeRightClick()");
      cancel = true;
    }

    #region VSTO Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InternalStartup()
    {
      this.Startup += new System.EventHandler(ThisWorkbook_Startup);
      this.Shutdown += new System.EventHandler(ThisWorkbook_Shutdown);
    }

    #endregion

  }
}

可取消事件和事件冒泡
清單4-3提出了一個有趣的問題。當多個對象處理多個級別的BeforeRightClick之類的事件時會發生什麼?清單4-3在Worksheet,Workbook和Application級別處理BeforeRightClick事件。 Excel首先在Worksheet級別爲針對Worksheet級事件註冊的全部代碼引起事件。請記住,其餘加載項也能夠在Excel中處理,也能夠處理Worksheet級事件。您的代碼可能會得到Worksheet.BeforeRightClick事件,以後是其餘一些加載項,也正在處理Worksheet.BeforeRightClick事件。當多個加載項處理相同對象上相同的事件時,您沒法依賴任何肯定的順序來肯定誰將首先獲取事件。所以,不要編寫代碼來依賴任何特定的順序。

在工做表級別提出事件以後,而後在「工做簿」級別,最後在「應用程序」級別引導。對於可取消事件,即便一個事件處理程序將cancel參數設置爲true,事件將繼續提高到其餘事件處理程序。所以,即便清單4-3中的代碼將Sheet1_BeforeRightClick中的cancel參數設置爲true,Excel將繼續在WorkReports的其餘處理程序BeforeRightClick上引起事件,而後再處理Workbook.SheetBeforeRightClick的處理程序,後跟Application.SheetBeforeRightClick的處理程序。

您應該瞭解可取消事件的另外一件事情是,您能夠檢查事件處理程序中的傳入取消參數,以查看最後一個事件處理程序設置爲什麼值。所以,在Sheet1_BeforeRightClick處理程序中,假設沒有其餘代碼處理該事件,傳入的cancel參數將爲false。在ThisWorkbook_SheetBeforeRightClick處理程序中,傳入的cancel參數將爲true,由於最後一個處理程序Sheet1_BeforeRightClick將其設置爲TRue。這意味着,做爲事件經過多個處理程序發生的事件,每一個後續處理程序能夠覆蓋先前處理程序在本示例中取消默認右鍵單擊行爲方面所作的工做。應用程序級處理程序獲得最終的說明,若是同一事件存在多個應用程序級處理程序,則無論事件是否被取消,都是不肯定的,由於沒有規則規定多個應用程序級事件處理程序中的哪一個處理程序首先或最後獲取事件。

計算事件
當從新計算工做表中的公式時,會引起四個事件。當您更改影響到該單元格的公式的單元格或添加或修改公式時,將從新計算工做表:

  • 只要從新計算Excel中的任何工做表,就會引起Application.SheetCalculate。 Excel將做爲對該事件從新計算的對象做爲參數傳遞給該表。 能夠將工做表對象轉換爲工做表或圖表。
  • Workbook.SheetCalculate是在具備從新計算的工做表的工做簿上引起的。 Excel將做爲對該事件從新計算的對象做爲參數傳遞給該表。 能夠將工做表對象轉換爲工做表或圖表。
  • Worksheet.Calculate是在從新計算的工做表上引起的。
  • Calculate是Worksheet對象上的方法和事件的名稱。 因爲此衝突,您將沒法在Visual Studio的與Worksheet對象關聯的屬性,事件和方法的彈出菜單中看到「計算」事件。 此外,當您嘗試處理此事件時,會在編譯時顯示警告。 要使Visual Studio的彈出菜單工做而且警告消失,能夠將Worksheet對象轉換爲DocEvents_Event接口,如清單4-4所示。
  • Chart.Calculate在已更新的圖表表上生成,由於其引用的數據已更改。直到圖表被強制從新繪製,若是圖表當前不可見,由於它沒有被選中或顯示在本身的窗口中,則不會發生此事件,直到圖表可見爲止,事件纔會被提高。

清單4-4顯示了一個處理全部計算事件的控制檯應用程序。控制檯應用程序建立一個新的工做簿,獲取工做簿中的第一個工做表,並在工做簿中建立一個圖表。控制檯應用程序還處理建立的工做簿的關閉事件,以使工做簿關閉時控制檯應用程序退出。獲取Excel以提升工做表和工做簿計算事件,將一些值和公式添加到工做簿中的第一個工做表。要升高Chart對象的Calculate事件,您能夠右鍵單擊處理事件的圖表表,而後從彈出菜單中選擇Source Data。而後,單擊數據範圍文本框右側的按鈕,切換到第一個工做表,而後爲要顯示的圖表表選擇一系列值。當您更改這些值並切換回圖表表時,圖表的「計算」事件將被提高。

清單4-4 處理計算事件的控制檯應用程序

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

namespace ConsoleApplication
{
  class Program
  {
    static private Excel.Application app;
    static private Excel.Workbook workbook;
    static private Excel.Worksheet worksheet;
    static private Excel.Chart chart;
    static bool exit = false;

    static void Main(string[] args)
    {
      app = new Excel.Application();
      app.Visible = true;

      workbook = app.Workbooks.Add(Type.Missing);
      worksheet = workbook.Sheets.get_Item(1) as Excel.Worksheet;
      chart = workbook.Charts.Add(Type.Missing, Type.Missing,
        Type.Missing, Type.Missing) as Excel.Chart;

      app.SheetCalculate += 
        new Excel.AppEvents_SheetCalculateEventHandler(
        App_SheetCalculate);

      workbook.SheetCalculate += 
        new Excel.WorkbookEvents_SheetCalculateEventHandler(
        Workbook_SheetCalculate);

      ((Excel.DocEvents_Event)worksheet).Calculate += 
        new Excel.DocEvents_CalculateEventHandler(
        Worksheet_Calculate);

      chart.Calculate += 
        new Excel.ChartEvents_CalculateEventHandler(
        Chart_Calculate);

      workbook.BeforeClose += 
        new Excel.WorkbookEvents_BeforeCloseEventHandler(
        Workbook_BeforeClose);

      while (exit == false)
        System.Windows.Forms.Application.DoEvents();

      app.Quit();
    }

    static void Workbook_BeforeClose(ref bool cancel)
    {
      exit = true;
    }

    static string SheetName(object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;

      if (worksheet != null)
      {
        return worksheet.Name;
      }

      Excel.Chart chart = sheet as Excel.Chart;
      if (chart != null)
      {
        return chart.Name;
      }

      return String.Empty;
    }

    static void App_SheetCalculate(object sheet)
    {
      Console.WriteLine(String.Format(
        "Application.SheetCalculate({0})",
        SheetName(sheet)));
    }

    static void Workbook_SheetCalculate(object sheet)
    {
      Console.WriteLine(String.Format(
        "Workbook.SheetCalculate({0})", SheetName(sheet)));
    }

    static void Worksheet_Calculate()
    {
      Console.WriteLine("Worksheet.Calculate()");
    }

    static void Chart_Calculate()
    {
      Console.WriteLine("Chart.Calculate()");
    }
  }
}

change事件
當工做表中更改單元格或單元格範圍時,Excel會引起多個事件。必須由用戶編輯要更改事件的單元格來更改單元格。當單元格連接到外部數據而且因爲從外部數據刷新單元格而改變時,也能夠引起更改事件。因爲從新計算更改單元格時,更改事件不會引起。當用戶更改單元格的格式時,不會改變它們,而不更改單元格的值。當用戶正在編輯單元格並處於單元格編輯模式時,在用戶退出單元格編輯模式以前,不改變事件,直到離開單元格或按Enter鍵:

當用戶更改任何工做簿中的單元格或單元格範圍或從外部數據更新時,將引起Application.SheetChange。 Excel將做爲更改發生的對象做爲對象傳遞給此事件的參數。您能夠隨時將工做表參數轉換爲工做表,由於不會爲圖表工做表提供更改事件。 Excel還會將範圍做爲更改單元格範圍的參數。

當工做簿中的單元格或單元格範圍由用戶更改或從外部數據更新時,Workbook.SheetChange將在工做簿上引起。 Excel將做爲更改發生的對象做爲對象傳遞給此事件的參數。您能夠隨時將工做表參數轉換爲工做表,由於不會爲圖表工做表提供更改事件。 Excel還會將範圍做爲更改單元格範圍的參數。

Worksheet.Change在工做表中引起,當工做表中的單元格或單元格範圍由用戶更改或從外部數據更新時。 Excel將範圍做爲更改單元格範圍的參數。

清單4-5顯示了一個處理全部Change事件的類。它將Excel Application對象傳遞給其構造函數。構造函數建立一個新的工做簿,並獲取工做簿中的第一個工做表。它處理在應用程序對象,工做簿和工做簿中的第一個工做表中引起的事件。

清單4-5 處理變動事件的班級

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

namespace ChangeEvents
{
  public class ChangeEventHandler
  {
    private Excel.Application app;
    private Excel.Workbook workbook;
    private Excel.Worksheet worksheet;
    object missing = System.Type.Missing;

    public ChangeEventHandler(Excel.Application application)
    {
      this.app = application;
      workbook = app.Workbooks.Add(missing);
      worksheet = workbook.Worksheets.get_Item(1) as Excel.Worksheet;

      app.SheetChange += 
        new Excel.AppEvents_SheetChangeEventHandler(
        App_SheetChange);

      workbook.SheetChange += 
        new Excel.WorkbookEvents_SheetChangeEventHandler(
        Workbook_SheetChange);

      worksheet.Change += 
        new Excel.DocEvents_ChangeEventHandler(
        Worksheet_Change);
    }

    // Change events only pass worksheets, never charts.
    private string SheetName(object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;
      return worksheet.Name;
    }

    private string RangeAddress(Excel.Range target)
    {
      return target.get_Address(missing, missing, 
        Excel.XlReferenceStyle.xlA1, missing, missing);
    }

    void App_SheetChange(object sheet, Excel.Range target)
    {
      Console.WriteLine(String.Format(
        "Application.SheetChange({0},{1})",
        SheetName(sheet), RangeAddress(target)));
    }

    void Workbook_SheetChange(object sheet, Excel.Range target)
    {
      Console.WriteLine(String.Format(
        "Workbook.SheetChange({0},{1})",
        SheetName(sheet), RangeAddress(target)));
    }

    void Worksheet_Change(Excel.Range target)
    {
      Console.WriteLine(String.Format(
        "Worksheet.Change({0})",
        RangeAddress(target)));
    }
  }
}

Hyperlink 事件

單擊單元格中的超連接時,Excel會引起多個事件。你可能會認爲這個事件並非頗有趣,可是您能夠將其用做在您的自定義中調用操做的簡單方法。訣竅是建立一個不執行任何操做的超連接,而後處理FollowHyperlink事件並在該事件處理程序中執行該操做。

要建立不執行任何操做的超連接,請右鍵單擊要放置超連接的單元格,而後選擇HyperLink。對於咱們的例子,咱們選擇單元格C3。在出現的對話框中,單擊對話框左側的「放置在此文檔」按鈕(參見圖4-2)。在「鍵入」單元格參考文本框中,鍵入C3或要添加超連接的單元格的引用。執行此操做的邏輯是,在單擊超連接以後,以及事件處理程序運行後,Excel將選擇C3連接到的單元格。若是您選擇用戶單擊的單元格之外的單元格,則選擇將移動,這是使人困惑的。因此咱們有效地將單元格連接到自身,建立一個無關的連接。在文本顯示文本框中,鍵入您要在單元格中顯示的名稱命令的名稱。在這個例子中,咱們命名爲Print。

圖4-2 插入超連接對話框

單擊超連接時會引起如下事件:

當在Excel中打開的任何工做簿中單擊超連接時,將引起Application.SheetFollowHyperlink。 Excel將超連接對象做爲參數傳遞給此事件。超連接對象提供了有關被點擊的超連接的信息。

當在該工做簿中單擊超連接時,Workbook.SheetFollowHyperlink在工做簿上引起。 Excel將超連接對象做爲參數傳遞給此事件。超連接對象提供了有關被點擊的超連接的信息。

當工做表中單擊超連接時,Worksheet.FollowHyperlink在工做表上引起。 Excel將超連接對象做爲參數傳遞給此事件。超連接對象提供了有關被點擊的超連接的信息。

清單4-6顯示了工做簿項目項目的VSTO自定義類。該類假定工做簿中有一個Print超連接,如圖4-2所示建立。應用程序或工做簿級超級連接事件的處理程序中的自定義功能不起做用,但會記錄到控制檯窗口。 Worksheet級處理程序檢測到單擊一個名爲Print的超連接,並調用Workbook對象上的PrintOut方法以打印工做簿。

清單4-6 處理超連接事件的VSTO工做簿定製

using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;

namespace ExcelWorkbook1
{
  public partial class ThisWorkbook
  {
    private Excel.Application app;

    private void ThisWorkbook_Startup(object sender, EventArgs e)
    {
      app = this.Application;

      app.SheetFollowHyperlink += 
        new Excel.AppEvents_SheetFollowHyperlinkEventHandler(
        App_SheetFollowHyperlink);

      this.SheetFollowHyperlink += 
        new Excel.WorkbookEvents_SheetFollowHyperlinkEventHandler(
        Workbook_SheetFollowHyperlink);

      Globals.Sheet1.FollowHyperlink += 
        new Excel.DocEvents_FollowHyperlinkEventHandler(
        Sheet_FollowHyperlink);     
    }

    private string SheetName(object sheet)
    {
      Excel.Worksheet worksheet = sheet as Excel.Worksheet;
      if (worksheet != null)
        return worksheet.Name;
      else
        return String.Empty;
    }

    void App_SheetFollowHyperlink(object sheet, Excel.Hyperlink target)
    {
      MessageBox.Show(String.Format(
        "Application.SheetFollowHyperlink({0},{1})",
        SheetName(sheet), target.Name));
    }

    void Workbook_SheetFollowHyperlink(object sheet, Excel.Hyperlink target)
    {
      MessageBox.Show(String.Format(
        "Workbook.SheetFollowHyperlink({0},{1})",
        SheetName(sheet), target.Name));
    }

    void Sheet_FollowHyperlink(Excel.Hyperlink target)
    {
      if (target.Name == "Print")
      {
        this.PrintOut(missing, missing, missing, missing,
          missing, missing, missing, missing);
      }
    }

    private void ThisWorkbook_Shutdown(object sender, EventArgs e)
    {
    }

    #region VSTO Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InternalStartup()
    {
      this.Startup += new System.EventHandler(ThisWorkbook_Startup);
      this.Shutdown += new System.EventHandler(ThisWorkbook_Shutdown);
    }

    #endregion

  }
}

選擇change事件
當所選單元格或單元格更改時,或當圖表表中所選圖表元素更改時,將在Chart.Select事件的狀況下發生選擇更改事件:

當Excel中任何工做表中選定的單元格或單元格更改時,將引起Application.SheetSelectionChange。 Excel將選擇更改的工做表傳遞給事件處理程序。可是,事件處理程序的參數鍵入對象,所以若是要使用Worksheet的屬性或方法,則必須將其轉換爲工做表。您保證始終可以將參數轉換爲工做表,由於在Chart上進行選擇更改時,不會引起SheetSelectionChange事件。 Excel也會傳遞新選擇的單元格範圍。

當工做簿中選定的單元格或單元格更改時,Workbook.SheetSelectionChange在Workbook上生成。 Excel做爲對象將選擇更改的工做表傳遞給對象。您能夠隨時將工做表對象轉換爲工做表,由於不會爲圖表工做表上的選擇更改引起此事件。 Excel還會傳遞一個Range做爲新選擇的單元格範圍。

Worksheet.SelectionChange在工做表中引起,只要該工做表中選定的單元格更改。 Excel傳遞一個範圍,做爲新選擇的單元格範圍。

當圖表表中的選定元素髮生變化時,圖表會在圖表上生成。 Excel將做爲int元素傳遞一個元素ID和兩個稱爲arg1和arg2的參數。經過這三個參數的組合,您能夠肯定選擇圖表的哪一個元素。

 注:Select是Chart對象上的方法和事件的名稱。 因爲此衝突,您將不會在Visual Studio的與圖表對象關聯的屬性,事件和方法的彈出菜單中看到Select事件。 此外,當您嘗試處理此事件時,會在編譯時顯示警告。 要使Visual Studio的彈出菜單工做,而且警告消失,您能夠將Chart對象轉換爲ChartEvents_Events界面,如清單4-2所示。

 

WindowResize事件
調整工做簿窗口大小時會引起WindowResize事件。只有當工做簿窗口未最大化才能填滿Excel的外部應用程序窗口(見圖4-3)時,纔會引起這些事件。若是調整非最大化的工做簿窗口大小或最小化工做簿窗口,則會引起事件。調整大小並最小化外部Excel應用程序窗口時,不會調整大小事件。

  • 當任何非最大化的工做簿窗口被調整大小或最小化時,將引起Application.WindowResize。 Excel將與調整大小或最小化的窗口相對應的Window對象做爲參數傳遞給此事件。 Excel還將做爲參數影響的Workbook對象傳遞給此事件。
  • 當與該工做簿關聯的非最大化窗口調整大小或最小化時,Workbook.WindowResize在Workbook上引起。 Excel將做爲參數調整大小或最小化的窗口傳遞給此事件。


圖4-3 僅當工做簿窗口未最大化以填充應用程序窗口時,纔會引起Window Resize事件

加載項安裝和卸載事件
經過從「文件」菜單中選擇「另存爲」,而後選擇Microsoft Office Excel加載項做爲所需的格式,可將工做簿保存爲特殊的附加格式(XLA文件)。而後將該工做簿保存到用戶文檔和設置目錄下的Application Data \ Microsoft \ AddIns目錄中。當您從「工具」菜單中選擇「加載項」時,它將顯示在可用的加載項列表中。當您單擊複選框以啓用加載項時,工做簿加載爲隱藏狀態,並引起Application.AddinInstall事件。當用戶單擊複選框以禁用加載項時,將引起Application.AddinUninstall事件。

雖然您理論上能夠將VSTO定製的工做簿保存爲XLA文件,但Microsoft不支持此方案,由於許多VSTO功能(如「文檔操做」任務窗格和「智能標記」)在工做簿保存爲XLA文件時不起做用。

XML導入和導出事件
Excel支持自定義XML數據文件的導入和導出,容許您使用XML模式並將其映射到工做簿中的單元格。而後能夠將這些單元格導出或導入到符合映射模式的XML數據文件。 Excel在引入或導出XML文件以前和以後引起應用程序和工做簿對象上的事件,從而容許開發人員進一步自定義和控制此功能。第21章「使用Excel中的XML」詳細討論了Excel的XML映射功能。

關閉事件以前
Excel在工做簿關閉以前引起事件。這些事件是給你的代碼一個機會來防止關閉工做簿。 Excel將bool cancel參數傳遞給事件。若是事件處理程序將cancel參數設置爲true,則工做簿的待處理關閉將被取消,而且工做簿保持打開狀態。

這些事件不能用於肯定工做簿是否實際關閉。另外一個事件處理程序可能會在事件處理程序以後運行,例如,另外一個加載項中的事件處理程序,該事件處理程序可能將cancel參數設置爲true,從而防止工做簿關閉。此外,若是用戶更改了工做簿,而且在工做簿關閉時提示保存更改,則用戶能夠單擊「取消」按鈕,致使工做簿保持打開狀態。

若是您只須要在工做簿實際關閉時運行代碼,則VSTO會提供一個關閉事件,直到全部其餘事件處理程序和用戶都容許關閉工做簿爲止。

  • Application.WorkbookBeforeClose在任何工做簿關閉以前被提出,使事件處理程序有機會阻止工做簿關閉。 Excel傳遞即將關閉的Workbook對象。 Excel也經過引用bool取消參數。取消參數能夠由事件處理程序設置爲TRue,以防止Excel關閉工做簿。
  • Workbook.BeforeClose是在即將關閉的工做簿上引起的,給予事件處理程序阻止工做簿關閉的機會。 Excel經過引用bool取消參數。您能夠經過事件處理程序將cancel參數設置爲true,以防止Excel關閉工做簿。

 

print前事件
Excel在打印工做簿以前引起事件。當用戶從文件菜單中選擇打印或打印預覽或按打印工具欄按鈕時,會引起這些事件。 Excel將bool cancel參數傳遞給事件。若是事件處理程序將cancel參數設置爲true,則工做簿的待處理打印將被取消,而且不會顯示打印對話框或打印預覽視圖。您可能想要這樣作,由於您要將Excel的默認打印行爲替換爲您本身的某些自定義打印行爲。

這些事件不能用於肯定工做簿是否實際打印。另外一個事件處理程序可能會在您的事件處理程序以後運行,並阻止打印工做簿。用戶還能夠按「打印」對話框中的「取消」按鈕中止打印。

Application.WorkbookBeforePrint在任何工做簿打印或打印預覽以前被提高,使得事件處理程序有機會在打印工做簿以前更改工做簿或更改默認打印行爲。 Excel將做爲要打印的工做簿的參數傳遞。 Excel也經過引用bool取消參數。事件處理程序能夠將cancel參數設置爲true,以防止Excel執行其默認打印行爲。

Workbook.BeforePrint是在要打印或打印預覽的工做簿上提出的,給予事件處理程序一次更改打印工做簿或更改默認打印行爲的機會。 Excel經過引用bool取消參數。您能夠經過事件處理程序將cancel參數設置爲true,以防止執行其默認打印行爲。

save前事件
在保存工做簿以前,Excel會引起可取消事件,容許您在保存文檔以前執行一些自定義操做。當用戶選擇「保存」,「另存爲」或「另存爲網頁」命令時,會引起這些事件。當用戶關閉已修改的工做簿並在出現提示時選擇保存,也會引起它們。 Excel將bool cancel參數傳遞給事件。若是事件處理程序將cancel參數設置爲true,則保存將被取消,而且不會顯示保存對話框。您可能想要這樣作,由於您要將Excel的默認保存行爲替換爲您本身的某些自定義保存行爲。

這些事件不能用於肯定工做簿是否實際上將被保存。另外一個事件處理程序可能在您的事件處理程序以後運行,並阻止保存工做簿。用戶還能夠在保存對話框中按取消中止工做簿的保存。

在保存任何工做簿以前,將引起Application.WorkbookBeforeSave,使事件處理程序有機會阻止或覆蓋工做簿的保存。 Excel做爲參數即將傳入即將被保存的工做簿。 Excel還傳遞一個bool saveAsUI參數,該參數告知事件處理程序是否選擇了Save或Save As。 Excel也經過引用bool取消參數。您能夠經過事件處理程序將cancel參數設置爲TRue,以防止Excel執行其默認保存行爲。

Workbook.BeforeSave是在即將被保存的工做簿上提出的,給事件處理程序一個機會來防止或覆蓋工做簿的保存。 Excel傳遞一個bool saveAsUI參數,它指示事件處理程序是否選擇了Save或Save As。 Excel經過引用bool取消參數。您能夠經過事件處理程序將cancel參數設置爲true,以防止Excel執行其默認保存行爲。

open事件
當打開工做簿或從模板或現有文檔建立新工做簿時,Excel會引起事件。若是建立了新的空白工做簿,則會引起Application.WorkbookNew事件。

當打開任何工做簿時,將引起Application.WorkbookOpen。 Excel將做爲參數打開的工做簿傳遞給此事件。建立新的空白工做簿時,不會引起此事件。提出Application.WorkbookNew事件。

Workbook.Open在打開時在工做簿上提出。

清單4-7顯示了一個處理BeforeClose,BeforePrint,BeforeSave和Open事件的控制檯應用程序。它在BeforeSave和BeforePrint處理程序中將cancel參數設置爲TRue,以防止保存和打印工做簿。

清單4-7 處理關閉,打印,保存和打開事件的控制檯應用程序

 

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

namespace ConsoleApplication
{
  class Program
  {
    static private Excel.Application app;
    static private Excel.Workbook workbook;
    static private bool exit = false;

    static void Main(string[] args)
    {
      app = new Excel.Application();
      app.Visible = true;

      workbook = app.Workbooks.Add(Type.Missing);

      app.WorkbookBeforeClose += 
        new Excel.AppEvents_WorkbookBeforeCloseEventHandler(
        App_WorkbookBeforeClose);

      workbook.BeforeClose += 
        new Excel.WorkbookEvents_BeforeCloseEventHandler(
        Workbook_BeforeClose);

      app.WorkbookBeforePrint += 
        new Excel.AppEvents_WorkbookBeforePrintEventHandler(
        App_WorkbookBeforePrint);

      workbook.BeforePrint += 
        new Excel.WorkbookEvents_BeforePrintEventHandler(
        Workbook_BeforePrint);

      app.WorkbookBeforeSave += 
        new Excel.AppEvents_WorkbookBeforeSaveEventHandler(
        App_WorkbookBeforeSave);

      workbook.BeforeSave += 
        new Excel.WorkbookEvents_BeforeSaveEventHandler(
        Workbook_BeforeSave);

      app.WorkbookOpen += 
        new Excel.AppEvents_WorkbookOpenEventHandler(
        App_WorkbookOpen);

      while (exit == false)
        System.Windows.Forms.Application.DoEvents();

      app.Quit();
    }

    static void App_WorkbookBeforeClose(Excel.Workbook workbook, 
      ref bool cancel)
    {
      Console.WriteLine(String.Format(
        "Application.WorkbookBeforeClose({0})",
        workbook.Name));
    }

    static void Workbook_BeforeClose(ref bool cancel)
    {
      Console.WriteLine("Workbook.BeforeClose()");
      exit = true;
    }

    static void App_WorkbookBeforePrint(Excel.Workbook workbook, 
      ref bool cancel)
    {
      Console.WriteLine(String.Format(
        "Application.WorkbookBeforePrint({0})",
        workbook.Name));
      cancel = true; // Don't allow printing
    }

    static void Workbook_BeforePrint(ref bool cancel)
    {
      Console.WriteLine("Workbook.BeforePrint()");
      cancel = true; // Don't allow printing
    }

    static void App_WorkbookBeforeSave(Excel.Workbook workbook, 
      bool saveAsUI, ref bool cancel)
    {
      Console.WriteLine(String.Format(
        "Application.WorkbookBeforeSave({0},{1})",
        workbook.Name, saveAsUI));
      cancel = true; // Don't allow saving
    }

    static void Workbook_BeforeSave(bool saveAsUI, ref bool cancel)
    {
      Console.WriteLine(String.Format(
        "Workbook.BeforePrint({0})",
        saveAsUI));
      cancel = true; // Don't allow saving
    }

    static void App_WorkbookOpen(Excel.Workbook workbook)
    {
      Console.WriteLine(String.Format(
        "Appplication.WorkbookOpen({0})",
        workbook.Name));
    }
  }
}

工具欄和菜單事件
運行代碼的常見方法是將自定義工具欄按鈕或菜單項添加到Excel,並處理由該按鈕或菜單項引起的點擊事件。 工具欄和菜單欄都由Office對象模型中的相同對象表示,即一個名爲CommandBar的對象。 CommandBar相關對象的層次結構如圖4-4所示。 Application對象具備CommandBars的集合,它們表示主菜單欄和Excel中的全部可用工具欄。 您能夠經過從「工具」菜單中選擇「自定義」來查看Excel中的全部可用工具欄。


圖4-4  CommandBar對象的層次結構

 

經過添加對Microsoft Office 11.0對象庫PIA(office.dll)的引用,CommandBar對象可用於您的應用程序。 CommandBar對象位於Microsoft.Office.Core命名空間中。

CommandBar具備CommandBarControls的集合,它包含CommandBarControl類型的對象。 CommandBarControl一般能夠轉換爲CommandBarButton,CommandBarPopup或CommandBarComboBox。也可能有一個CommandBarControl不能轉換爲其餘類型之一,例如,它只是一個CommandBarControl,不能轉換爲CommandBarButton,CommandBarPopup或CommandBarComboxBox。

清單4-8顯示了一些代碼,它遍歷Excel中可用的全部CommandBars。代碼顯示每一個CommandBar和相關CommandBarControls的名稱或標題。當清單4-8獲取到CommandBarControl時,它首先檢查它是否爲CommandBarButton,CommandBarComboBox或CommandBarPopup,而後轉換爲相應的對象。若是不是任何這些對象類型,代碼將使用CommandBarControl屬性。請注意,CommandBarPopup具備返回CommandBarControls集合的Controls屬性。咱們的代碼使用遞歸來迭代與CommandBarPopup控件相關聯的CommandBarControls集合。

清單4-8 控制檯應用程序,迭代Excel中的全部CommandBars和CommandBarControls

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

namespace ConsoleApplication
{
  class Program
  {
    static private Excel.Application app;

    static void Main(string[] args)
    {
      app = new Excel.Application();
      Office.CommandBars bars = app.CommandBars;

      foreach (Office.CommandBar bar in bars)
      {
        Console.WriteLine(String.Format(
          "CommandBar: {0}", bar.Name));
        DisplayControls(bar.Controls, 1);
      }

      Console.ReadLine();
    }

    static void DisplayControls(Office.CommandBarControls ctls, 
      int indentNumber)
    {
      System.Text.StringBuilder sb = new System.Text.StringBuilder();
      sb.Append(' ', indentNumber);

      foreach (Office.CommandBarControl ctl in ctls)
      {
        Office.CommandBarButton btn = ctl as Office.CommandBarButton;
        Office.CommandBarComboBox box = ctl as Office.CommandBarComboBox;
        Office.CommandBarPopup pop = ctl as Office.CommandBarPopup;

        if (btn != null)
        {
          sb.Append("CommandBarButton: ");
          sb.Append(btn.Caption);
          Console.WriteLine(sb.ToString());
        }
        else if (box != null)
        {
          sb.Append("CommandBarComboBox: ");
          sb.Append(box.Caption);
          Console.WriteLine(sb.ToString());
        }
        else if (pop != null)
        {
          DisplayControls(pop.Controls, indentNumber + 1);
        }
        else
        {
          sb.Append("CommandBarControl: ");
          sb.Append(ctl.Caption);
          Console.WriteLine(sb.ToString());
        }
      }
    }
  }
}

Excel在CommandBar,CommandBarButton和CommandBarComboBox對象上引起了幾個事件:

CommandBar.OnUpdate在CommandBar或相關CommandBarControls發生任何更改時引起。此事件頻繁出現,甚至能夠在Excel中進行選擇更改時加註。處理此事件可能會減慢Excel,所以您應該謹慎處理此事件。

CommandBarButton.Click是在單擊的CommandBarButton上引起的。 Excel將做爲參數單擊的CommandBarButton傳遞給此事件。它也經過參考傳遞bool cancelDefault參數。事件處理程序能夠將cancelDefault參數設置爲true,以防止Excel執行與該按鈕相關聯的默認操做。例如,您能夠處理現有按鈕(例如打印按鈕)的此事件。經過將cancelDefault設置爲TRue,您能夠防止Excel在用戶單擊按鈕時執行其默認打印行爲,而將其替換爲您本身的行爲。

CommandBarComboBox.Change在CommandBarComboBox上引起,其文本值已更改,由於用戶從下拉列表中選擇了一個選項,或者因爲用戶直接在組合框中鍵入了新值。 Excel將做爲參數更改的CommandBarComboBox傳遞給此事件。

清單4-9顯示了一個建立CommandBar,CommandBarButton和CommandBarComboBox的控制檯應用程序。它處理CommandBarButton.Click事件以退出應用程序。它還顯示在控制檯窗口中對CommandBarComboBox所作的更改。 CommandBar,CommandBarButton和CommandBarComboBox臨時添加;當應用程序退出時,Excel將自動刪除它們。這經過將true傳遞給CommandBarControls.Add方法的最後一個參數來完成。

清單4-9 添加CommandBar和CommandBarButton的控制檯應用程序

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

namespace ConsoleApplication
{
  class Program
  {
    static private Excel.Application app;
    static bool close = false;
    static Office.CommandBarButton btn;
    static Office.CommandBarComboBox box;
    static object missing = Type.Missing;

    static void Main(string[] args)
    {
      app = new Excel.Application();
      app.Visible = true;

      Office.CommandBars bars = app.CommandBars;
      Office.CommandBar bar = bars.Add("My Custom Bar", missing, 
        missing, true);
      bar.Visible = true;

      btn = bar.Controls.Add(Office.MsoControlType.msoControlButton, 
        missing, missing, missing, true) as Office.CommandBarButton;
      btn.Click += 
        new Office._CommandBarButtonEvents_ClickEventHandler(
        Btn_Click);

      btn.Caption = "Stop Console Application";
      btn.Tag = "ConsoleApplication.btn";
      btn.Style = Office.MsoButtonStyle.msoButtonCaption;

      box = bar.Controls.Add(
        Office.MsoControlType.msoControlComboBox, missing, 
        missing, missing, true) as Office.CommandBarComboBox;
      box.AddItem("Choice 1", 1);
      box.AddItem("Choice 2", 2);
      box.AddItem("Choice 3", 3);
      box.Tag = "ConsoleApplication.box";
      box.Change += 
        new Office._CommandBarComboBoxEvents_ChangeEventHandler(
        Box_Change);

      while (close == false)
        System.Windows.Forms.Application.DoEvents();
    }

    static void Btn_Click(Office.CommandBarButton ctrl, 
      ref bool cancelDefault)
    {
      close = true;
    }

    static void Box_Change(Office.CommandBarComboBox ctrl)
    {
      Console.WriteLine("Selected " + ctrl.Text);
    }
  }
}

其餘事件
表4-1列出了Excel對象模型中的其餘一些較不經常使用的事件。 圖4-17顯示了本表中說起的信封UI。

表4-1  其餘Excel事件

圖4-5 Excel中的信封界面

 

Visual Studio 2005 Office for Office中的事件
在Visual Studio 2005 Tools for Office對象中找到幾個事件,這些事件在單獨使用Excel PIA時找不到。 表4-2列出了這些事件。 幾乎全部這些都是來自不一樣對象從新提出的Excel PIA的事件。 例如,在Excel PIA中,在Range對象上沒有BeforeDoubleClick事件,事實上,在Range對象上沒有任何事件。 在VSTO中,VSTO定義的兩個表示Range(NamedRange和XMLMappedRange)的對象都有一個BeforeDoubleClick事件。 VSTO將BeforeDoubleClick事件添加到這些對象中,並在每當引起Worksheet.BeforeDoubleClick事件並傳遞與給定的NamedRange或XMLMappedRange對象匹配的Range對象時引起事件。

表4-2  在VSTO中添加的事件

VSTO更改事件的另外一種狀況是激活事件的命名和Worksheet對象上的Select事件。 這兩個事件名稱都與Worksheet上的方法名稱衝突。 爲了不這種衝突,VSTO將這些事件重命名爲ActivateEvent和SelectEvent。

還有一些新事件,例如在VSTO項目主機項目(如Workbook,Worksheet和ChartSheet)上引起的啓動和關閉事件。 ListObject也有數據綁定的幾個新事件。

 

結論
本章已經對Excel對象模型中對象引起的各類事件進行了研究。 本章還介紹了Excel對象模型中的一些主要對象,如應用程序,工做簿和文檔。 您還學習了VSTO對象在Excel中引起的其餘事件。

第5章「使用Excel對象」更詳細地討論如何使用Excel對象模型中的主要對象。

相關文章
相關標籤/搜索