Visual Studio Tools for Office: Using C# with Excel, Word, Outlook, and InfoPath【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对象模型中的主要对象。

相关文章
相关标签/搜索