对于打算利用用于 Microsoft Office 系统的 Microsoft Visual Studio 工具的开发人员和想要仅仅使用 COM 自动化来控制 Microsoft Office Excel 2003 应用程序的人来说,他们需要能够与 Excel 对象模型提供的对象进行交互。Excel 提供了数百个您可能想要与之交互的对象,但是您可以通过集中于这些可用的对象的一个非常小的子集来获得对象模型方面的一个良好的开端。这些对象包括:
Application
Workbook
Worksheet
Range
尽管不可能具体地量化,但是当您使用 Excel 时,大部分的工作将以这四个类以及它们的成员为核心。在本文档中,您将学会如何利用这些类中的每一个,另外,我们还会向您介绍每个类的一些属性、方法和事件。您也将会看到一些可以尝试的示例,这些示例演示了每个对象的一些功能。
提示 总的说来,使用 Microsoft Visual Basic .NET 的开发人员在使用 Microsoft Office 对象时,相比于使用 Microsoft Visual C# 的开发人员来说要轻松得多,一个重要的原因在于:Visual Basic for Applications (VBA) 方法常包含可选参数,而 Visual Basic .NET 支持可选参数。C# 开发人员将发现他们必须为每个可选方法参数提供一个值,而 Visual Basic .NET 开发人员可以简单地使用命名的参数只提供他们需要的值。另外,C# 不支持带有参数的属性(除了索引器以外),然而,许多 Excel 属性可以接受参数。您将会发现,对于 C# 开发人员来说,一些属性(例如可用于 VBA 和 Visual Basic .NET 的 Application.Range 属性)需要单独的访问器方法(get_Range 方法替换了 Range 属性)。在本文中,请注意这样的语言之间存在的差异。
在大多数情况下,您将会发现 Excel 对象模型直接模拟其用户界面。不难猜想,Application 对象提供了封装整个应用程序的包装,并且每个 Workbook 对象都包含 Worksheet 对象的一个集合。其中,表示单元格的主要的抽象是 Range 对象,这使得您能够使用单个单元格或者单元格组。
下面的每个部分都将描述一个主要的 Excel 对象,挑选对象的特定成员来进行演示。由于可供研究的对象有数百个,所以不可能在这里对所有的对象进行深入的探讨:您将会得到足够的对象模型方面的知识来开始您的工作,并且可以使用 Excel 联机帮助来获得更详细的信息。
提示 在本文中,您将看到 DirectCast 和 CType 方法的许多用途。其原因在于示例项目有自己的 Option Strict 设置 — 这意味着 Visual Basic .NET 需要严格的类型转换。许多 Excel 方法和属性返回 Object 类型或者依赖于晚期绑定:例如,Application.ActiveSheet 属性返回 Object,而不是您猜想的 Worksheet。因此,为了尽可能地进行严格的类型转换,示例启用了 Option Strict,并且显式地处理每种类型转换。(如果不在 Visual Basic .NET 中使用 Option Strict,您编写的代码可能编译良好,但是会在运行时失败。这就是 Option Strict 意义所在 — 它大大减少了非法转换在运行时产生异常的可能性)。如果您是一名正在阅读本文档的 C# 开发人员,您可能会赞赏这种决定。
这本白皮书引用了示例项目 ExcelObjectModel.sln。这个项目包含一个 Excel 工作簿以及相关的 Visual Basic .NET 代码。并不是本文中展示的每个示例都出现在这个示例项目中,但是需要多于一行或两行代码的任何示例都放到了工作簿中,并且在项目内设置了调用代码的超级链接。
提示 在这篇篇幅有限的文章中,不可能对每个对象或成员进行注解。甚至不可能提及这些类中的一小部分。研究任何大型对象模型最好的工具是 Object Browser 窗口,其中,您可以找到每个类的列表、以及该类的成员。您将会发现,在本文档中讨论的许多类成员适用于许多其他不同的类:例如,在 Sheets 集合的上下文中讨论的 PrintOut 方法同样适用于 Chart、Worksheet、Range 和其他的对象。本文档旨在让您知道什么是可用的,而剩下的东西要靠您好奇的本性来挖掘了。
Excel Application 对象代表 Excel 应用程序本身。这可能听起来是显而易见的,但是 Application 对象公开了大量关于运行时应用程序、应用到该实例的一些选项、以及在该实例内打开的当前用户对象的信息。Application 对象提供了许多成员,其中的许多成员您从来都不需要研究,但是其他的一些成员对于您的应用程序的行为是否正确至关紧要。您可以将这些成员分为以下种类:
在 Excel 中控制状态和显示的成员
返回对象的成员
执行操作的成员
处理文件操作的成员
其他成员
下面几部分介绍了这些组中的每一个、以及演示一些成员的代码示例。
Application 对象提供了一个大的属性集来控制 Excel 的一般状态。表 1 列出了与状态有关的 Application 对象属性的一个子集。
属性
类型
说明
Cursor
XlMousePointer (xlDefault, xlIBeam, xlNorthwestArrow, xlWait)
获取或设置鼠标指针的外观。
EditDirectlyInCell
布尔值
直接就地获取或设置编辑单元格的能力。如果为 False,则您只能在公式栏中编辑单元格。
FixedDecimal
布尔值
如果为 True,则所有的数字值都使用 FixedDecimalPlaces 属性来确定小数位数;否则将忽略 FixedDecimalPlaces 属性(默认值为 False)。
FixedDecimalPlaces
Long
确定用于数值数据的小数位数(如果 FixedDecimal 属性为 True)。
Interactive
布尔值
获取或设置用户通过键盘和鼠标与 Excel 交互的能力;如果将此属性设置成 False,则一定要确保在异常处理程序中将其重新设置成 True。Excel 不会自动为您重新设置它。
MoveAfterReturn
布尔值
如果为 True,则当您按下 Enter 键时,选择会移到下一个单元格;默认值为 True。
MoveAfterReturnDirection
xlDirection (xlDown, xlToLeft, xlToRight, xlUp)
指示在按下 Enter 键之后移动的方向(如果 MoveAfterReturn 属性为 True)。默认值为 xlDown。
ScreenUpdating
布尔值
如果为 True,Excel 就会在每个方法调用之后更新其屏幕。为了节省时间并且使您的应用程序看起来更加专业,您可以在代码运行时关掉显示。一旦完成,就一定要再次将此属性值重新设置为 True。Excel 不会自动为您重新设置它。
SheetsInNewWorkbook
Long
获取或设置 Excel 自动放置在新的工作簿中的工作表的数目。
StandardFont
字符串
获取或设置 Excel 中默认字体的名称;只有在重新启动 Excel 之后才会生效。
StandardFontSize
Long
获取或设置 Excel 中默认字体的大小;只有在重新启动 Excel 之后才会生效。
StartupPath(只读)
字符串
返回包含 Excel 启动加载项的文件夹的完整路径。
TemplatesPath(只读)
字符串
返回包含模板的文件夹的完整路径;此值代表着一个 Windows 特殊文件夹。
在表 1 所列出的所有属性中,您最可能使用的一个属性是 ScreenUpdating 属性。通过利用这个属性,您不但可以使您的 Excel 应用程序看起来更加专业,还可以使它们运行得更快 — 在每次修改后更新显示会严重影响代码的运行效率,特别是在大范围中通过编程方式填写时。然而,重要的是,当您完成您的工作时始终要设置这个属性,因为 Excel 不会为您重新设置它。因此,当使用 ScreenUpdating 属性时,您将需要始终使用如下代码片段,并且利用 .NET 异常处理来确保屏幕更新恢复:
' Visual Basic Try ThisApplication.ScreenUpdating = False ' Do your work that updates the screen. Finally ThisApplication.ScreenUpdating = True End Try // C# try { ThisApplication.ScreenUpdating = false; // Do your work that updates the screen. } finally { ThisApplication.ScreenUpdating = true; }
Application 对象还提供了一组控制 Excel 中的显示的属性。您可以修改这些属性中的任何一个来改变用户在屏幕上所看到的内容。表 2 列出了可用的显示选项的一个子集。
属性
类型
说明
DisplayAlerts
布尔值
如果为 True(默认值),则当运行代码时,只要有必要(例如在删除一个工作表时),Excel 就会显示警告消息。设置为 False 以忽略警告。Excel 的行为就好像您已经为每个警告选择了默认值。
DisplayFormulaBar
布尔值
如果为 True(默认值),Excel 就会显示标准公式栏以编辑单元格;设置为 False 以隐藏公式栏。
DisplayFullScreen
布尔值
如果为 True,则 Excel 以全屏模式运行(这种模式与仅仅最大化 Excel 窗口的效果不一样);其默认值为 False。
提示 与 ScreenUpdating 属性完全一样,重新设置 DisplayAlerts 属性是非常重要的。因为 Excel 不会为您重新设置此属性,并且它设置为 False,所以在您关闭工作簿之前 Excel 不会提示您保存它们;没有仔细地重新设置 DisplayAlerts 属性可能会在您不小心的情况下导致您丢失数据。
许多 Application 对象的属性返回其他的对象。因为 Visual Studio .NET 提供的标准 Microsoft Office 项目模板只包含 ThisApplication 和 ThisWorkbook 对象,所以您通常需要利用 Application 类的对象成员来引用 Excel 提供的其他对象。您可以使用这些成员通过诸如 ActiveWindow 的属性检索对特定子对象的引用,或者通过诸如 Charts 的属性检索对一个可用的对象集的引用。表 3 列出了 Application 对象的返回对象的属性的一个子集。
属性
类型
型说明
ActiveCell
范围
返回对活动窗口(顶部的窗口)中当前活动单元格的引用。如果没有活动窗口,此属性会产生一个错误。
ActiveChart
图表
返回对当前活动的图表的引用。对于一个嵌入式图表来说,只有当此图表被选中或被激活时才可认为是活动的。
ActiveSheet
对象
返回对活动工作簿中的活动工作表的引用。
ActiveWindow
窗口
返回对活动窗口(顶部的窗口)的引用;如果没有活动窗口,则不返回任何结果。
Charts
工作表
返回 Sheet 对象(Chart 和 Worksheet 对象的父对象)的集合,这些对象包含对活动工作簿中的每个图表的引用。
Selection
对象
返回应用程序中选中的对象。可能是一个 Range、一个 Worksheet 或任何其他的对象 — 同样适用于 Window 类,在这种情况下,选择通常是一个 Range 对象。如果当前没有对象被选中,则不返回任何结果。
Sheets
工作表
返回 Sheet 对象的集合,这些对象包含对活动工作簿中每个工作表的引用。
Workbooks
工作簿
返回 Workbook 对象的集合,这些对象包含对所有打开的工作簿的引用。
您将会最常与 Application 类的 Workbooks 属性交互。这个属性使得您能够循环访问打开的工作簿、打开或创建一个新的工作簿。下面的部分描述了这个属性的行为。
工作簿集合
Workbooks 集合使得有可能使用所有打开的工作簿、创建一个新的工作簿以及将数据导入一个新的工作簿。下表列出了您将发现的 Workbooks 集合的主要用途:
创建一个新的工作簿 使用如下代码(您也可以指定一个工作簿模板的名称作为 Add 方法的一个参数):
' Visual Basic Dim wb As Excel.Workbook = ThisApplication.Workbooks.Add() // C# Excel.Workbook wb = ThisApplication.Workbooks.Add(Type.Missing);
关闭所有打开的工作簿。与大多数的集合不同,这个集合允许您一次性地关闭所有的成员。下面的方法调用关闭所有打开的工作簿:
' Visual Basic ThisApplication.Workbooks.Close() // C# ThisApplication.Workbooks.Close();
打开一个现有的工作簿(使用 Workbooks 集合的 Open 方法)。最简单的形式是使用 Open 方法,如下面的代码片段所示。Open 方法提供了大量的可选参数,这些参数会影响它在特定环境中的行为,但是,您通常不需要使用这些可选参数:
' Visual Basic Dim wb As Excel.Workbook = _ ThisApplication.Workbooks.Open("C:\YourPath\YourWorkbook.xls") // C# Excel.Workbook wb = ThisApplication.Workbooks.Open( "C:\\YourPath\\Yourworkbook.xls", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
提示 如果您是一名 C# 开发人员,您将习惯于在方法调用中看到对 Type.Missing 值的引用。因为 Excel 对象模型是以 VBA 为目标编写的,所以它的许多方法接受可选参数 — 有时达到 30 个可选参数。您将需要使用 Type.Missing 值的许多实例,或者提供每个参数的特定默认值。
以工作簿的形式打开一个文本文件、数据库或 XML 文件(使用 OpenText、OpenDatabase 或 OpenXml )。这些方法提供了极大的灵活性,但即使是对它们进行简单的介绍,也会占用比这里安排的多得多的篇幅。就现在而言,您知道这些方法存在就足够了 — 如果您需要将这些项中的任何一个加载到 Excel 中,您可以更仔细地研究这些项。您可以使用如下代码以工作簿的形式加载一个文本文件(使用逗号作为分隔符,从文本文件中的第三行开始):
' Visual Basic Dim wb as Excel.Workbook = _ ThisApplication.Workbooks.OpenText("C:\Test.txt", StartRow:=3, _ DataType:=xlDelimited, Comma:=True) // C# Excel.Workbook wb = ThisApplication.Workbooks.OpenText("C:\\Test.txt", Type.Missing, 3, Excel.XlTextParsingType.xlDelimited, Excel.XlTextQualifier.xlTextQualifierDoubleQuote, Type.Missing, Type.Missing, Type.Missing, True, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
引用单个工作簿。您可以使用整数(指示在集合中的位置)或工作簿名作为 Workbooks 集合中的索引。然而,如果您想要通过名称引用工作簿,您将需要注意对其进行引用的方法:您必须使用您在标题栏看到的名称,在保存该文件之前,这个名称不包括“.xls”扩展名:
' Visual Basic Dim wb As Excel.Workbook = ThisApplication.Workbooks(1) ' Before Book1 is saved: wb = ThisApplication.Workbooks("Book1") ' After Book1 is saved: wb = ThisApplication.Workbooks("Book1.xls") // C# Excel.Workbook wb = ThisApplication.Workbooks[1]; // Before Book1 is saved: wb = ThisApplication.Workbooks["Book1"]; // After Book1 is saved: wb = ThisApplication.Workbooks["Book1.xls"];
提示 当您引用特定的工作簿时,您利用的是默认的索引器(Item 属性)。除了 Item 属性之外,Workbooks 集合像 Microsoft Office 提供的所有集合一样,包括 Count 属性,Count 属性返回集合中的项(本例中的 Workbooks)的数目。
Application 对象提供了许多允许您执行操作(从重新计算当前数据到撤销对数据的更改)的方法。下表枚举了 Application 对象的一些方法,并且使用了一些小例子对每个方法进行了描述。这一部分的示例出现在示例工作簿中的 Application Object 工作表内:
Calculate:强制重新计算所有打开的工作簿、特定的工作簿或者特定的范围:
' Visual Basic ThisApplication.Calculate ' Or... ThisWorkbook.Calculate ' Or... ThisApplication.Range("SomeNamedRange").Calculate // C# ThisApplication.Calculate(); // Or... ThisWorkbook.Calculate(); // Or... ThisApplication.get_Range("A1", "B12").Calculate();
注 正如示例代码中所示,Range 和 Worksheet 对象也提供 Calculate 方法。使用可以将计算范围限定在您想要重新计算的最小单元格数内的对象的方法。Excel 中的重新计算引擎非常快,但是如果您可以限制所涉及到的单元格数,您就可以优化这一操作。只有在您想重新计算每个打开的工作簿中的每个未决更改时才使用 Application.Calculate。
提示 Visual Basic .NET 和 C# 在处理 Excel 成员时并不完全一样。例如,Excel、VBA 和 Visual Basic .NET 中的 Range 属性在 C# 中只能通过 get_Range 方法进行访问。在本文中,您将会发现此访问器成员和其他访问器成员的一些例子。
Checkspelling:返回一个 Boolean 来指示提供的参数是否拼写正确。您可以选择提供一个自定义字典的名称和一个 Boolean 来指示您是否想要忽略大小写。下面的代码片段检查您所提供的值的拼写,并且在工作表上指示其结果:
' Visual Basic
Code Private Sub TestSpelling() Dim rng As Excel.Range = _ ThisApplication.Range("CheckSpelling") Dim strOut As String If ThisApplication.CheckSpelling( _ rng.Offset(0, 1).Value.ToString) Then strOut = "Spelled correctly" Else strOut = "Spelled incorrectly" End If rng.Offset(0, 2).Value = strOut End Sub
Code private void TestSpelling() { // If you specify only a named range in the call // to get_Range, use Type.Missing for the second parameter. Excel.Range rng = ThisApplication. get_Range("CheckSpelling", Type.Missing); // Note that C# requires you to retrieve and set // the Value2 property of the Range, rather than // the Value property, because the Value property // is parameterized, making it unavailable to C# code: rng.get_Offset(0, 2).Value2 = (ThisApplication.CheckSpelling( rng.get_Offset(0, 1).Value2.ToString(), Type.Missing, Type.Missing) ? "Spelled correctly" : "Spelled incorrectly"); }
提示 前面的代码片段使用了 Range 对象的 Offset 方法,到目前为止您还都没有遇到过。在本文后面讨论 Range 对象的部分中将对这二者进行讨论。Range 类的使用很容易理解:Range 对象代表一个单元格或者一组单元格。在本例中,Range 对象引用已命名范围 CheckSpelling。Offset 属性从相关 Range 的左上角的指定行数和列数处返回一个 Range 对象,并且允许您处理相对于一个已知位置的单元格。
Evaluate:将 Excel 名称转换成实际的引用或值。这个方法允许您以字符串的形式创建引用,并且在需要时将其转换成一个实际对象引用,或者求表达式的值。下面的示例允许您将单元格的地址输入到示例工作表中,并且该代码将文本放到您指定地址的单元格中:
' Visual Basic
Code Private Sub TestEvaluate() Dim rng As Excel.Range = _ ThisApplication.Range("Evaluate") Try Dim rngNew As Excel.Range = _ ThisApplication.Evaluate( _ DirectCast(rng.Offset(0, 1).Value), Excel.Range) rngNew.Value = "Hello, World!" Catch ex As Exception MessageBox.Show(ex.Message, ThisApplication.Name) End Try End Sub
Code private void TestEvaluate() { Excel.Range rng = ThisApplication. get_Range("Evaluate", Type.Missing); try { Excel.Range rngNew = (Excel.Range) ThisApplication.Evaluate( rng.get_Offset(0, 1).Value2); rngNew.Value2 = "Hello, World!"; } catch (Exception ex) { MessageBox.Show(ex.Message, ThisApplication.Name); } }
MailSystem、MailSession、MailLogoff、MailLogon 和 SendMail:这些成员允许您登录您所安装的电子邮件系统、将当前的工作簿作为附件发送、以及从您所安装的电子邮件系统中注销。MailSystem 属性指出已安装的电子邮件系统,而 MailSession 属性返回对当前电子邮件会话(如果有活动的会话,您就不需要登录)的引用。下面的示例将示例工作簿作为一个简单的电子邮件消息的附件发送:
' Visual Basic
Code Private Sub TestEmail() If ThisApplication.MailSystem = Excel.XlMailSystem.xlMAPI Then If ThisApplication.MailSession Is Nothing Then Dim frm As New SendMail If frm.ShowDialog = DialogResult.OK Then ThisApplication.MailLogon( _ frm.EmailName, frm.EmailPassword, frm.DownloadNewMail) End If End If Dim strEmail As String = _ ThisApplication.Range("SendMail").Offset(0, 1). _ Value.ToString ThisWorkbook.SendMail(strEmail, "Sample Excel Email") ThisApplication.MailLogoff() Else MessageBox.Show( _ "This demonstration works only if MAPI is installed.") End If End Sub
Code private void TestEmail() { if (ThisApplication.MailSystem == Excel.XlMailSystem.xlMAPI ) { if ( ThisApplication.MailSession == null ) { SendMail frm = new SendMail(); if (frm.ShowDialog() == DialogResult.OK ) { ThisApplication.MailLogon(frm.EmailName, frm.EmailPassword, frm.DownloadNewMail); } } string strEmail = ThisApplication. get_Range("SendMail", Type.Missing). get_Offset(0, 1).Value2.ToString(); ThisWorkbook.SendMail(strEmail, "Sample Excel Email", Type.Missing); ThisApplication.MailLogoff(); } else { MessageBox.Show("This demonstration works only if " + "MAPI is installed."); } }
注:Workbook 类提供了 SendMail 方法;这是很有意义的,因为可以通过电子邮件发送的最小粒度的对象就是工作簿本身。您会注意到,SendMail 方法并没有提供任何将文本附加到您所发送的消息的方法,并且寻址方面也不够灵活。很明显,提供这些成员只是使通过电子邮件发送一个工作簿变得容易。如果您想获得功能更加齐全的支持,则您需要研究其他与电子邮件交互的方法。此外,如果您当前不在线,并且没有连接到您的电子邮件系统,则上面的示例代码将失败。如果 MailSession 属性没有返回任何结果,则您可以处理这种失败情况,并且不再尝试发送邮件。
Quit:允许您通过编程方式退出 Excel。如果您将 DisplayAlerts 属性设置为 False,则系统不会提示您保存任何未保存的数据。此外,如果您将 Workbook 的 Saved 属性设置为 True,则不管您有没有进行更改,Excel 都不会提示您保存它:
' Visual Basic ThisApplication.Quit // C# ThisApplication.Quit();
Undo:取消用户在用户界面内进行的最后一次操作。这个方法不会对代码进行的操作产生影响,并且只能撤销单个操作。这项功能并不是十分的强大,但是它确实可以使您在执行您的代码之前撤销用户进行的最后一次操作:
' Visual Basic ThisApplication.Undo // C# ThisApplication.Undo();
Application 对象提供了几个成员,通过这些成员,您可以与 Excel 应用程序的上下文内的文件系统交互。下面几部分描述了您可能会使用到的一些成员。(这部分所描述的示例在示例工作簿的 Application File Handling 工作表中。)
DefaultFilePath 属性
这个简单的属性获取或者设置 Excel 用于加载和保存文件的路径:
' Visual Basic ' When the workbook opens: ThisApplication.Range("DefaultFilePath").Value = _ ThisApplication.DefaultFilePath ' When you save the DefaultFilePath property: ThisApplication.DefaultFilePath = _ ThisApplication.Range("DefaultFilePath"). _ Value.ToString // C# // When the workbook opens: ThisApplication.get_Range("DefaultFilePath", Type.Missing). Value2 = ThisApplication.DefaultFilePath; // When you save the DefaultFilePath property: ThisApplication.DefaultFilePath = ThisApplication.get_Range("DefaultFilePath", Type.Missing). Value2.ToString();
DefaultSaveFormat 属性
此属性获取或者设置保存工作簿的默认格式。Excel 为此属性提供了大量的选项,这些选项都是 XlFileFormat 枚举的成员。示例工作簿允许您从可选项中进行选择,如图 1 所示。下面的代码片段演示了示例如何加载和保存此属性的值。
在这个例子中,示例工作表中的列 E 包含 XlFileFormat 枚举(在名为“XlFileFormat”的范围中)的所有可能的值的名称列表,而列 F 包含相应的整数值。图 2 展示了这两个列的一个子集。DefaultSaveFormat 命名范围(在图 1 中)包含对 XlFileFormat 范围的引用,这使得您可以从一个列表中进行选择。一旦您选择保存该值,代码就必须使用 Range.Find 方法找到您已经选择的字符串,然后使用 Range.Offset 方法以您找到的值的指定偏移量返回一个值。(有关 Range.Find 方法的详细信息,请参阅本文后面标题为“在范围内搜索”的部分。)最后,代码将整数值(转换成适当的枚举类型)重新保存到 DefaultSaveFormat 属性。
检索 DefaultSaveFormat 的当前值非常简单。下面的代码将值转换成文本,并且在示例工作表上的正确 Range 中显示它:
' Visual Basic ' When the workbook opens, convert the enumerated value ' into a string: ThisApplication.Range("DefaultSaveFormat").Value = _ ThisApplication.DefaultSaveFormat.ToString // C# // When the workbook opens, convert the enumerated value // into a string: ThisApplication.get_Range("DefaultSaveFormat", Type.Missing). Value2 = ThisApplication.DefaultSaveFormat.ToString();
重新赋选定的值要困难一些。这涉及到三个步骤。代码必须处理下列任务:
从工作表上的 DefaultSaveFormat 范围检索选择的保存格式的名称:
' Visual Basic ' Retrieve the name of the new save format, as a string: Dim strSaveFormat As String = _ ThisApplication.Range("DefaultSaveFormat"). _ Value.ToString() // C# // Retrieve the name of the new save format, // as a string: string strSaveFormat = ThisApplication. get_Range("DefaultSaveFormat", Type.Missing). Value2.ToString();
在工作表上临近 XlFileFormat 范围的列中查找相匹配的整数值(调用 Range 类的 Find 方法)。然后,代码使用 Range.Offset 属性来检索右边一列的值:
' Visual Basic Dim intSaveFormat As Integer = _ CType(ThisApplication.Range("XlFileFormat"). _ Find(strSaveFormat).Offset(0, 1).Value, Integer) // C# Excel.Range rng = ThisApplication. get_Range("xlFileFormat", Type.Missing); Excel.Range rngFind = rng.Find(strSaveFormat, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Excel.XlSearchDirection.xlNext, Type.Missing, Type.Missing, Type.Missing); // In C#, use the get_Offset method instead of the Offset property: int intSaveFormat = Convert.ToInt32(rngFind.get_Offset(0, 1).Value2);
将整数值重新赋值给 DefaultSaveFormat 属性:
' Visual Basic ThisApplication.DefaultSaveFormat = _ CType(intSaveFormat, Excel.XlFileFormat) // C# ThisApplication.DefaultSaveFormat = Excel.XlFileFormat) intSaveFormat;
RecentFiles 属性
RecentFiles 属性返回一个字符串的集合,包含出现在“文件”菜单的最近使用的文件列表内的所有文件的名称。这个列表的长度将随着用户已经选择保留的文件的数目的变化而变化。示例工作簿在其打开时调用这一过程,将最近的文件列表复制到示例工作表上一个名为 RecentFiles 的范围中:
' Visual Basic Private Sub ListRecentFiles() Dim i As Integer Dim rng As Excel.Range = DirectCast( _ ThisApplication.Range("RecentFiles"). _ Cells(1, 1), Excel.Range) For i = 1 To ThisApplication.RecentFiles.Count rng.Offset(i - 1, 0).Value = _ ThisApplication.RecentFiles(i).Name Next End Sub // C# private void ListRecentFiles() { Excel.Range rng = (Excel.Range)ThisApplication. get_Range("RecentFiles", Type.Missing).Cells[1, 1]; for (int i = 1; i <= ThisApplication.RecentFiles.Count; i++) { rng.get_Offset(i - 1, 0).Value2 = ThisApplication.RecentFiles[i].Name; } }
FileDialog 属性
FileDialog 属性返回 FileDialog 对象,这个对象处理四种类型的文件操作。由该属性返回的这个 FileDialog 对象允许您:
选择一个文件,并且将其打开。
选择一个文件位置,并且保存当前工作簿。
选择一个文件夹。
选择一个文件名。
通过使用这个对话框,您可以利用 Microsoft Office 提供的所有文件处理功能。FileDialog 属性需要您通过传递给它一个 msoFileDialogType 枚举值(msoFileDialogFilePicker、msoFileDialogFolderPicker、msoFileDialogOpen 或 msoFileDialogSaveAs)来选择对话框的特定使用。然后,您就可以与这个属性返回的 FileDialog 对象进行交互了。
与许多其他的对象相似,FileDialog 对象是由 Microsoft.Office.Core 命名空间提供的。为了避免键入每个 Office 对象的完整路径,示例项目使用 Imports 或 using 语句来导入这个命名空间。本文中的代码片段还假定您已经将适当的命名空间引用添加到您的文件中了:
' Visual Basic Imports Office = Microsoft.Office.Core // C# using Office = Microsoft.Office.Core;
FileDialog 对象的 Show 方法显示对话框,并且返回 -1(如果您按 OK)和 0(如果您按 Cancel)。如果您已经使用了 msoFileDialogOpen 或 msoFileDialogSaveAs 枚举值,您可以使用该类的 Execute 方法来实际打开或者保存文件。SelectedItems 属性包含一个字符串的集合,每个字符串代表一个选择的文件名。
例如,下面来自示例工作簿的代码提示您打开一个新的工作簿。这个代码片段允许多重选择、清除可用筛选器的列表、添加两个新的筛选器,然后显示对话框,如图 3 所示。如果您选择一个文件或多个文件,则代码会调用 FileDialog 对象的 Execute 方法来打开请求的文件:
' Visual Basic With ThisApplication.FileDialog( _ Office.MsoFileDialogType.msoFileDialogOpen) .AllowMultiSelect = True .Filters.Clear .Filters.Add "Excel Files", "*.xls;*.xlw" .Filters.Add "All Files", "*.*" If .Show <> 0 Then .Execute End If End With // C# dlg = ThisApplication.get_FileDialog( Office.MsoFileDialogType.msoFileDialogOpen); dlg.Filters.Clear(); dlg.Filters.Add("Excel Files", "*.xls;*.xlw", Type.Missing); dlg.Filters.Add("All Files", "*.*", Type.Missing); if(dlg.Show() != 0) dlg.Execute();
下面来自示例的代码片段演示了您可以如何使用该对话框来选择一个文件夹:
' Visual Basic With ThisApplication.FileDialog( _ Office.MsoFileDialogType.msoFileDialogFolderPicker) If .Show <> 0 Then ThisApplication.Range("FolderPickerResults"). _ Value = .SelectedItems.Item(1) End If End With // C# dlg = ThisApplication.get_FileDialog( Office.MsoFileDialogType.msoFileDialogFolderPicker); if (dlg.Show() != 0) { ThisApplication.get_Range("FolderPickerResults", Type.Missing). Value2 = dlg.SelectedItems.Item(1); }
注 Application 对象还提供了 GetOpenFileName 和 GetSaveAsFileName 方法,这些方法允许您选择一个要打开的文件的文件名。尽管您可以使用这些方法,但是您将发现 Microsoft .NET Framework 提供的相对应的 OpenFileDialog 和 SaveFileDialog 控件功能更加丰富,而且更加易于使用。
Application 对象提供了一些不适用于其他种类的成员,例如 WorksheetFunction 属性、Names 集合和 Windows 集合。下面几部分将描述这些成员。
WorksheetFunction 类
Application 对象包含一个属性 WorksheetFunction,这个属性返回 WorksheetFunction 类的实例。这个类提供了许多共享/静态方法,其中的每个方法都包装了一个 Excel 工作表函数。这些方法中的每一个都公开许多 Excel 电子表格计算函数中的一个,而 VBA 没有提供这些函数。而且其中的一些成员在 Visual Basic .NET 和 C# 的运算符和方法中已经具备,因此您不大可能会使用这些成员(例如,And 方法)。
您在 WorksheetFunction 类的方法中将会发现大量有趣的和有用的函数,总结在下面的列表中:
数学函数,例如 Acos、Acosh、Asin、Asinh、Cosh、Degrees、Ln、Log、Median、Max、Min、Mode、Radians 等等。
域函数,允许您对范围执行运算,例如 DAverage、DCount、DCountA、DGet、DMax、DMin、DProduct、DSum 等等。
逻辑函数,例如 IsErr、IsError、IsLogical、IsNA、IsNonText、IsNumber、IsText。
统计函数,例如 BetaDist、BinomDist、ChiTest、ChiInv、LogNormDist、NegBinomDist、Pearson、SumProduct、SumSq、TDist、TTest, Var、VarP 等等。
电子表格函数,在 .NET Framework 中,您不大可能会使用这些函数,例如 And、Or、Choose 等等。
与泰国有关的函数:您将会发现大量使人莫名其妙的函数,这些函数用于处理泰国数字、日历和货币(谣传 Excel 小组曾经特别喜欢吃泰国食品,因而添加了这些函数来帮助计算他们在当地泰国餐馆的餐费,但是现在看来这个谣言是不真实的),例如 BahtText、IsThaiDigit、ThaiDayOfWeek、ThaiDigit、ThaiMonthOfYear、ThaiNumSound、ThaiNumString、ThaiStringLength、ThaiYear、RoundBahtDown 和 RoundBahtUp。
在 Visual Studio .NET 项目中,利用 WorksheetFunction 类非常容易。因为项目模板为您提供了 ThisApplication 对象,您可以简单地引用该对象的 WorksheetFunction 属性。示例应用程序包含一个名为 Other Application Members 的工作表,如图 4 所示,这个示例只测试了这个类的几个成员。
注 WorksheetFunction 类及其成员提供了一个好例子,说明了为什么从 Visual Basic 中使用 Excel 对象要比从 C# 中使用等效的代码容易得多。WorksheetFunction 类的许多方法要求 C# 开发人员传递 30 个参数,其中的大多数为空。当然,通过编写封装各种不同的方法组(一些具有一个必需的参数,一些具有两个必需的参数,等等)的包装无疑可以减轻这种负担。出于本文的目的,代码调用“裸”方法,而不使用包装方法。当然,C# 代码很难看。
单击 Demonstrate WorksheetFunction 链接运行下面的代码(有关 Sort 方法的详细信息,请参阅“对范围内的数据进行排序”部分):
' Visual Basic
Code Private Sub TestWorksheetFunction() Dim ws As Excel.Worksheet = _ DirectCast(ThisWorkbook.ActiveSheet, Excel.Worksheet) Dim rng As Excel.Range = ws.Range("RandomNumbers") Dim rnd As New System.Random Dim i As Integer For i = 1 To 20 ws.Cells(i, 2) = rnd.Next(100) Next i rng.Sort(rng, _ Orientation:=Excel.XlSortOrientation.xlSortColumns) With ThisApplication.WorksheetFunction ws.Range("Min").Value = .Min(rng) ws.Range("Max").Value = .Max(rng) ws.Range("Median").Value = .Median(rng) ws.Range("Average").Value = .Average(rng) ws.Range("StDev").Value = .StDev(rng) End With End Sub
Code private void TestWorksheetFunction() { Excel.Worksheet ws = (Excel.Worksheet) ThisWorkbook.ActiveSheet; Excel.Range rng = ws.get_Range("RandomNumbers", Type.Missing); System.Random rnd = new System.Random(); for ( int i = 1 ; i <= 20; i++) ws.Cells[i, 2] = rnd.Next(100); rng.Sort(rng, Excel.XlSortOrder.xlAscending, Type.Missing, Type.Missing, Excel.XlSortOrder.xlAscending, Type.Missing, Excel.XlSortOrder.xlAscending, Excel.XlYesNoGuess.xlNo, Type.Missing,Type.Missing, Excel.XlSortOrientation.xlSortColumns, Excel.XlSortMethod.xlPinYin, Excel.XlSortDataOption.xlSortNormal, Excel.XlSortDataOption.xlSortNormal, Excel.XlSortDataOption.xlSortNormal); Excel.WorksheetFunction wsf = ThisApplication.WorksheetFunction; ws.get_Range("Min", Type.Missing).Value2 = wsf.Min(rng, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); ws.get_Range("Max", Type.Missing).Value2 = wsf.Max(rng, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); ws.get_Range("Median", Type.Missing).Value2 = wsf.Median(rng, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); ws.get_Range("Average", Type.Missing).Value2 = wsf.Average(rng, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); ws.get_Range("StDev", Type.Missing).Value2 = wsf.StDev(rng, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); }
正如您在示例代码中看到的,您可以把 Range 对象作为参数传递给 WorksheetFunction 方法。此外,您也可以将单值或值列表作为参数进行传递。这些方法通常可接受多达 32 个参数,因此,如果您想要计算一个固定的数字列表的平均值,您可以使用如下代码:
' Visual Basic dblAverage = ThisApplication.WorksheetFunction.Average( _ 12, 14, 13, 19, 21) // C# // Note the number of Type.Missing values--the method accepts // 30 parameters. dblAverage = ThisApplication.WorksheetFunction.Average( 12, 14, 13, 19, 21, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
Window 类和 Windows 集合
正如您可能期望的,Application 对象提供了对 Excel 应用程序内显示的窗口的控制,并且您可以使用 Application 对象的 Windows 属性来打开、关闭和排列 Excel 对象窗口。
Windows 属性返回 Window 对象的集合,并且您可以调用 Arrange 方法来排列所有打开的窗口(或者只是可见的窗口)。指定一个 XlArrangeStyle 枚举值来指示您想要以何种方式排列窗口,并且还可以选择指定一些关于您是否只想排列可见的窗口、以及您想如何同步窗口滚动的信息。例如,要在 Excel 工作区中平铺显示窗口,您可以使用如下代码:
' Visual Basic ThisApplication.Windows.Arrange( _ Excel.XlArrangeStyle.xlArrangeStyleTiled) // C# ThisApplication.Windows.Arrange( Excel.XlArrangeStyle.xlArrangeStyleTiled, Type.Missing, Type.Missing, Type.Missing);
如果您想要通过编程方式创建一个新的窗口,您可以调用工作簿的 NewWindow 方法,例如:
' Visual Basic ThisWorkbook.NewWindow() // C# ThisWorkbook.NewWindow();
因为 NewWindow 方法返回 Window 对象,所以您也可以编写如下代码,它设置新窗口的标题,然后并将其激活:
' Visual Basic With ThisWorkbook.NewWindow() .Caption = "New Window" .Activate() End With // C# Excel.Window wnd = ThisWorkbook.NewWindow(); wnd.Caption = "New Window"; wnd.Activate();
Windows 类提供控制相关窗口的外观和行为的属性和方法,包括颜色、标题、窗口特性的可视性、以及滚动行为。您可以编写如下代码来使用特定窗口的属性:
' Visual Basic With ThisApplication.Windows(3) .GridlineColor = ColorTranslator.ToOle(Color.Red) .Caption = "A New Window" .DisplayHeadings = False .DisplayFormulas = False .DisplayWorkbookTabs = False .SplitColumn = 1 End With // C# wnd = ThisApplication.Windows[3]; wnd.GridlineColor = ColorTranslator.ToOle(Color.Red); wnd.Caption = "A New Window"; wnd.DisplayHeadings = false; wnd.DisplayFormulas = false; wnd.DisplayWorkbookTabs = false; wnd.SplitColumn = 1;
提示 虽然 VBA 和 .NET 都通过相似的范式使用颜色 — 每种都使用三个一组的字节,包含颜色中红、绿和蓝组成部分,编码成 32 位整数的三个低位字节 — 但是它们处理颜色的方式不同。您可以使用 System.Drawing.ColorTranslator.ToOle 方法从 .NET 颜色转换到 VBA 所需的 OLE 颜色。
单击 Other Application Members 工作表上的 Work with Windows 会运行示例程序 TestWindows,它包含这一部分中以小程序块的形式提供的所有代码。单击相同的工作表中的 Reset Windows 会运行下面的过程,它将关闭除了第一个窗口以外的所有窗口,然后把第一个窗口最大化:
' Visual Basic Private Sub ResetWindows() Dim i As Integer For i = ThisApplication.Windows.Count To 2 Step -1 ThisApplication.Windows(i).Close() Next ThisApplication.Windows(1).WindowState = _ Excel.XlWindowState.xlMaximized End Sub // C# private void ResetWindows() { for (int i = ThisApplication.Windows.Count; i >= 2; i--) ThisApplication.Windows[i].Close( false, Type.Missing, Type.Missing); ThisApplication.Windows[1].WindowState = Excel.XlWindowState.xlMaximized; }
Name 类和 Names 集合
Application 对象提供了它的 Names 属性,这个属性返回 Name 对象的集合。每个 Name 对象都对应于 Excel 应用程序中的命名范围。有许多检索对命名范围的引用的方法 — 您可以使用 Workbook 对象的 Names 属性,也可以使用 Worksheet 对象的 Names 属性。
为了创建一个新的命名范围,可以使用 Names 集合的 Add 方法,如下面的代码片段所示。除了两个必需的参数之外,Add 方法还接受许多可选的参数:
' Visual Basic Dim nm As Excel.Name nm = ThisApplication.Names.Add( _ "NewName", "='Other Application Members'!$A$6") // C# Excel.Name nm; nm = ThisApplication.Names.Add( "NewName", @"='Other Application Members'!$A$6", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
指定名称和位置(以及其他可选的参数),然后,您就可以在您的代码中引用该范围:
' Visual Basic ThisApplication.Range("NewName").Value = "Hello, World!" // C# ThisApplication.get_Range( "NewName", Type.Missing).Value2 = "Hello, World!";
为了检索有关命名范围的信息,您可以使用 Name 类的各种属性。下面的列表描述了一些最常用的成员:
Name 返回分配给命名范围的名称。
RefersTo 以标准格式 ("=SheetName!$B$25") 返回一个包含实际目标地址的字符串。
RefersToR 1C 1 以“R1C1”格式 ("=SheetName!R25C2") 返回目标地址。
Value 返回对解析为范围的内容的命名范围的引用。
单击示例中的 Work with Names 链接运行下面的代码,用关于所有命名范围的信息填充工作表的一个区域:
' Visual Basic
Code Dim nm As Excel.Name Dim rng As Excel.Range = ThisApplication.Range("Names") Dim i As Integer For i = 0 To ThisApplication.Names.Count – 1 nm = ThisApplication.Names.Item(i + 1) rng.Offset(i, 0).Value = nm.Name ' Without the leading "'", these references ' get evaluated, rather than displayed directly. rng.Offset(i, 1).Value = "'" & nm.RefersTo.ToString rng.Offset(i, 2).Value = "'" & nm.RefersToR1C1.ToString rng.Offset(i, 3).Value = nm.Value Next i
Code Excel.Range rng = ThisApplication.get_Range("Names", Type.Missing); for ( int i = 0 ; i <= ThisApplication.Names.Count - 1; i++) { nm = ThisApplication.Names.Item(i + 1, Type.Missing, Type.Missing); rng.get_Offset(i, 0).Value2 = nm.Name; // Without the leading "'", these references // get evaluated, rather than displayed directly. rng.get_Offset(i, 1).Value2 = "'" + nm.RefersTo.ToString(); rng.get_Offset(i, 2).Value2 = "'" + nm.RefersToR1C1.ToString(); rng.get_Offset(i, 3).Value2 = nm.Value; }
除了 Application 类提供的所有其他方法之外,您还将发现有一大组事件可用。虽然不可能以任何一种一致的方式演示所有事件,但是单单根据名称,就可以比较清楚地知道它们的用途。下面几部分描述了这些事件的一个子集,并讨论了在您自己的应用程序中最可能使用的事件。
提示 传递给 Office 应用程序中的事件处理程序的参数会让人感到与用在本机 .NET 事件中的参数不同。通常,.NET 事件处理程序总是接收 Object 变量(该变量引用引发事件的对象)和第二个参数(该参数从 EventArgs 基类继承而来,包含关于事件的额外信息)。没有这样定义良好的事件设计模式用于 Office 应用程序,因此每个事件处理程序都接受任意数目的参数(由最初的开发人员定义)。
表行为
Application 对象提供了各种与表(包括图表和工作表)相关的事件。下面的列表包含关于许多这样的事件的信息:
当任何一个表被激活时,SheetActivate 都会发生。Excel 将一个包含对被激活的表的引用的 Object 变量传递给事件处理程序。
提示 正如 Excel 中传递一个引用表的 Object 的任何情况一样,在可以使用这个引用之前,您需要将引用强制转换成一个正确类型(Worksheet 或 Chart,视具体的情况而定)的变量。然而,如果您已经禁用了 Visual Basic .NET 中的 Option Strict 设置,您可以利用晚期绑定。在您键入时,您将仍然不能利用 IntelliSense,这使得编写代码变得更加困难。本文档中所有在 Sheets 集合内使用项的示例都显式地将结果强制转换成所需要的特定类型的表(Worksheet 或 Chart)。
在 Excel 提供默认的双击处理之前,当任何表被双击时,SheetBeforeDoubleClick 都会发生。Excel 将下列参数传递给事件处理程序:一个包含对表的引用的 Object 变量、一个包含离双击位置最近的单元格的 Range 对象、一个允许您取消默认事件处理的 Boolean 值(默认为 False)。(此事件没有出现在图表中。)
提示 所有在它们的名称中包括单词“Before”的事件都允许您取消默认的事件处理。传递给您的事件处理程序的参数通常名为 Cancle,具有默认值 False。如果将这个参数设置为 True,Excel 将不会执行事件的默认处理。
在 Excel 提供默认的右键单击处理之前,当任何表被右键单击时,SheetBeforeRightClick 都会发生。Exce 将下列参数传递给事件处理程序:一个包含对表的引用的 Object 变量、一个包含离右击位置最近的单元格的 Range 对象、一个允许您取消默认事件处理的 Boolean 值(默认为 False)。(此事件没有出现在图表中。)
当任何表被重新计算时,SheetCalculate 都会出现。Excel 将一个包含对重新计算的表的引用的 Object 传递给事件处理程序。
当任何工作表中的单元格发生变化(通过用户或者通过运行代码)时,SheetChange 都会发生。Excel 将一个 Object 变量(包含对表的引用)和一个 Range 变量(引用改变的范围)传递给事件处理程序。
当任何表单被停用时(即当它不再有焦点时),SheetDeactivate 都会发生。只有当焦点转移到同一工作簿内的另一个表时,这个事件处理程序才会运行。Excel 将一个包含对已经停用的表的引用的 Object 变量传递给事件处理程序。
当您单击任何工作簿内的任何超级链接时,SheetFollowHyperlink 都会发生。Excel 将一个引用包含此链接的表的 Object 变量和一个包含对您所单击的链接的引用的 Hyperlink 对象传递给事件处理程序。(示例项目使用了这个事件,从而在示例内提供了导航。)
当工作表上的选择改变时,SheetSelectionChange 会发生(该事件没有出现在图表中)。Excel 将一个引用选择发生改变的表的 Object 变量和一个引用新选择的 Range 变量传递给事件处理程序。(注意,在最初的选择发生改变之前,Excel 没有传递关于最初的选择的信息。)
注 这一部分中的每个事件也可用作 Workbook 类提供的事件。如果该事件是由 Application 对象提供的,则它可以被 Excel 内当前打开的任何一个表引发。当它是由 Workbook 对象提供的,则该事件只有在它影响特定工作簿中的一个表时才会发生。此外,您还将发现 Worksheet 类提供的相同事件。在这种情况下,事件名不包含单词“Sheet”(例如,您将会找到 FollowHyperlink 而不是 SheetFollowHyperlink,等等),并且事件处理程序不传递对表的引用 — 这种信息隐含在接收事件的对象中。另外,事件及其使用方法和参数与此处您所看到的事件相同。
Window 行为
Application 对象(和相应的 Workbook 对象)提供了各种处理 Window 对象的行为的事件。下面的列表描述了这些事件:
当任何窗口被激活时,WindowActivate 都会发生。Excel 将下面两个参数传递给事件处理程序:一个是 Workbook 对象,这个对象引用提供窗口的工作簿;一个是引用被选择的窗口的 Window 对象。与其他激活事件一样,这个事件也是只有在 Excel 内的焦点移动时才激发。切换到另一个应用程序,然后再回到 Excel 时,不会引发此事件。
当任何窗口被停用时,WindowDeactivate 都会发生。有关更多信息,请参阅 WindowActivate 事件描述。
当任何工作簿窗口重新调整大小时,WindowResize 都会发生。Excel 将一个引用提供窗口的工作簿的 Workbook 对象和一个引用大小重新调整的窗口的 Window 对象传递给事件处理程序。
注由 Workbook 类提供的事件中,事件处理程序不会接收对 Workbook 的引用 — 这种信息隐含在引发此事件的对象中。
Workbook 管理
Application 对象提供了各种当您与任何 Workbook 对象交互时都会发生的事件。这些事件过程中的每一个都接收 Workbook 变量,该变量指示参与事件的特定工作簿。下面的列表描述了可用事件的一个子集:
当创建一个新的工作簿时,NewWorkbook 会发生。Excel 将一个引用新的工作簿的 Workbook 变量传递给事件处理程序。(此事件只由 Application 类提供。)
当任何工作簿被激活时,WorkbookActivate 都会发生。Excel 将一个引用被激活的工作簿的 Workbook 变量传递给事件处理程序。(与其他的“激活”事件一样,只有在您从一个工作簿切换到另一个工作簿时这个事件才发生。)
当一个打开的工作簿刚好在默认事件处理之前关闭时,WorkbookBeforeClose 会发生。Excel 将一个引用将要关闭的工作簿的 Workbook 变量以及一个允许事件处理程序取消默认事件处理(即保持工作簿打开)的 Boolean 值(默认为 False)传递给事件处理程序。
警告如果您草率地将 Cancel 参数设置为 True,而不考虑任何条件,则所有的工作簿将永远不会被关闭。
当工作簿内的打印刚好在默认事件处理之前开始时,WorkbookBeforePrint 会发生。Excel 将一个引用包含打印内容的工作簿的 Workbook 变量以及一个允许事件处理程序取消默认事件处理(即跳过请求的打印)的 Boolean 值(默认为 False)传递给事件处理程序。
当刚好在默认事件处理之前保存工作簿时,WorkbookBeforeSave 会发生。Excel 将一个引用保存的工作簿的 Workbook 变量以及一个允许事件处理程序取消默认事件处理(即取消保存)的 Boolean 值(默认为 False)传递给事件处理程序。
当任何工作簿被停用时,WorkbookDeactivate 都会发生。Excel 将一个引用已经停用的工作簿的 Workbook 变量传递给事件处理程序。(与其他的“激活”事件一样,这个事件只有在您从一个工作簿切换到另一个工作簿时才会发生。)
当将新的表添加到工作簿时,WorkbookNewSheet 会发生。Excel 将一个引用工作簿的 Workbook 变量和一个引用新表的 Object 变量传递给事件处理程序。
当一个工作簿打开时,WorkbookOpen 会发生。Excel 将一个引用新打开的工作簿的 Workbook 变量传递给事件处理程序。
注 Workbook 类提供了自己的一组事件,与您在此处看到的事件非常相似。所有以“Workbook”开头的事件在没有该委托(“Activate”而不是“WorkbookActivate”,等等)的情况下出现在 Workbook 类的事件列表中。Workbook 类事件处理程序不接收 Workbook 变量作为参数;该信息隐含在引发这个事件的对象中。此外,Workbook 类还提供了其他 Application 对象事件的镜像,但是只为单个工作簿捕获它们,这与为所有的工作簿捕获这些事件形成了对比。本文档的剩余部分将不讨论事件,因为您现在已经了解了一些您最有可能会使用的事件。
正如您可能想象到的那样,Workbook 类代表了 Excel 应用程序内的一个单一的工作簿。在这一部分,您将会了解这个类的一些成员,包括那些最常使用的属性和方法。
提示 许多 Application 类的成员也作为 Workbook 类的成员加以介绍。在这种情况下,其属性适用于特定的工作簿,而不适用于活动工作簿。这一部分所要讨论的成员远比上一部分中讨论的少,主要因为您对许多提到的成员已经有所了解。
Workbook 类提供了大量的属性(大约 90 个),并且有许多属性处理多数开发人员从不会考虑到的特殊情况;例如,AutoUpdateFrequency 属性返回共享工作簿的自动更新的分钟数;如果工作簿使用 1904 日期系统(一种日期顺序方案,它将 1904 年 1 月 2 日作为对应于值 1 的日期,通常使用于 Macintosh 计算机),Date1904 属性会返回 True 值;PasswordEncryptionAlgorithm 属性可以让您设置用于加密密码的确切算法,等等。
这一部分只是介绍您最可能用到的 Workbook 对象属性,而不是试图全面介绍其众多属性。通常的规则是:如果您需要工作簿的某一行为,而其他人可能已经请求该行为,实际上最可能的情况是一个属性允许该行为,而通常由一个方法提供该行为。在您向一个工作簿中添加自己的代码之前要仔细检查文档。
以下列表描述了一些最常使用的 Workbook 属性:
Name、FullName、Path(字符串,只读):这些属性分别返回不同版本的工作簿名称。FullName 返回完整路径名称,包括工作簿文件名。Name 只是返回名称部分,而 Path 则只返回路径部分。单击示例工作簿中的 Name Information 链接来运行以下代码,并返回信息,如图 5 所示:
' Visual Basic ThisApplication.Range("WorkbookName").Value = _ ThisWorkbook.Name ThisApplication.Range("WorkbookPath").Value = _ ThisWorkbook.Path ThisApplication.Range("WorkbookFullName").Value = _ ThisWorkbook.FullName // C# ThisApplication.get_Range("WorkbookName", Type.Missing). Value2 = ThisWorkbook.Name; ThisApplication.get_Range("WorkbookPath", Type.Missing). Value2 = ThisWorkbook.Path; ThisApplication.get_Range("WorkbookFullName", Type.Missing). Value2 = ThisWorkbook.FullName;
Password(字符串):获取或者设置和工作簿相关的密码。如果您指定了一个非空的密码,工作簿的 HasPassword 属性也会返回 True。您可以检索 Password 属性,但是它的值总是“********”。单击示例工作簿中的 Set Password 链接来运行以下代码,它可以根据您是提供文本还是只提供空字符串来设置或清除工作簿密码。这个示例使用示例项目中名为 Password 的窗体,它提供一个文本框和一个 Password 属性:
' Visual Basic Private Sub SetPassword() Dim frm As New Password If frm.ShowDialog = DialogResult.OK Then ThisWorkbook.Password = frm.Password End If frm.Dispose() End Sub // C# private void SetPassword() { Password frm = new Password(); if (frm.ShowDialog() == DialogResult.OK) ThisWorkbook.Password = frm.Value; frm.Dispose(); }
PrecisionAsDisplayed(布尔值):如果为 True,则 Excel使用以十进制显示的数字进行计算。如果为 False(默认值),则 Excel 使用所有可用的十进制数进行计算,甚至包括那些根本没有显示的部分。图 6 显示此属性设置成 True 的示例工作簿。第 C 列中的每个值都是第 B 列中值的副本,但是第 C 列中的数字格式已经设置成了只显示两个十进制位。要注意到,如果将 PrecisionAsDisplayed 属性设置成 True,求和就会不一样,因为经过四舍五入后实际值会不同。。如果您单击 PrecisionAsDisplayed = False 链接,求和又一样了。单击会调用以下过程,传递 True 或 False 值(取决于您所单击的链接):
' Visual Basic Private Sub TestPrecisionAsDisplayed( _ ByVal IsPrecisionAsDisplayedOn As Boolean) ThisWorkbook.PrecisionAsDisplayed = IsPrecisionAsDisplayedOn End Sub // C# private void TestPrecisionAsDisplayed( bool IsPrecisionAsDisplayedOn) { ThisWorkbook.PrecisionAsDisplayed = IsPrecisionAsDisplayedOn; }
ReadOnly(布尔值,只读):如果工作簿以只读的方式打开,则此属性返回 True 值。此时如果您无法将数据保存到工作簿,那么您可以在应用程序中采取其他不同操作。
Saved(布尔值):用来获取或设置工作簿的保存状态。如果用户已经对工作簿的内容或结构进行了修改,则 Saved 属性就为 True。如果试图关闭工作簿或者退出 Excel,将会出现一个警报提示您保存工作簿(除非您已经将 Application.DisplayAlerts 属性设置成 False)。如果您在代码中将 Saved 属性值设置成 False,Excel 就会认为您的工作簿已经保存,并且不会再次提醒您保存。
正如其他的 Office 应用程序一样,Excel 允许您在保存工作簿的同时保存文档属性。Excel 提供了许多内置属性,并且您也可以添加自己的属性。选择“文件|属性”来显示如图 7 所示的对话框,并且您也可以选择“自定义”选项卡来创建和修改自定义属性。
通过 Workbook 类的 BuiltInDocumentProperties 属性来使用内置属性,并通过 CustomDocumentProperties 属性来使用自定义属性。这些属性都返回一个 DocumentProperties 对象,它是 DocumentProperty 对象的一个集合。通过集合内的名称或者索引可以使用集合的 Item 属性来检索特定的属性。在 Excel 文档中有全部的属性名列表,但是有一个检索列表的简单方法:当您单击示例工作簿中的 Document Properties 链接时会运行下面的过程(参见 图 8)。该过程调用 DumpPropertyCollection 方法列出所有内置属性和它们的当前值,然后对自定义属性也重复进行这一过程。此外,该过程还单独修改 Revision Number 属性,并且创建一个新的自定义属性:
' Visual Basic
Code Private Sub DisplayDocumentProperties() Dim prp As Office.DocumentProperty Dim prps As Office.DocumentProperties Dim rng As Excel.Range = _ ThisApplication.Range("DocumentProperties") Dim i As Integer Try ThisApplication.ScreenUpdating = False Try prps = DirectCast( _ ThisWorkbook.BuiltinDocumentProperties, _ Office.DocumentProperties) ' Set the Revision Number property: prp = prps.Item("Revision Number") prp.Value = CType(prp.Value, Integer) + 1 ' Dump contents of the collection: DumpPropertyCollection(prps, rng, i) Catch ex As Exception MessageBox.Show(ex.Message) End Try ' Work with custom properties: Try prps = DirectCast( _ ThisWorkbook.CustomDocumentProperties, _ Office.DocumentProperties) DumpPropertyCollection(prps, rng, i) Catch ex As Exception MessageBox.Show(ex.Message) End Try ' Add a custom property: Try ' Delete the property, if it exists. prp = prps.Item("Project Name") prp.Delete() Catch ' Do nothing if you get an exception. End Try Try ' Add a new property. prp = prps.Add("Project Name", False, _ Office.MsoDocProperties.msoPropertyTypeString, _ "White Papers") Catch ex As Exception MessageBox.Show(ex.Message) End Try Finally ThisApplication.ScreenUpdating = True End Try End Sub Private Sub DumpPropertyCollection( _ ByVal prps As Office.DocumentProperties, _ ByVal rng As Excel.Range, ByRef i As Integer) Dim prp As Office.DocumentProperty For Each prp In prps rng.Offset(i, 0).Value = prp.Name Try If Not prp.Value Is Nothing Then rng.Offset(i, 1).Value = _ prp.Value.ToString End If Catch ' Do nothing at all. End Try i += 1 Next End Sub
// C#
Code private void DisplayDocumentProperties() { Office.DocumentProperty prp = null; Office.DocumentProperties prps = (Office.DocumentProperties) ThisWorkbook.BuiltinDocumentProperties; Excel.Range rng = ThisApplication. get_Range("DocumentProperties", Type.Missing); int i = 0; try { ThisApplication.ScreenUpdating = false; try { // Set the Revision Number property: prp = prps["Revision Number"]; prp.Value = Convert.ToInt32(prp.Value) + 1; // Dump contents of the collection: i = DumpPropertyCollection(prps, rng, i); } catch (Exception ex) { MessageBox.Show(ex.Message, ThisApplication.Name); } // Work with custom properties: try { prps = (Office.DocumentProperties) ThisWorkbook.CustomDocumentProperties; DumpPropertyCollection(prps, rng, i); } catch (Exception ex) { MessageBox.Show(ex.Message, ThisApplication.Name); } // Add a custom property: try { // Delete the property, if it exists. prp = prps["Project Name"]; prp.Delete(); } catch { // Do nothing if you get an exception. } try { // Add a new property. prp = prps.Add("Project Name", false, Office.MsoDocProperties.msoPropertyTypeString, "White Papers", Type.Missing); } catch (Exception ex) { MessageBox.Show(ex.Message, ThisApplication.Name); } } finally { ThisApplication.ScreenUpdating = true; } } private int DumpPropertyCollection( Office.DocumentProperties prps, Excel.Range rng, int i) { foreach (Office.DocumentProperty prp in prps) { rng.get_Offset(i, 0).Value2 = prp.Name; try { if (prp.Value != null ) { rng.get_Offset(i, 1).Value2 = prp.Value.ToString(); } } catch { // Do nothing at all. } i += 1; } return i; }
提示 前面的代码示例 DisplayDocumentProperties 使用了 Microsoft.Office.Core 程序集中的几个枚举和类型。示例代码包含一个 Imports/using 语句,它将文本“Office”设置成这个命名空间的缩写,就像设置“Excel”缩写一样。项目模板会自动设置“Excel”缩写。而您需要自己添加“Office”语句。
注 尽管在这里您使用的是 Excel 及其对象,但是实际上是 Office 提供了可用的内置文档属性列表,并且 Excel 没必要实现所有的属性 — 如果试图访问一个未定义属性的 Value 属性,就会触发一个异常。示例过程包含应付这种情况(如果会出现的话)的简单异常处理。