从.NET 人员地角度理解Excel对象模型
从 .NET 开发人员的角度理解 Excel 对象模型发布日期: 10/27/2004 | 更新日期: 10/27/2004
Ken Getz MCW Technologies, LLC 适用于: 用于 Microsoft Office 系统的 Microsoft Visual Studio 工具 Microsoft Office Excel 2003 Microsoft Visual Studio .NET 2003 摘要:讨论了 Microsoft Office Excel 2003 提供的一些对象,并且说明了如何使用它们通过用于 Microsoft Office 系统的 Microsoft Visual Studio 工具来创建托管代码解决方案。重点主要是 Application、Workbook、Worksheet 和 Range 对象。Visual Basic .NET 和 Visual C# 代码示例演示了每个对象一些属性、方法和事件。 从 Microsoft 下载中心下载 ExcelObj.exe。 本页内容
简介对于打算利用用于 Microsoft Office 系统的 Microsoft Visual Studio 工具的开发人员和想要仅仅使用 COM 自动化来控制 Microsoft Office Excel 2003 应用程序的人来说,他们需要能够与 Excel 对象模型提供的对象进行交互。Excel 提供了数百个您可能想要与之交互的对象,但是您可以通过集中于这些可用的对象的一个非常小的子集来获得对象模型方面的一个良好的开端。这些对象包括:
尽管不可能具体地量化,但是当您使用 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 方法的许多用途。其原因在于示例项目有自己的 OptionStrict 设置 — 这意味着 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 和其他的对象。本文档旨在让您知道什么是可用的,而剩下的东西要靠您好奇的本性来挖掘了。 Application 对象Excel Application 对象代表 Excel 应用程序本身。这可能听起来是显而易见的,但是 Application 对象公开了大量关于运行时应用程序、应用到该实例的一些选项、以及在该实例内打开的当前用户对象的信息。Application 对象提供了许多成员,其中的许多成员您从来都不需要研究,但是其他的一些成员对于您的应用程序的行为是否正确至关紧要。您可以将这些成员分为以下种类:
下面几部分介绍了这些组中的每一个、以及演示一些成员的代码示例。 在 Excel 中控制状态和显示的成员Application 对象提供了一个大的属性集来控制 Excel 的一般状态。表 1 列出了与状态有关的 Application 对象属性的一个子集。
在表 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 列出了可用的显示选项的一个子集。
提示 与 ScreenUpdating 属性完全一样,重新设置 DisplayAlerts 属性是非常重要的。因为 Excel 不会为您重新设置此属性,并且它设置为 False,所以在您关闭工作簿之前 Excel 不会提示您保存它们;没有仔细地重新设置 DisplayAlerts 属性可能会在您不小心的情况下导致您丢失数据。 返回对象的成员许多 Application 对象的属性返回其他的对象。因为 Visual Studio .NET 提供的标准 Microsoft Office 项目模板只包含 ThisApplication 和 ThisWorkbook 对象,所以您通常需要利用 Application 类的对象成员来引用 Excel 提供的其他对象。您可以使用这些成员通过诸如 ActiveWindow 的属性检索对特定子对象的引用,或者通过诸如 Charts 的属性检索对一个可用的对象集的引用。表 3 列出了 Application 对象的返回对象的属性的一个子集。
您将会最常与 Application 类的 Workbooks 属性交互。这个属性使得您能够循环访问打开的工作簿、打开或创建一个新的工作簿。下面的部分描述了这个属性的行为。 工作簿集合 Workbooks 集合使得有可能使用所有打开的工作簿、创建一个新的工作簿以及将数据导入一个新的工作簿。下表列出了您将发现的 Workbooks 集合的主要用途:
执行操作的成员Application 对象提供了许多允许您执行操作(从重新计算当前数据到撤销对数据的更改)的方法。下表枚举了 Application 对象的一些方法,并且使用了一些小例子对每个方法进行了描述。这一部分的示例出现在示例工作簿中的 ApplicationObject 工作表内:
处理文件操作的成员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; 图 1. 从可用类型的列表中选择一种文件格式。 图 2. 示例工作表上的 XlFileFormat 范围的一个子集。 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(); 图 3. 使用 FileDialog 类来显示标准 File Open 对话框。 下面来自示例的代码片段演示了您可以如何使用该对话框来选择一个文件夹: ' 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 类的方法中将会发现大量有趣的和有用的函数,总结在下面的列表中:
在 Visual Studio .NET 项目中,利用 WorksheetFunction 类非常容易。因为项目模板为您提供了 ThisApplication 对象,您可以简单地引用该对象的 WorksheetFunction 属性。示例应用程序包含一个名为 Other Application Members 的工作表,如图 4 所示,这个示例只测试了这个类的几个成员。 注WorksheetFunction 类及其成员提供了一个好例子,说明了为什么从 Visual Basic 中使用 Excel 对象要比从 C# 中使用等效的代码容易得多。WorksheetFunction 类的许多方法要求 C# 开发人员传递 30 个参数,其中的大多数为空。当然,通过编写封装各种不同的方法组(一些具有一个必需的参数,一些具有两个必需的参数,等等)的包装无疑可以减轻这种负担。出于本文的目的,代码调用“裸”方法,而不使用包装方法。当然,C# 代码很难看。 单击 DemonstrateWorksheetFunction 链接运行下面的代码(有关 Sort 方法的详细信息,请参阅“对范围内的数据进行排序”部分): ' Visual Basic 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 // C# 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); } 图 4. 选择 WorksheetFunction 工作表来检验 WorksheetFunction 类及其有用的方法。 正如您在示例代码中看到的,您可以把 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 类的各种属性。下面的列表描述了一些最常用的成员:
单击示例中的 Work with Names 链接运行下面的代码,用关于所有命名范围的信息填充工作表的一个区域: ' Visual Basic 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 // C# 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 事件除了 Application 类提供的所有其他方法之外,您还将发现有一大组事件可用。虽然不可能以任何一种一致的方式演示所有事件,但是单单根据名称,就可以比较清楚地知道它们的用途。下面几部分描述了这些事件的一个子集,并讨论了在您自己的应用程序中最可能使用的事件。 提示 传递给 Office 应用程序中的事件处理程序的参数会让人感到与用在本机 .NET 事件中的参数不同。通常,.NET 事件处理程序总是接收 Object 变量(该变量引用引发事件的对象)和第二个参数(该参数从 EventArgs 基类继承而来,包含关于事件的额外信息)。没有这样定义良好的事件设计模式用于 Office 应用程序,因此每个事件处理程序都接受任意数目的参数(由最初的开发人员定义)。 表行为 Application 对象提供了各种与表(包括图表和工作表)相关的事件。下面的列表包含关于许多这样的事件的信息:
Window 行为 Application 对象(和相应的 Workbook 对象)提供了各种处理 Window 对象的行为的事件。下面的列表描述了这些事件:
Workbook 管理 Application 对象提供了各种当您与任何 Workbook 对象交互时都会发生的事件。这些事件过程中的每一个都接收 Workbook 变量,该变量指示参与事件的特定工作簿。下面的列表描述了可用事件的一个子集:
Workbook 类正如您可能想象到的那样,Workbook 类代表了 Excel 应用程序内的一个单一的工作簿。在这一部分,您将会了解这个类的一些成员,包括那些最常使用的属性和方法。 提示 许多 Application 类的成员也作为 Workbook 类的成员加以介绍。在这种情况下,其属性适用于特定的工作簿,而不适用于活动工作簿。这一部分所要讨论的成员远比上一部分中讨论的少,主要因为您对许多提到的成员已经有所了解。 Workbook 类的属性Workbook 类提供了大量的属性(大约 90 个),并且有许多属性处理多数开发人员从不会考虑到的特殊情况;例如,AutoUpdateFrequency 属性返回共享工作簿的自动更新的分钟数;如果工作簿使用 1904 日期系统(一种日期顺序方案,它将 1904 年 1 月 2 日作为对应于值 1 的日期,通常使用于 Macintosh 计算机),Date1904 属性会返回 True 值;PasswordEncryptionAlgorithm 属性可以让您设置用于加密密码的确切算法,等等。 这一部分只是介绍您最可能用到的 Workbook 对象属性,而不是试图全面介绍其众多属性。通常的规则是:如果您需要工作簿的某一行为,而其他人可能已经请求该行为,实际上最可能的情况是一个属性允许该行为,而通常由一个方法提供该行为。在您向一个工作簿中添加自己的代码之前要仔细检查文档。 以下列表描述了一些最常使用的 Workbook 属性:
使用 Document 属性正如其他的 Office 应用程序一样,Excel 允许您在保存工作簿的同时保存文档属性。Excel 提供了许多内置属性,并且您也可以添加自己的属性。选择“文件|属性”来显示如图 7 所示的对话框,并且您也可以选择“自定义”选项卡来创建和修改自定义属性。 图 7. 使用此对话框设置文档属性。 通过 Workbook 类的 BuiltInDocumentProperties 属性来使用内置属性,并通过 CustomDocumentProperties 属性来使用自定义属性。这些属性都返回一个 DocumentProperties 对象,它是 DocumentProperty 对象的一个集合。通过集合内的名称或者索引可以使用集合的 Item 属性来检索特定的属性。在 Excel 文档中有全部的属性名列表,但是有一个检索列表的简单方法:当您单击示例工作簿中的 Document Properties 链接时会运行下面的过程(参见 图 8)。该过程调用 DumpPropertyCollection 方法列出所有内置属性和它们的当前值,然后对自定义属性也重复进行这一过程。此外,该过程还单独修改 Revision Number 属性,并且创建一个新的自定义属性: ' Visual Basic 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# 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”语句。 图 8. 内置的文档属性 注 尽管在这里您使用的是 Excel 及其对象,但是实际上是 Office 提供了可用的内置文档属性列表,并且 Excel 没必要实现所有的属性 — 如果试图访问一个未定义属性的 Value 属性,就会触发一个异常。示例过程包含应付这种情况(如果会出现的话)的简单异常处理。 使用样式和 Word 文档很相似,Excel 工作簿允许您将指定的样式应用于工作簿内的区域,并且 Excel 提供了许多预定义的(尽管并不非常引人注目)样式。使用“格式|样式”菜单项,会显示一个对话框,它允许您交互式地修改样式,如图 9 所示。 图 9. 利用此对话框交互式地修改样式。 单击“样式”对话框中的 Modify 会显示“单元格格式”对话框,如图 10 所示。 图 10. 使用“单元格格式”对话框修改样式。 “单元格格式”对话框显示了您在格式化单元格时可以使用的所有选项,该对话框中可用的所有选项同样可以在代码中使用。您可以使用 Workbook 对象的 Styles 属性来与工作簿交互,并对工作簿内的范围应用样式。 通过使用 Workbook 对象的 Styles 属性,您可以创建、删除和修改样式。单击示例工作簿中的 Apply Style 来运行以下过程,它创建了一个新的样式(如果您已经运行了这段代码,则使用已有的样式),设置该样式的各个方面,并将其应用到一个区域: ' Visual Basic Private Sub ApplyStyle() Const STYLE_NAME As String = "PropertyBorder" Dim rng As Excel.Range ' Get the range containing all the document properties. rng = GetDocPropRange() Dim sty As Excel.Style Try sty = ThisWorkbook.Styles(STYLE_NAME) Catch sty = ThisWorkbook.Styles.Add(STYLE_NAME) End Try sty.Font.Name = "Verdana" sty.Font.Size = 12 sty.Font.Color = ColorTranslator.ToOle(Color.Blue) sty.Interior.Color = ColorTranslator.ToOle(Color.LightGray) sty.Interior.Pattern = XlPattern.xlPatternSolid rng.Style = STYLE_NAME rng.Columns.AutoFit() End Sub // C# private void ApplyStyle() { const String STYLE_NAME = "PropertyBorder"; // Get the range containing all the document properties. Excel.Range rng = GetDocPropRange(); Excel.Style sty; try { sty = ThisWorkbook.Styles[STYLE_NAME]; } catch { sty = ThisWorkbook.Styles.Add(STYLE_NAME, Type.Missing); } sty.Font.Name = "Verdana"; sty.Font.Size = 12; sty.Font.Color = ColorTranslator.ToOle(Color.Blue); sty.Interior.Color = ColorTranslator.ToOle(Color.LightGray); sty.Interior.Pattern = Excel.XlPattern.xlPatternSolid; rng.Style = STYLE_NAME; rng.Columns.AutoFit(); } GetDocPropRange 方法返回一个由文档属性填充的范围。这个过程使用 Range.End 方法来查找由文档属性填充的范围的结尾,并且基于这个范围的左上角和右下角创建一个新的范围: ' Visual Basic Private Function GetDocPropRange() As Excel.Range Dim rng As Excel.Range = _ ThisApplication.Range("DocumentProperties") Dim rngStart As Excel.Range = _ DirectCast(rng.Cells(1, 1), Excel.Range) Dim rngEnd As Excel.Range = _ rng.End(Excel.XlDirection.xlDown).Offset(0, 1) Return ThisApplication.Range(rngStart, rngEnd) End Function // C# private Excel.Range GetDocPropRange() { Excel.Range rng = ThisApplication.get_Range("DocumentProperties", Type.Missing); Excel.Range rngStart = (Excel.Range) rng.Cells[1, 1]; Excel.Range rngEnd = rng.get_End(Excel.XlDirection.xlDown).get_Offset(0, 1); return ThisApplication.get_Range(rngStart, rngEnd); } 提示 要想知道关于检索和使用 Range 对象的更多信息,请参看本文后面标题为“使用 Range”的章节。 一旦您运行了这段代码,在示例工作簿中包含文档属性的区域会改变底纹和字体,如图 11 所示。 图 11. 应用自定义样式之后 单击 Clear Style 运行以下过程,它清除同一区域的样式: ' Visual Basic Private Sub ClearStyle() ' Get the range containing all the document properties, and ' clear the style. GetDocPropRange().Style = "Normal" End Sub // C# private void ClearStyle() { // Get the range containing all the document properties, and // clear the style. GetDocPropRange().Style = "Normal"; } 使用表Workbook 类提供了一个 Sheets 属性,它返回一个 Sheets 对象。这个对象包含 Sheet 对象集合,其中每个对象既可以是 Worksheet 对象,也可以是 Chart 对象。单击示例工作簿的 List Sheets 链接来运行下面的过程,它列出工作簿中的所有现有的表: ' Visual Basic Private Sub ListSheets() Dim sh As Excel.Worksheet Dim rng As Excel.Range Dim i As Integer rng = ThisApplication.Range("Sheets") For Each sh In ThisWorkbook.Sheets rng.Offset(i, 0).Value = sh.Name i = i + 1 Next sh End Sub // C# private void ListSheets() { int i = 0; Excel.Range rng = ThisApplication.get_Range("Sheets", Type.Missing); foreach (Excel.Worksheet sh in ThisWorkbook.Sheets) { rng.get_Offset(i, 0).Value2 = sh.Name; i = i + 1; } } 您可能还会发现下面的 Sheets 类的成员会很有用。
Workbook 类的方法Workbook 类提供了大量的方法,其中有许多方法处理一些非常特殊的情况。这一部分探讨一些在每个应用程序中您都可能会用到的方法,而不是详细介绍各种方法,并将一些难理解的方法放在后面的章节中介绍,下面的列表描述了一些您最可能使用的方法:
Worksheet 类当您阅读到文章的此处时,您已经了解了使用一个单独的工作表需要掌握的大多数概念。尽管 Worksheet 类提供了大量的成员,但是其大多数的属性、方法和事件与 Application 和(或) Workbook 类提供的成员是相同的或相似的。这一部分将集中探讨 Worksheet 类的重要成员及特定问题,这些问题是您在本文的其他部分所没有接触过的内容。(您可以在示例工作簿中的 Worksheet Object 工作表看到这一部分中的例子。) 不存在 Sheet 类尽管 Excel 提供了一个 Sheets 集合作为 Workbook 对象的属性,但是在 Excel 中您找不到 Sheet 类。相反,Sheets 集合的每个成员都是 Worksheet 或 Chart 对象。您可以以这种方式考虑它:把 Worksheet 和 Chart 类看成内部 Sheet 类的特定实例(并且对无法访问源代码的人来说,将无法知道这种看法是否和实际的实现相符),但是 Sheet 类对外部不可用。 使用保护通常,Excel 中的保护功能可以防止用户和(或)代码修改工作表内的对象。一旦您启用了对工作表保护功能,除非您预作安排,否则用户不能编辑或者修改工作表。在用户界面内,您可以使用 Tools|Protection|Protect Sheet 菜单项启用保护功能。当选择此项后会显示“保护工作表”对话框,如图 12 所示。您可以在此设置密码或者允许用户执行特定的操作。默认情况下,一旦启用保护功能,所有的单元格都会被锁定。此外,通过使用 Tools|Protection|Allow Users to Edit Ranges 菜单项(它会显示如图 13 所示的对话框),您可以让用户编辑特定区域。将这两个对话框结合使用,您可以锁定工作表,然后可以让用户编辑特定的功能和区域。 图 12. 在用户界面中,使用此对话框的控制保护。 图 13. 使用此对话框,您可以允许用户编辑特定的区域。 您可以使用工作表的 Protect 方法通过编程方法控制对工作表的保护。这个方法的语法如下面的例子所示,其中的每个参数都是可选的: ' Visual Basic WorksheetObject.Protect(Password, DrawingObjects, Contents, _ Scenarios, UserInterfaceOnly, AllowFormattingCells, _ AllowFormattingColumns, AllowFormattingRows, _ AllowInsertingColumns, AllowInsertingRows, _ AllowInsertingHyperlinks, AllowDeletingColumns, _ AllowDeletingRows, AllowSorting, AllowFiltering, _ AllowUsingPivotTables) // C# WorksheetObject.Protect(Password, DrawingObjects, Contents, Scenarios, UserInterfaceOnly, AllowFormattingCells, AllowFormattingColumns, AllowFormattingRows, AllowInsertingColumns, AllowInsertingRows, AllowInsertingHyperlinks, AllowDeletingColumns, AllowDeletingRows, AllowSorting, AllowFiltering, AllowUsingPivotTables); 下面的列表描述了 Protect 方法的参数:
可以调用工作表的 Protect 方法来保护工作表,如下面的代码片段所示,这段代码设置了密码,并且只允许排序: ' Visual Basic DirectCast(ThisApplication.Sheets(1), Excel.Worksheet). _ Protect("MyPassword", AllowSorting:=True) // C# ((Excel.Worksheet)ThisApplication.Sheets[1]).Protect( "MyPassword", Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, true, Type.Missing, Type.Missing); 提示很明显,在您的代码中硬编码密码并不是一个很好的主意。最常见的情况是,您需要从用户那里得到密码,然后将这个密码应用于工作簿,但不保存。通常,在源代码中您是不会看到硬编码密码的。 为了取消对工作表的保护,您可以使用下面的代码。这段代码假定有一个名称为 GetPasswordFromUser 的过程,这个过程要求用户输入一个密码,并且返回输入的密码值: ' Visual Basic DirectCast(ThisApplication.Sheets(1), Excel.Worksheet). _ Unprotect(GetPasswordFromUser()) // C# ((Excel.Worksheet)ThisApplication.Sheets[1]). Unprotect(GetPasswordFromUser()); Unprotect 方法将取消对工作表的保护,并让您提供一个可选的密码。 Excel 也提供其他两个对象,您将会发现,当使用保护的时候它们很有用:Protection 和 AllowEditRange 对象。Protection 对象封装了您调用 Protect 方法时指定的所有信息,及未保护区域的信息。通过调用 Protect 方法设置共享的 Protection 对象的属性,这些对象提供了以下对应于 Protect 方法的参数的 Boolean 属性:
此外,Protection 类提供 AllowEditRanges 属性,它允许您指定工作表上的可编辑区域,对应于在图 13 中所示的对话框中指定的信息。 AllowEditRanges 属性包含一个 AllowEditRange 对象集合,其中的每个对象都提供许多有用的属性,包括:
在示例工作簿上的 WorksheetObject 工作表(见图 14)中,您可以试验一下通过编程实现的保护功能。单击 Protect 保护工作表,这样您就只能编辑处于阴影区域的内容(名称为 Information 和 Date 的两个范围)。单击 Unprotect取消保护工作表。 图 14. 测试工作表的保护功能。 在示例工作表中的链接会运行以下过程: ' Visual Basic Private Sub ProtectSheet() Dim ws As Excel.Worksheet = _ DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet) With ws.Protection.AllowEditRanges .Add("Information", ThisApplication.Range("Information")) .Add("Date", ThisApplication.Range("Date")) End With ws.Protect() End Sub Private Sub UnprotectSheet() Dim ws As Excel.Worksheet = _ DirectCast(ThisApplication.Sheets("Worksheet Class"), _ Excel.Worksheet) ' Unprotect the sheet. ws.Unprotect() ' Delete all protection ranges, just to clean up. ' You must loop through this using the index, ' backwards. This collection doesn't provide ' an enumeration method, and it doesn't handle ' being resized as you're looping in a nice way. Dim i As Integer With ws.Protection.AllowEditRanges For i = .Count To 1 Step -1 .Item(i).Delete() Next i End With End Sub // C# private void ProtectSheet() { Excel.Worksheet ws = (Excel.Worksheet)ThisApplication.ActiveSheet; Excel.AllowEditRanges ranges = ws.Protection.AllowEditRanges; ranges.Add("Information", ThisApplication.get_Range("Information", Type.Missing), Type.Missing); ranges.Add("Date", ThisApplication.get_Range("Date", Type.Missing), Type.Missing); ws.Protect(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); } private void UnprotectSheet() { Excel.Worksheet ws = (Excel.Worksheet) ThisApplication.Sheets["Worksheet Class"]; ws.Unprotect(Type.Missing); // Delete all protection ranges, just to clean up. // You must loop through this using the index, // backwards. This collection doesn't provide // an enumeration method, and it doesn't handle // being resized as you're looping in a nice way. Excel.AllowEditRanges ranges = ws.Protection.AllowEditRanges; for (int i = ranges.Count; i >= 1; i--) { ranges[i].Delete(); } } 对象属性Worksheet 类提供了几个返回对象的属性。下面的章节将介绍这些对象,并提供使用这些对象的例子。 批注 使用 Insert|Comment 菜单项,您可以在工作表的一个范围中插入附加的文本批注(见图 15)。在代码中使用 Range 对象的 AddComment 方法也可以达到相同目的。下面的代码删除与名为 Date 的范围相关联的批注(若存在批注),然后创建一个新的批注。最后,代码通过调用下一个代码示例所描述的 ShowOrHideComments 方法来显示工作表中的所有批注(见图 16): ' Visual Basic Dim rng As Excel.Range = ThisApplication.Range("Date") If Not rng.Comment Is Nothing Then rng.Comment.Delete() End If rng.AddComment("Comment added " & DateTime.Now) ' Display all the comments: ShowOrHideComments(Show:=True) // C# Excel.Range rng = ThisApplication.get_Range("Date", Type.Missing); if (rng.Comment != null ) { rng.Comment.Delete(); } rng.AddComment("Comment added " + DateTime.Now); // Display all the comments: ShowOrHideComments(true); 图 15. 在用户界面中您可以方便地将一个新的批注插入到工作表中。 图 16. 在示例工作表中显示所有批注后 Worksheet 类提供了它的 Comments 属性,这个属性返回一个 Comments 对象。这个 Comment 对象集合允许您循环访问和 Worksheet 相关的所有 Comment 对象。 Comment 类并没有提供很多成员。可以使用 Comment 类的 Visible 属性来显示或者隐藏批注,或者使用 Delete 方法删除批注。此外,您可能发现 Text 方法很有用:这个方法允许您将文本添加到批注中,可以添加到现有文本的后面,也可以覆盖现有的文本。 添加一个批注后,您可能想要显示工作表中的批注。示例项目包含一个过程 ShowOrHideComments,这个过程会显示或者隐藏所有在活动工作表中的批注: ' Visual Basic Private Sub ShowOrHideComments(ByVal Show As Boolean) ' Show or hide all the comments: Dim ws As Excel.Worksheet = _ DirectCast(ThisApplication.Sheets("Worksheet Class"), _ Excel.Worksheet) Dim i As Integer For i = 1 To ws.Comments.Count ws.Comments(i).Visible = Show Next End Sub // C# private void ShowOrHideComments(bool show) { // Show or hide all the comments: Excel.Worksheet ws = (Excel.Worksheet) ThisApplication.Sheets["Worksheet Class"]; for (int i = 1; i <= ws.Comments.Count; i++) { ws.Comments[i].Visible = show; } } 注 与 Excel 中的许多辅助集合类相似,Comments 集合没有提供一个默认的枚举器。也就是说,您将不能使用一个 For Each 循环来访问这个集合的所有元素。对于类似 Comment 集合的集合,您必须使用一个索引的循环来循环访问这个集合。 提纲 Excel通过使用提纲功能支持将不同行的数据进行分组。您也可以在代码中利用相同的功能。例如,给定如图 17 所示的一组行,您可以添加提纲功能(在所示的图中已添加),这样您就能够将这些行进行折叠(如图 18 所示),折叠的组如图 19 所示。 图 17. 创建这些组 图 18. 折叠的组 图 19. 完全折叠的组 Worksheet 类提供了 Outline 属性,它本身就是一个 Outline 对象。 Outline 类并没有提供太多成员,下面的列表描述了您可能会使用到的成员:
示例工作表包含对应于 2001 (Data2001) 和 2002 (Data2001) 年及整个行集 (AllData) 的数据的命名范围。这些命名范围覆盖工作表的整个范围;要想进行分组,您必须使用包含所有行的范围。对于 2003 的数据,没有一个和其关联的命名范围以便示例代码演示如何将所有的行作为范围使用。 创建组是很简单的:可以调用与一个或多个完整行相对应的一个范围的 Group 方法来创建组。(您可以指定 4 个可选的分组参数,包括:被分组的开始和终止值、按值分组和一个表明分组周期的 Boolean 值数组。该示例中没有使用这些可选参数,因为您很少会使用这些参数。)调用 Ungroup 方法可以取消分组。例如,单击示例工作表上的 WorkwithGroups 链接来运行下面的代码: ' Visual Basic Private Sub WorkWithGroups() Dim ws As Excel.Worksheet = _ DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet) ' Set worksheet-level features for the outline. ' In this case, summary rows are below ' the data rows (so Excel knows where to put ' the summary rows), and we don't want Excel ' to format the summary rows--that's already been done. ws.Outline.SummaryRow = Excel.XlSummaryRow.xlSummaryBelow ws.Outline.AutomaticStyles = False ' Group the two named ranges. Each of these ' ranges extends across entire rows. ThisApplication.Range("Data2001").Group() ThisApplication.Range("Data2002").Group() ThisApplication.Range("AllData").Group() ' The range of rows from 24 to 27 doesn't have ' a named range, so you can work with that ' range directly. Dim rng As Excel.Range = _ DirectCast(ws.Rows("24:27"), Excel.Range) rng.Group() ' Collapse to the second group level. ws.Outline.ShowLevels(RowLevels:=2) End Sub // C# private void WorkWithGroups() { Excel.Worksheet ws = (Excel.Worksheet) ThisApplication.ActiveSheet; // Set worksheet-level features for the outline. // In this case, summary rows are below // the data rows (so Excel knows where to put // the summary rows), and we don't want Excel // to format the summary rows--that's already been done. ws.Outline.SummaryRow = Excel.XlSummaryRow.xlSummaryBelow; ws.Outline.AutomaticStyles = false; // Group the two named ranges. Each of these // ranges extends across entire rows. ThisApplication.get_Range("Data2001", Type.Missing). Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing); ThisApplication.get_Range("Data2002", Type.Missing). Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing); ThisApplication.get_Range("AllData", Type.Missing). Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing); // The range of rows from 24 to 27 doesn't have // a named range, so you can work with that // range directly. Excel.Range rng = (Excel.Range)ws.Rows["24:27", Type.Missing]; rng.Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing); // Collapse to the second group level. ws.Outline.ShowLevels(2, Type.Missing); } 为了对三个命名范围分组,代码只是简单的调用相应范围的 Group 方法: ' Visual Basic ThisApplication.Range("Data2001").Group() // C# ThisApplication.get_Range("Data2001", Type.Missing). Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing); 为了对未命名的范围分组,代码使用了工作表的 Rows 属性,给定行范围。这个属性返回一个对应于要使用的行的范围: ' Visual Basic Dim rng As Excel.Range = _ DirectCast(ws.Rows("24:27"), Excel.Range) rng.Group() // C# Excel.Range rng = (Excel.Range)ws.Rows["24:27", Type.Missing]; rng.Group(Type.Missing, Type.Missing, Type.Missing, Type.Missing); 单击示例工作表中的 Clear Groups 链接来运行类似代码,这样可以清除组: ' Visual Basic Private Sub ClearGroups() Dim ws As Excel.Worksheet = _ DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet) ' Specify RowLevels and/or ColumnLevels parameters: ws.Outline.ShowLevels(RowLevels:=3) Dim rng As Excel.Range = _ DirectCast(ws.Rows("24:27"), Excel.Range) rng.Ungroup() ThisApplication.Range("Data2001").Ungroup() ThisApplication.Range("Data2002").Ungroup() ThisApplication.Range("AllData").Ungroup() End Sub // C# private void ClearGroups() { Excel.Worksheet ws = (Excel.Worksheet) ThisWorkbook.Sheets["Worksheet Class"]; // Specify RowLevels and/or ColumnLevels parameters: ws.Outline.ShowLevels(3, Type.Missing); Excel.Range rng = (Excel.Range) ws.Rows["24:27", Type.Missing]; rng.Ungroup(); ThisApplication.get_Range("Data2001", Type.Missing).Ungroup(); ThisApplication.get_Range("Data2002", Type.Missing).Ungroup(); ThisApplication.get_Range("AllData", Type.Missing).Ungroup(); } 通过使用这些方法,您可以创建和删除组,并且可以控制工作表中显示的组级。 Range 对象Range 对象是您在 Excel 应用程序中最经常使用的对象;在您可以操作 Excel 内的任何区域之前,您需要将其表示为一个 Range 对象,然后使用该 Range 对象的方法和属性。Range 类是很重要的,目前为止,本篇文章中的每个示例中在某种程度上都使用了一个 Range 对象。基本上来说,一个 Range 对象代表一个单元格、一行、一列、包含一个或者更多单元块(可以是连续的单元格,也可以式不连续的单元格)的选定单元格,甚至是多个工作表上的一组单元格。 由于不可能讨论 Range 这个大类的所有成员,所以这一部分集中探讨三个主要的问题:
换句话说,由于 Range 对象在众多不同场合下有众多不同用途,所有本节集中回答“我如何……”这样的问题,而不是提供对所有成员全面的列表。 管理选择区域尽管使用当前选择区域作为修改一个范围的属性和行为的做法很具有吸引力,但是您最好避免这样做。就像任何其他共享资源一样,在 Excel 内的选择区域代表用户的选择。如果您在代码中修改该选择区域,将会导致用户失去对当前选择区域的控制。经验法则是:只有在您想改变用户的选择区域时,才可以调用对象的 Select 方法。作为一个开发人员,您不能只是为了方便就去调用 Select 方法。如果您的目的只是设置一个范围的属性,总会有其他替代方法。总之,避免使用 Select 方法不但可以使您的代码运行得更快,还可以使您的用户免受干扰。 如下代码清除用户当前单元格相邻区域,编写这样的代码是很简单的: ' Visual Basic ThisApplication.ActiveCell.CurrentRegion.Select DirectCast(ThisApplication.Selection, Excel.Range).ClearContents // C# ThisApplication.ActiveCell.CurrentRegion.Select(); ((Excel.Range)ThisApplication.Selection).ClearContents(); 这样做会取消用户的选择。如果最初只选择一个单元格,那么当运行前面的代码片段后,单元格附近的整大块将会被选定。实际上,除非您的目的是选择所有的单元格区域,否则使用如下所示代码是更好的解决方案: ' Visual Basic ThisApplication.ActiveCell.CurrentRegion.ClearContents // C# ThisApplication.ActiveCell.CurrentRegion.ClearContents(); 为什么任何人都会想到使用第一个代码片段呢?之所以会使用这样的代码,是因为 Excel 开发人员在尝试发现如何使用 Excel 内的各种对象及其方法的一开始都会倾向于使用 Excel 宏记录器。这个一个好主意,但是宏记录器编写 的代码实在很糟糕。通常,宏记录器使用了选择区域,并在记录任何任务的时候修改选择区域。 提示 当使用一个或一组单元格时,尽可能使用描述您想使用的单元格的范围,而不是修改选择区域。如果您的目的是更改用户的选择区域,则使用 Range.Select 方法。 在代码中引用 RangeRange 类是很灵活的,您在编程使用范围的时候会发现它给您提供太多的选择。有时 Range 对象是单个的对象,而有时它代表对象的一个集合。它具有 Item 和 Count 成员,尽管 Range 对象通常指单个的对象,这使得有时如何准确使用 Range 对象成为一件很棘手的事情。 提示 下面的几个示例获得一个范围的 Address 属性。这个属性返回一个包含范围坐标的字符串,坐标以下面几种格式之一表示,包括:“$A$1”(单元格在位置 A1)、“$1”(在工作表的第一行)和“$A$1:$C$5”(范围包括介于 A1 和 C5 之间矩形内的所有单元格)。“$”表示绝对坐标(而非相对坐标)。使用 Address 属性是找到您要检索的范围的准确位置的最简单方法。有关引用范围的各种方法的更多信息,请参考 Excel 联机帮助。 以其最简单的方式,您可以编写如下程序清单所示的代码来使 Range 对象引用单个单元格或者一组单元格。所有示例都假定具有下面的设置代码: ' Visual Basic Dim ws As Excel.Worksheet = _ DirectCast(ThisWorkbook.Worksheets(1), Excel.Worksheet) Dim rng, rng1, rng2 As Excel.Range // C# Excel.Worksheet ws = (Excel.Worksheet)ThisWorkbook.Worksheets[1]; Excel.Range rng, rng1, rng2; 您可以使用下面的任何一种方法来引用一个特定范围(也有其他几种取得 Range 对象引用的方法):
使用技术开发人员通常要求具有这样的能力:改变包含选定单元格的整行的字体,使文本变成粗体。Excel 中并没有内置这个功能,但是添加它也不是非常困难。示例工作簿中的 Range 类的工作表包含一个特别处理的范围:当您选择一个条目,其所在行会变成粗体。图 23 显示了这一行为。 图 23. 选择一个条目使整行变成粗体。 示例工作簿包含以下过程来处理格式化: ' Visual Basic Private Sub BoldCurrentRow(ByVal ws As Excel.Worksheet) ' Keep track of the previously bolded row. Static intRow As Integer ' Work with the current active cell. Dim rngCell As Excel.Range = _ ThisApplication.ActiveCell ' Bold the current row. rngCell.EntireRow.Font.Bold = True ' Make sure intRow isn't 0 (meaning that ' this is your first pass through here). If intRow <> 0 Then ' If you're on a different ' row than the last time through here, ' make the old row not bold. If rngCell.Row <> intRow Then Dim rng As Excel.Range = _ DirectCast(ws.Rows(intRow), Excel.Range) rng.EntireRow.Font.Bold = False End If End If ' Store away the new row number ' for next time. intRow = rngCell.Row End Sub // C# private int LastBoldedRow = 0; private void BoldCurrentRow(Excel.Worksheet ws) { // Keep track of the previously bolded row. // Work with the current active cell. Excel.Range rngCell = ThisApplication.ActiveCell; // Bold the current row. rngCell.EntireRow.Font.Bold = true; // Make sure intRow isn't 0 (meaning that // this is your first pass through here). if (LastBoldedRow != 0) { // If you're on a different // row than the last time through here, // make the old row not bold. if (rngCell.Row != LastBoldedRow) { Excel.Range rng = (Excel.Range)ws.Rows[LastBoldedRow, Type.Missing]; rng.Font.Bold = false; } } // Store away the new row number // for next time. LastBoldedRow = rngCell.Row; } 这个示例采用如下步骤来使当前行变成粗体,并且使前一次变成粗体的行变回原来的状态:
示例工作簿从它的 SheetSelectionChange 事件处理程序调用 BoldCurrentRow 过程。在这个过程中,代码验证新选择的行是否位于正确范围(使用 Application 对象的 Intersect 方法),如果是,就调用 BoldCurrentRow 过程: ' Visual Basic Private Sub ThisWorkbook_SheetSelectionChange( _ ByVal Sh As Object, ByVal Target As Excel.Range) _ Handles ThisWorkbook.SheetSelectionChange If Not ThisApplication.Intersect(Target, _ ThisApplication.Range("BoldSelectedRow")) Is Nothing Then ' The selection is within the range where you're making ' the selected row bold. BoldCurrentRow(DirectCast(Sh, Excel.Worksheet)) End If End Sub // C# protected void ThisWorkbook_SheetSelectionChange( System.Object sh, Excel.Range Target) { // Don't forget that the Intersect method requires // thirty parameters. if (ThisApplication.Intersect(Target, ThisApplication.get_Range("BoldSelectedRow", 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) != null) { // The selection is within the range where you're making //the selected row bold. BoldCurrentRow((Excel.Worksheet) sh); } } 使用 Range一旦您得到了对一个范围的引用,您能用它作什么呢?可以列出的用途是无穷的,只要您能够想象得到。这一节集中讨论一些使用 Range 对象的技术,并且为每种技术提供简单的示例。这一部分中的所有示例都可以在示例工作簿的 Range Class 工作表中找到。 自动填充范围 Range 类的 AutoFill 方法允许您使用值自动填充一个范围。大多数情况下,AutoFill 方法用于将递增或递减的值存储到一个范围中。您可以通过提供可选的常量来指定此方法的行为。这个常量来自 XlAutoFillType 枚举(xlFillDays、xlFillFormats、xlFillSeries、xlFillWeekdays、xlGrowthTrend、xlFillCopy、xlFillDefault、xlFillMonths、 xlFillValues、xlFillYears 或 xlLinearTrend)。如果您不指定一个填充类型,Excel 会假定您使用默认填充类型(xlFillDefault),并且填充它认为合适的指定范围。 示例工作表(如图 24 所示)包含四个将被自动填充的区域。列 B 包含五个工作日;列 C 包含五个月;列 D 包含五年内逐年递增的日期;列 E 包含一系列数字,每行以二递增。图 25 显示运行示例代码后的相同区域。 图 24. 调用 AutoFill 方法之前的四个示例范围。 图 25. 自动填充范围后。 单击 AutoFill 链接运行以下过程: ' Visual Basic Private Sub AutoFill() Dim rng As Excel.Range = ThisApplication.Range("B1") rng.AutoFill(ThisApplication.Range("B1:B5"), _ Excel.XlAutoFillType.xlFillDays) rng = ThisApplication.Range("C1") rng.AutoFill(ThisApplication.Range("C1:C5"), _ Excel.XlAutoFillType.xlFillMonths) rng = ThisApplication.Range("D1") rng.AutoFill(ThisApplication.Range("D1:D5"), _ Excel.XlAutoFillType.xlFillYears) rng = ThisApplication.Range("E1:E2") rng.AutoFill(ThisApplication.Range("E1:E5"), _ Excel.XlAutoFillType.xlFillSeries) End Sub // C# private void AutoFill() { Excel.Range rng = ThisApplication.get_Range("B1", Type.Missing); rng.AutoFill(ThisApplication.get_Range("B1:B5", Type.Missing), Excel.XlAutoFillType.xlFillDays); rng = ThisApplication.get_Range("C1", Type.Missing); rng.AutoFill(ThisApplication.get_Range("C1:C5", Type.Missing), Excel.XlAutoFillType.xlFillMonths); rng = ThisApplication.get_Range("D1", Type.Missing); rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing), Excel.XlAutoFillType.xlFillYears); rng = ThisApplication.get_Range("E1:E2", Type.Missing); rng.AutoFill(ThisApplication.get_Range("E1:E5", Type.Missing), Excel.XlAutoFillType.xlFillSeries); } 每种情况您都必须指定两个范围:
AutoFill 方法的第二个参数(XlAutoFillType 枚举值)是可选的。通常,您需要提供该值才能得到您想要的行为。例如,尝试改变以下代码: ' Visual Basic rng.AutoFill(ThisApplication.Range("D1:D5"), _ Excel.XlAutoFillType.xlFillYears) // C# rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing), Excel.XlAutoFillType.xlFillYears); 使之看起来像这样: ' Visual Basic rng.AutoFill(ThisApplication.Range("D1:D5")) // C# rng.AutoFill(ThisApplication.get_Range("D1:D5", Type.Missing), Excel.XlAutoFillType.xlFillDefault); 代码经过修改后,日期将按天递增,而不是按年递增。 在范围中查找 Range 类的 Find 方法允许您在范围内搜索文本。这个灵活的方法模仿 Excel 中的查找和替换对话框的行为,如图 26 所示 - 实际上,这个方法直接和这个对话框交互。也就是说,Range.Find 方法或者使用您传递给它的参数来决定它的搜索行为,或者如果您没有传递参数,它就使用其在查找和替换对话框中的值来进行查找。表 4 列出了 Range.Find 方法的参数,除了第一个参数外,其他所有参数都是可选的。 图 26. 在这个对话框上的选择会影响 Find 方法的行为。 警告因为 Range.Find 的几乎所有参数都是可选的,同时因为用户可能通过“查找和替换”对话框改变值,所以您要确保真正将所有值传给了 Find 方法,除非您想将用户的选择也考虑在内。当然,C# 开发人员不需要担心这个问题,因为他们在每个方法调用时都必须提供所有参数。
以下示例来自示例工作簿,它搜索一个范围(名称为“Fruits”),并更改含有单词“apples”的单元格的字体(图 27 显示了搜索结果)。这个过程也使用了 FindNext 方法,它使用前面设好的搜索设置重复搜索。(Range.FindPrevious 方法和 Range.FindNext 方法的使用几乎一样,但这个示例没用到。)您要指定在哪个单元格后搜索,而剩下的就由 FindNext 方法处理。 图 27. 包含单词“apples”的单元格的搜索结果 提示FindNext(和 FindPrevious)方法一旦搜索到范围的末端,就会重新回到搜索范围的开始位置。要确保搜索不会成为无限循环,永远不休,您需要在代码中设定。示例过程演示了处理这种情况的一种方法。如果您想完全避免这种无限循环,或者您想进行一个比 Find/FindNext/FindPrevious 方法更加复杂的搜索,那么您也可以使用一个 For Each 循环在一个范围内对所有单元格进行循环查找。 单击示例工作簿的 Range Class 工作表中的 Find 链接来运行以下过程: ' Visual Basic Private Sub DemoFind() Dim rng As Excel.Range = ThisApplication.Range("Fruits") Dim rngFound As Excel.Range ' Keep track of the first range you find. Dim rngFoundFirst As Excel.Range ' You should specify all these parameters ' every time you call this method, since they ' can be overriden in the user interface. rngFound = rng.Find( _ "apples", , _ Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, _ Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, False) While Not rngFound Is Nothing If rngFoundFirst Is Nothing Then rngFoundFirst = rngFound ElseIf rngFound.Address = rngFoundFirst.Address Then Exit While End If With rngFound.Font .Color = ColorTranslator.ToOle(Color.Red) .Bold = True End With rngFound = rng.FindNext(rngFound) End While End Sub // C# private void DemoFind() { Excel.Range rng = ThisApplication. get_Range("Fruits", Type.Missing); Excel.Range rngFound; // Keep track of the first range you find. Excel.Range rngFoundFirst = null; // You should specify all these parameters // every time you call this method, since they // can be overriden in the user interface. rngFound = rng.Find("apples", Type.Missing, Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, false, Type.Missing, Type.Missing); while (rngFound != null) { if (rngFoundFirst == null ) { rngFoundFirst = rngFound; } else if (GetAddress(rngFound) == GetAddress(rngFoundFirst)) { break; } rngFound.Font.Color = ColorTranslator.ToOle(Color.Red); rngFound.Font.Bold = true; rngFound = rng.FindNext(rngFound); } } 这段代码采取这些步骤来实现其目的:
单击示例工作表的 Reset Find 链接来运行这个简单的过程,开始运行时将会重新设置范围: ' Visual Basic Private Sub ResetFind() Dim rng As Excel.Range = ThisApplication.Range("Fruits") With rng.Font .Color = ColorTranslator.ToOle(Color.Black) .Bold = False End With End Sub // C# private void ResetFind() { Excel.Range rng = ThisApplication. get_Range("Fruits", Type.Missing); rng.Font.Color = ColorTranslator.ToOle(Color.Black); rng.Font.Bold = false; } 提示 如果您想在一个范围内查找和替换,请使用 Range.Replace 方法。这个方法的使用类似于 Find 方法,但是可以让您指定要替换的值。 Replace 方法返回一个指示是否执行替换的 Boolean 值。即使只替换一个值,它也会返回 True。 在范围中对数据进行排序就如通过 Excel 用户界面对一个范围内的数据进行排序一样,您也可以采用编程方式使用 Range.Sort 方法对数据进行排序。您指出要被排序的范围,要进行排序的至多三行或三列(可选),以及其他可选的参数,剩下的则由 Excel 来处理。表 5 列出了 Sort 方法的所有参数。(Visual Basic .NET 开发人员很可能只会用到其中的一部分,而 C# 开发人员则必须为每个参数赋予值。)
提示当调用像这样的方法时,Visual Basic .NET 开发人员相对于 C# 开发人员来说,有着明显的优势。因为您不太可能会用到所有参数,Visual Basic .NET 开发人员能够使用命名的参数,只须指定他们需要的参数即可。而为了接受默认行为,C# 开发人员必须将所有不使用的参数传递 null 值。 图 28. 您可以创建自己的自定义排序列表,然后在代码中引用这些特定的排序顺序。 单击 Range Class 示例工作表中的 Sort 链接运行以下过程,它首先根据第一列中的数据来对“Fruits”范围排序,然后根据第二列中的数据排序: ' Visual Basic Private Sub DemoSort() Dim rng As Excel.Range = ThisApplication.Range("Fruits") rng.Sort( _ Key1:=rng.Columns(1), Order1:=Excel.XlSortOrder.xlAscending, _ Key2:=rng.Columns(2), Order2:=Excel.XlSortOrder.xlAscending, _ Orientation:=Excel.XlSortOrientation.xlSortColumns, _ Header:=Excel.XlYesNoGuess.xlNo) End Sub // C# private void DemoSort() { Excel.Range rng = ThisApplication. get_Range("Fruits", Type.Missing); rng.Sort(rng.Columns[1, Type.Missing], Excel.XlSortOrder.xlAscending, rng.Columns[2, 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); } 单击同一个工作表中的 Reset Sort 链接来运行以下过程,它根据自定义排序方法对第二列进行排序,如图 28 所示: ' Visual Basic Private Sub ResetSort() Dim rng As Excel.Range = ThisApplication.Range("Fruits") rng.Sort(rng.Columns(2), OrderCustom:=6, _ Orientation:=Excel.XlSortOrientation.xlSortColumns, _ Header:=Excel.XlYesNoGuess.xlNo) End Sub // C# private void ResetSort() { Excel.Range rng = ThisApplication. get_Range("Fruits", Type.Missing); rng.Sort(rng.Columns[2, Type.Missing], Excel.XlSortOrder.xlAscending, Type.Missing, Type.Missing, Excel.XlSortOrder.xlAscending, Type.Missing, Excel.XlSortOrder.xlAscending, Excel.XlYesNoGuess.xlNo, 6, Type.Missing, Excel.XlSortOrientation.xlSortColumns, Excel.XlSortMethod.xlPinYin, Excel.XlSortDataOption.xlSortNormal, Excel.XlSortDataOption.xlSortNormal, Excel.XlSortDataOption.xlSortNormal); } |