// C#
ThisApplication.Workbooks[1].Activate;
•
Close 方法关闭一个指定的工作簿,并且(可选)指定是否保存修改。如果工作簿从未保存过,则可以指定一个文件名。此外,如果您要将工作簿发送给其他用户,您可以指定您是否想将工作簿发送给下一个用户。下面的代码片段关闭工作簿,并且不保存修改:
' Visual Basic
ThisApplication.Workbooks(1).Close(SaveChanges:=False)
// C#
ThisApplication.Workbooks(1).Close(false,
Type.Missing, Type.Missing);
•
Protect 和 Unprotect 方法允许您保护一个工作簿,从而不能添加或者删除工作表,以及再次取消保护工作簿。您可以指定一个密码(可选),并且指明是否保护工作簿的结构(这样用户就不能移动工作表)以及工作簿的窗口(可选)。保护工作簿用户仍可以编辑单元格。要想保护数据,您必须保护工作表。调用 Unprotect 方法(如果需要,还要传递一个密码)可以取消保护工作簿。以下示例假定一个名称为 GetPasswordFromUser 的过程,它要求用户输入密码,并且会返回输入的值:
' Visual Basic
ThisApplication.Workbooks(1).Protect(GetPasswordFromUser())
// C#
ThisApplication.Workbooks[1].Protect(
GetPasswordFromUser(), Type.Missing, Type.Missing);
•
正如您所认为的,Save 方法保存工作簿。如果您还未保存过工作簿,则应该调用 SaveAs 方法,这样您可以指定一个路径(如果还未保存过工作簿,Excel 会将其保存在当前文件夹中,并以创建工作簿时所给的名称命名):
' Visual Basic
' Save all open workbooks.
Dim wb As Excel.Workbook
For Each wb in ThisApplication.Workbooks
wb.Save
Next wb
// C#
// Save all open workbooks.
foreach (Excel.Workbook wb in ThisApplication.Workbooks)
{
wb.Save();
}
•
SaveAs 方法要比 Save 方法复杂的多。这个方法允许您保存指定的工作簿,并且指定名称、文件格式、密码、访问模式和其他更多的选项(可选)。查看联机帮助可以获得所有选项列表。下面的代码片段将当前工作簿保存到一个指定位置,并且存成 XML 格式:
' Visual Basic
ThisApplication.ActiveWorkbook.SaveAs("C:\MyWorkbook.xml", _
FileFormat:=Excel.XlFileFormat.xlXMLSpreadsheet)
// C#
ThisApplication.ActiveWorkbook.SaveAs("C:\\MyWorkbook.xml",
Excel.XlFileFormat.xlXMLSpreadsheet, Type.Missing,
Type.Missing, Type.Missing, Type.Missing,
Excel.XlSaveAsAccessMode.xlNoChange, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing);
提示 由于保存成某些格式需要一些交互,您也许想在调用 SaveAs 方法之前将 Application.DisplayAlerts 属性设置成 False。例如,在将一个工作表保存成 XML 格式时,Excel 会提醒您不能随工作簿保存 VBA 项目。如果将 DisplayAlerts 属性设置成 False,就不会出现这种警告。
•
SaveCopyAs 方法将工作簿的一个副本保存到文件中,但不会修改在内存中打开的工作簿。当您想创建工作簿的备份,同时不修改工作簿的位置时,这个方法非常有用:
' Visual Basic
ThisApplication.ActiveWorkbook.SaveCopyAs("C:\Test.xls")
// C#
ThisApplication.ActiveWorkbook.SaveCopyAs("C:\\Test.xls");
警告 交互式地取消任何保存或者复制工作簿的方法会在您的代码中触发一个运行时异常。例如,如果您的过程调用 SaveAs 方法,但是没有禁用 Excel 的提示功能,并且您的用户在提示后单击“取消”,则 Excel 会将运行时错误返回给代码。
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 方法的参数:
•
设置 Password 参数来指定一个区分大小写的字符串,这是取消保护工作表所需要的。如果您不指定这个参数,任何人都可以取消保护工作表。
•
将 DrawingObjects 参数设置为 True 来保护工作表的形状。默认值为 False。
•
将 Contents 参数设置为 True 来保护工作表的内容(单元格)。默认值为 True,您可能永远不会改变它。
•
将 Scenarios 参数设置为 True 来保护工作表中的方案。默认值为 True。
•
将 UserInterfaceOnly 参数设置为 True 可以允许通过代码修改,但是不允许通过用户界面修改。默认值为 False,这意味着通过代码和用户界面项都不可以修改受保护的工作表。这个属性设置只适用于当前会话。如果您想让代码可以在任何会话中都可以操作工作表,那么您需要每次工作簿打开的时候添加设置这个属性的代码。
•
AllowFormattingCells 参数、AllowFormattingColumns 参数和前面方法语法的完整列表中所示的其余参数允许特定的格式化功能,对应于对话框中(如图 12 所示)的选项。默认情况下,所有这些属性都是 False。
可以调用工作表的 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 属性:
•
AllowDeletingColumns, AllowDeletingRows
•
AllowFiltering
•
AllowFormattingCells, AllowFormattingColumns, AllowFormattingRows
•
AllowInsertingColumns, AllowInsertingHyperlinks, AllowInsertingRows
•
AllowSorting
•
AllowUsingPivotTables
此外,Protection 类提供 AllowEditRanges 属性,它允许您指定工作表上的可编辑区域,对应于在图 13 中所示的对话框中指定的信息。 AllowEditRanges 属性包含一个 AllowEditRange 对象集合,其中的每个对象都提供许多有用的属性,包括:
•
Range:获取或者设置对应于可编辑区域的范围
•
Title:获取或者设置可编辑区域的标题(用于在如图 13 所示的对话框中显示)。
•
Users:获取或者设置 UserAccess 对象集合(有关 UserAccess 对象的更多信息,请参考联机文档)。
在示例工作簿上的 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 类并没有提供太多成员,下面的列表描述了您可能会使用到的成员:
•
AutomaticStyles (Boolean) 向 Excel 指示是否对提纲应用自动样式。
•
SummaryColumn (XlSummaryColumn) 获取或设置摘要列的位置。 XlSummaryColumn 枚举有两个可能使用的值:xlSummaryOnLeft 和 xlSummaryOnRight。
•
SummaryRow (XlSummaryRow) 获取或设置摘要行的位置。 XlSummaryRow 枚举具有两个可能使用的值:xlSummaryAbove 和 xlSummaryBelow。
•
ShowLevels 允许您折叠提纲组或者将其扩展到您想要的行级和(或)列级。您可以给这个方法传递两个参数,如下面的代码所示:
' Visual Basic
Dim ws As Excel.Worksheet = _
DirectCast(ThisApplication.ActiveSheet, Excel.Worksheet)
' Specify RowLevels and/or ColumnLevels parameters:
ws.Outline.ShowLevels(RowLevels:=3)
// C#
Excel.Worksheet ws =
(Excel.Worksheet) ThisApplication.ActiveSheet;
// Specify RowLevels and/or ColumnLevels parameters:
ws.Outline.ShowLevels(3, Type.Missing);
示例工作表包含对应于 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 对象达到特定目的。
换句话说,由于 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 方法。
在代码中引用 Range
Range 类是很灵活的,您在编程使用范围的时候会发现它给您提供太多的选择。有时 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 对象引用的方法):
•
引用 Application 对象的 ActiveCell 属性:
' Visual Basic
rng = ThisApplication.ActiveCell
// C#
rng = ThisApplication.ActiveCell;
•
使用对象的 Range 属性指定一个区域。由于 C# 不支持参数化的非索引属性,作为替代,您必须调用 get_Range 方法,这个方法需要两个参数:
' Visual Basic
rng = ws.Range("A1")
rng = ws.Range("A1:B12")
// C#
rng = ws.get_Range("A1", Type.Missing);
rng = ws.get_Range("A1:B12", Type.Missing);
•
使用工作表的 Cells 属性,指定单个行和列值:
' Visual Basic
' The Cells collection returns an Object--
' Convert it to a Range object explicitly:
rng = DirectCast(ws.Cells(1, 1), Excel.Range)
// C#
rng = (Excel.Range)ws.Cells[1, 1];
•
指定一个范围的“角落”;您也可以直接引用范围的 Cells、Rows 或 Columns 属性;每种情况下,属性都返回一个范围:
' Visual Basic
rng = ws.Range("A1", "C5")
rng = ws.Range("A1", "C5").Cells
rng = ws.Range("A1", "C5").Rows
rng = ws.Range("A1", "C5").Columns
// C#
rng = ws.get_Range("A1", "C5");
rng = ws.get_Range("A1", "C5").Cells;
rng = ws.get_Range("A1", "C5").Rows;
rng = ws.get_Range("A1", "C5").Columns;
•
引用一个命名范围。您可以看到本文广泛使用这种技术。注意:由于 C# 的 get_Range 方法需要两个参数,而范围名只需要其中的一个参数,所以您必须为第二个参数指定 Type.Missing:
' Visual Basic
rng = ThisApplication.Range("SomeRangeName")
// C#
rng = ThisApplication.Range("SomeRangeName", Type.Missing);
•
引用特定行或特定列或行和列的范围;注意:Rows 和 Columns 属性都返回一个 Object,如果您将 Option Strict 设置成 On,则需要类型转换:
' Visual Basic
rng = DirectCast(ws.Rows(1), Excel.Range)
rng = DirectCast(ws.Rows("1:3"), Excel.Range)
rng = DirectCast(ws.Columns(3), Excel.Range)
// C#
rng = (Excel.Range)ws.Rows[1, Type.Missing];
rng = (Excel.Range)ws.Rows["1:3", Type.Missing];
rng = (Excel.Range)ws.Columns[3, Type.Missing];
警告Columns 属性的 IntelliSense 功能是容易误解的 — 它表明您必须指定行值,然后指定列值。在实际应用中,Columns 属性的值是取倒数的。对于 Rows 和 Columns 属性,不使用第二个参数。
•
使用 Application 对象的 Selection 属性返回与选定单元格对应的范围;在如图 20 所示的情况下,下面的代码片段返回字符串“$C$3”(使用“$”表示绝对坐标):
' Visual Basic
Debug.WriteLine( _
DirectCast(ThisApplication.Selection, Excel.Range).Address)
// C#
System.Diagnostics.Debug.WriteLine(
((Excel.Range)ThisApplication.Selection).
get_Address(Type.Missing, Type.Missing,
Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing));
提示Address 属性是 C# 不能直接处理的另一个参数化属性。调用 get_Address 方法来取得对应于 Range 对象的地址。 Address 属性的所有参数都是可选的,但是 get_Address 方法要取得 5 个参数 — 您可能只需要关心第三个参数,它允许您指定地址格式。
•
创建一个包含其他两个合并范围的范围(在引号内指定两个范围,并用逗号隔开):
' Visual Basic
rng = ThisApplication.Range("A1:D4, F2:G5")
' You can also use the Application object's Union
' method to retrieve the intersection of two ranges:
rng1 = ThisApplication.Range("A1:D4")
rng2 = ThisApplication.Range("F2:G5")
rng = ThisApplication.Union(rng1, rng2)
// C#
rng = ThisApplication.get_Range("A1:D4, F2:G5", Type.Missing);
// You can also use the Application object's Union
// method to retrieve the intersection of two ranges, but this
// is far more effort in C#:
rng1 = ThisApplication.get_Range("A1", "D4");
rng2 = ThisApplication.get_Range("F2", "G5");
// Note that the Union method requires you to supply thirty
// parameters:
rng = ThisApplication.Union(rng1, rng2,
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);
•
创建一个引用其他两个范围重叠部分的范围(在引号内指定两个范围,并不使用分隔符):
' Visual Basic
rng = ThisApplication.Range("A1:D16 B2:F14")
' You can also use the Application object's Intersect
' method to retrieve the intersection of two ranges:
rng1 = ThisApplication.Range("A1:D16")
rng2 = ThisApplication.Range("B2:F14")
rng = ThisApplication.Intersect(rng1, rng2)
// C#
rng = ThisApplication.get_Range("A1:D16 B2:F14", Type.Missing);
// You can also use the Application object's Intersect
// method to retrieve the intersection of two ranges. Note
// that the Intersect method requires you to pass 30 parameters:
rng1 = ThisApplication.get_Range("A1", "D16");
rng2 = ThisApplication.get_Range("B2", "F14");
rng = ThisApplication.Intersect(rng1, rng2,
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);
•
使用范围的 Offset 属性取得相对于初始范围的一个范围;以下示例在位于第一行、第一列的单元格范围中添加内容:
' Visual Basic
rng = DirectCast(ws.Cells(1, 1), Excel.Range)
Dim i As Integer
For i = 1 To 5
rng.Offset(i, 0).Value = i.ToString
Next
// C#
rng = (Excel.Range) ws.Cells[1, 1];
for (int i = 1; i <= 5; i++)
{
rng.get_Offset(i, 0).Value2 = i.ToString();
}
提示Range.Offset 属性是一个参数化的属性,因此 C# 代码不能直接取得这个属性值。作为替代,C# 开发人员必须调用 get_Offset 方法。
•
使用范围的 CurrentRegion 属性取得一个代表当前区域的范围,这个当前区域由最近的空行和列限定;例如,在图 20 中,以下表达式将当前区域的字体设置成粗体:
' Visual Basic
ThisApplication.Range("C3").CurrentRegion.Font.Bold = True
// C#
ThisApplication.get_Range("C3", Type.Missing).
CurrentRegion.Font.Bold = True;
图 20. 请求 C3 单元格的 CurrentRegion 属性返回 A1:E5 范围。
•
使用范围的 Areas 属性取得范围集合,其中每个范围对应于范围内容的一个区域。例如,下面的代码片段显示了名称为 Test 的范围内两个区域的地址,“$B$1:$E$5”和“$C$7:$G$11”(使用“$”代表绝对坐标),如图 21 所示:
' Visual Basic
rng = ThisApplication.Range("Test")
Dim i As Integer
For i = 1 To rng.Areas.Count
Debug.WriteLine(rng.Areas(i).Address)
Next
// C#
rng = ThisApplication.get_Range("Test", Type.Missing);
for (int i = 1; i <= rng.Areas.Count; i++)
{
System.Diagnostics.Debug.WriteLine(
rng.Areas[i].get_Address(Type.Missing, Type.Missing,
Excel.XlReferenceStyle.xlA1, Type.Missing, Type.Missing));
}
图 21. 范围包含的区域可以不连续,并且可以使用 Areas 属性分别取得这些区域。
•
使用 End 属性,以及一个 XlDirection 枚举值(xlUp、xlToRight、xlToLeft、xlDown)来取得一个代表区域末端单元格的范围,如同您按下了枚举值所描述的键一样;使用如图 22 所示的选定单元格,下面的代码片段会得到四个定义的范围(如代码中的注释所示):
' Visual Basic
Dim rngLeft, rngRight, rngUp, rngDown as Excel.Range
rng = DirectCast(ThisApplication.Selection, Excel.Range)
' E3
rngRight = rng.End(Excel.XlDirection.xlToRight)
' A3
rngLeft = rng.End(Excel.XlDirection.xlToLeft)
' C1
rngUp = rng.End(Excel.XlDirection.xlUp)
' C5
rngDown = rng.End(Excel.XlDirection.xlDown)
// C#
Excel.Range rngLeft, rngRight, rngUp, rngDown;
rng = (Excel.Range) ThisApplication.Selection;
// Note that the Range.End property is parameterized, so
// C# developers cannot retrieve it. You must call the
// get_End method, instead:
// E3
rngRight = rng.get_End(Excel.XlDirection.xlToRight);
// A3
rngLeft = rng.get_End(Excel.XlDirection.xlToLeft);
// C1
rngUp = rng.get_End(Excel.XlDirection.xlUp);
// C5
rngDown = rng.get_Down(Excel.XlDirection.xlDown);
图 22. 使用 End 属性返回对应于一个范围的范围。
•
使用 EntireRow 或 EntireColumn 属性引用包含特定范围的行或列。例如,下面的代码片段使用图 21 中所示的示例将第 7 行到第 11 行的字体设置成粗体:
' Visual Basic
rng = ThisApplication.Range("Test")
rng.Areas(2).EntireRow.Font.Bold = True
// C#
rng = ThisApplication.get_Range("Test", Type.Missing);
rng.Areas[2].EntireRow.Font.Bold = true;
使用技术
开发人员通常要求具有这样的能力:改变包含选定单元格的整行的字体,使文本变成粗体。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