从开发人员的角度理解 Excel 对象模型 (二)
其他有用的成员
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// C#Code
Code
正如您在示例代码中看到的,您可以把 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// C#CodeCode
Application 事件
除了 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 类
正如您可能想象到的那样,Workbook 类代表了 Excel 应用程序内的一个单一的工作簿。在这一部分,您将会了解这个类的一些成员,包括那些最常使用的属性和方法。
提示 许多 Application 类的成员也作为 Workbook 类的成员加以介绍。在这种情况下,其属性适用于特定的工作簿,而不适用于活动工作簿。这一部分所要讨论的成员远比上一部分中讨论的少,主要因为您对许多提到的成员已经有所了解。
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;
图 5. 使用 Workbook 属性检索名称的有关信息。 -
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; }
图 6. 将PrecisionAsDisplayed 属性设置成 True,Excel 只使用显示的十进制数进行计算。 -
ReadOnly(布尔值,只读):如果工作簿以只读的方式打开,则此属性返回 True 值。此时如果您无法将数据保存到工作簿,那么您可以在应用程序中采取其他不同操作。
-
Saved(布尔值):用来获取或设置工作簿的保存状态。如果用户已经对工作簿的内容或结构进行了修改,则 Saved 属性就为 True。如果试图关闭工作簿或者退出 Excel,将会出现一个警报提示您保存工作簿(除非您已经将 Application.DisplayAlerts 属性设置成 False)。如果您在代码中将 Saved 属性值设置成 False,Excel 就会认为您的工作簿已经保存,并且不会再次提醒您保存。
使用 Document 属性
正如其他的 Office 应用程序一样,Excel 允许您在保存工作簿的同时保存文档属性。Excel 提供了许多内置属性,并且您也可以添加自己的属性。选择“文件|属性”来显示如图 7 所示的对话框,并且您也可以选择“自定义”选项卡来创建和修改自定义属性。
通过 Workbook 类的 BuiltInDocumentProperties 属性来使用内置属性,并通过 CustomDocumentProperties 属性来使用自定义属性。这些属性都返回一个 DocumentProperties 对象,它是 DocumentProperty 对象的一个集合。通过集合内的名称或者索引可以使用集合的 Item 属性来检索特定的属性。在 Excel 文档中有全部的属性名列表,但是有一个检索列表的简单方法:当您单击示例工作簿中的 Document Properties 链接时会运行下面的过程(参见 图 8)。该过程调用 DumpPropertyCollection 方法列出所有内置属性和它们的当前值,然后对自定义属性也重复进行这一过程。此外,该过程还单独修改 Revision Number 属性,并且创建一个新的自定义属性:
' Visual BasicCode
// C#Code
提示 前面的代码示例 DisplayDocumentProperties 使用了 Microsoft.Office.Core 程序集中的几个枚举和类型。示例代码包含一个 Imports/using 语句,它将文本“Office”设置成这个命名空间的缩写,就像设置“Excel”缩写一样。项目模板会自动设置“Excel”缩写。而您需要自己添加“Office”语句。
注 尽管在这里您使用的是 Excel 及其对象,但是实际上是 Office 提供了可用的内置文档属性列表,并且 Excel 没必要实现所有的属性 — 如果试图访问一个未定义属性的 Value 属性,就会触发一个异常。示例过程包含应付这种情况(如果会出现的话)的简单异常处理。