改变
积极的改变才意味着进步

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];
            

指定一个范围的“角落”;您也可以直接引用范围的 CellsRowsColumns 属性;每种情况下,属性都返回一个范围:

' 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);
            

引用特定行或特定列或行和列的范围;注意:RowsColumns 属性都返回一个 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 属性的值是取倒数的。对于 RowsColumns 属性,不使用第二个参数。

使用 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 枚举值(xlUpxlToRightxlToLeftxlDown)来取得一个代表区域末端单元格的范围,如同您按下了枚举值所描述的键一样;使用如图 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 属性返回对应于一个范围的范围。

使用 EntireRowEntireColumn 属性引用包含特定范围的行或列。例如,下面的代码片段使用图 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
// 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;
}

这个示例采用如下步骤来使当前行变成粗体,并且使前一次变成粗体的行变回原来的状态:

声明一个变量(在 Visual Basic 中,类型为静态)用来跟踪前面选定的行:

' Visual Basic
            Static intRow As Integer
            // C#
            private int LastBoldedRow = 0;
            

使用 Application.ActiveCell 属性取得对当前单元格的引用:

' Visual Basic
            private int LastBoldedRow = 0;
            Dim rngCell As Excel.Range = ThisApplication.ActiveCell
            // C#
            Excel.Range rngCell = ThisApplication.ActiveCell;
            

使用活动单元格的 EntireRow 属性使当前行变成粗体:

' Visual Basic
            rngCell.EntireRow.Font.Bold = True
            // C#
            rngCell.EntireRow.Font.Bold = true;
            

确保 intRow 的当前值不为 0,如果为 0,则表明这是第一次运行这段代码:

' Visual Basic
            If intRow <> 0 Then
            ' Code removed here...
            End If
            // C#
            if (LastBoldedRow != 0)
            {
            // Code removed here...
            }
            

确保当前行和前面的行不同。如果当前行和前面的行不同,代码只需修改行的状态。Row 属性返回一个整数值来指明对应于范围的行:

' Visual Basic
            If rngCell.Row <> intRow Then
            ' Code removed here...
            End If
            // C#
            if (rngCell.Row != LastBoldedRow)
            {
            // Code removed here...
            }
            

检索一个代表前面选定行的范围的引用,并将那一行设置成不为粗体:

' Visual Basic
            Dim rng As Excel.Range = _
            DirectCast(ws.Rows(intRow), Excel.Range)
            rng.Font.Bold = False
            // C#
            Excel.Range rng =
            (Excel.Range)ws.Rows[LastBoldedRow, Type.Missing];
            rng.Font.Bold = false;
            

示例工作簿从它的 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 枚举(xlFillDaysxlFillFormatsxlFillSeriesxlFillWeekdaysxlGrowthTrendxlFillCopyxlFillDefaultxlFillMonthsxlFillValuesxlFillYearsxlLinearTrend)。如果您不指定一个填充类型,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 方法的范围,它指定填充的“起点”。

将要被填充的范围,它作为参数传递给 AutoFill 方法;目的范围必须包含源范围。

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# 开发人员不需要担心这个问题,因为他们在每个方法调用时都必须提供所有参数。

表 4. Range.Find 方法的参数
参数 类型 说明

What(必需的)

对象

要查找的数据;可以是一个字符串或者任何 Excel 数据类型。

After

范围

您想从这个范围的后面开始搜索(在搜索中,不包括这个单元格);如果不指定单元格,则从范围的左上角开始搜索。

LookIn

XlFindLookin(xlValue、xlComments、xlFormulas)

要搜索的信息类型;不能用 Or 运算符组合查询。

LookAt

XlLookAt(xlWhole、xlPart)

确定搜索匹配所有单元格,还是部分单元格。

SearchOrder

XlSearchOrder(xlByRows、xlByColumns)

决定搜索顺序;xlByRows(默认值)将横向搜索,然后纵向搜索;xlByColumns 将纵向搜索,然后横向搜索。

SearchDirection

XlSearchDirection(xlNext、xlPrevious)

确定搜索的方向;默认值是 xlNext。

MatchCase

布尔值

确定搜索是否区分大小写。

MatchByte

布尔值

确定是否双字节字符只和双字节匹配 (True) 或者也可以和单字节字符匹配 (False);只有当您安装了对双字节支持时才适用。

以下示例来自示例工作簿,它搜索一个范围(名称为“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);
}
}

这段代码采取这些步骤来实现其目的:

声明 Excel.Range 变量来跟踪整个范围、第一个被发现的范围和当前发现的范围:

' Visual Basic
            Dim rng As Excel.Range = ThisApplication.Range("Fruits")
            Dim rngFound As Excel.Range
            Dim rngFoundFirst As Excel.Range
            // C#
            Excel.Range rng = ThisApplication.
            get_Range("Fruits", Type.Missing);
            Excel.Range rngFound;
            Excel.Range rngFoundFirst = null;
            

搜索第一个匹配值,指定所有的参数(要在以后搜索的单元格除外) — 默认情况下,搜索从范围左上角的单元格开始 — 然后在单元格值中搜索“apples”,匹配部分值,逐行向前搜索,并且不区分大小写:

' Visual Basic
            rngFound = rng.Find( _
            "apples", , _
            Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart, _
            Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext, _
            False)
            // C#
            rngFound = rng.Find("apples", Type.Missing,
            Excel.XlFindLookIn.xlValues, Excel.XlLookAt.xlPart,
            Excel.XlSearchOrder.xlByRows, Excel.XlSearchDirection.xlNext,
            false, Type.Missing, Type.Missing);
            

只要还能发现匹配值搜索就会继续下去:

' Visual Basic
            While Not rngFound Is Nothing
            ' Code removed here...
            End While
            // C#
            while (rngFound != null)
            {
            // Code removed here...
            }
            

将第一个发现的范围 (rngFoundFirst) 和 Nothing 进行比较,如果代码只发现第一个匹配值,rngFoundFirst 就会为 Nothing,也只有在这种情况下它才会为 Nothing。在这种情况下,代码将找到的范围保存起来;否则,如果找到的范围地址和第一个找到的范围地址一致,代码会退出循环。

' Visual Basic
            If rngFoundFirst Is Nothing Then
            rngFoundFirst = rngFound
            ElseIf rngFound.Address = rngFoundFirst.Address Then
            Exit While
            End If
            // C#
            if (rngFoundFirst == null )
            {
            rngFoundFirst = rngFound;
            }
            else if (GetAddress(rngFound) == GetAddress(rngFoundFirst))
            {
            break;
            }
            

设置找到的范围的外观:

' Visual Basic
            With rngFound.Font
            .Color = ColorTranslator.ToOle(Color.Red)
            .Bold = True
            End With
            // C#
            rngFound.Font.Color = ColorTranslator.ToOle(Color.Red);
            rngFound.Font.Bold = true;
            

执行另一次搜索:

' Visual Basic
            rngFound = rng.FindNext(rngFound)
            // C#
            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# 开发人员则必须为每个参数赋予值。)

表 5. Sort 方法的参数
参数 类型 说明

Key1

Object(String 或 Range)

首要排序字段,可以是一个范围名称 (String),或是一个 Range 对象,确定了要排序的值。

Order1

XlSortOrder(xlAscending、xlDescending)

为 Key1 中指定的值决定排序顺序。

Key2

Object(String 或 Range)

第二个排序字段,排序透视表时无法使用。

Type

Object

当对透视表进行排序时,指定对哪些元素排序;对一个普通范围则没有影响。

Order2

XlSortOrder

为在 Key2 中指定的值决定排序顺序。

Key3

Object(String 或 Range)

第三个排序字段,不能使用于透视表。

Order3

XlSortOrder

为在 Key3 中指定的值决定排序顺序。

Header

XlYesNoGuess(xlGuess、xlNo、xlYes)

指定第一行是否包含头信息,默认值为 xlNo;如果想让 Excel 自己去推测,就指定为 xlGuess。

OrderCustom

Integer

为自定义排序顺序列表指定一个基于 1 的索引;如果不指定这个参数,则使用默认排序顺序。图 28 显示了一种创建自定义排序顺序的技术。对于这个例子,将这个参数指定为 6 将基于“fruits”自定义顺序进行排序。

MatchCase

Boolean

设置成 True 就会进行区分大小写的排序,设置成 False 则进行不区分大小写的排序;不能用于透视表。

Orientation

XlSortOrientation (xlSortRows, xlSortColumns)

排序方向。

SortMethod

XlSortMethod(xlStroke、xlPinYin)

指定排序方法;不能适用于所有语言(当前值只适用于对汉字进行排序,而不适用于对其他语言排序)。

DataOption1

XlSortDataOption (xlSortTextAsNumbers, xlSortNormal)

指定如何对 Key1 中指定的范围进行文本排序;不能用于透视表排序。

DataOption2

XlSortDataOption

指定如何对 Key2 中指定的范围进行文本排序;不能用于透视表排序。

DataOption3

XlSortDataOption

指定如何对 Key3 中指定的范围进行文本排序;不能用于透视表排序。

提示当调用像这样的方法时,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);
}
posted on 2007-09-07 15:18  赵俊  阅读(2382)  评论(0编辑  收藏  举报