// 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 类的各种属性。下面的列表描述了一些最常用的成员:
•
Name 返回分配给命名范围的名称。
•
RefersTo 以标准格式 ("=SheetName!$B$25") 返回一个包含实际目标地址的字符串。
•
RefersToR1C1 以“R1C1”格式 ("=SheetName!R25C2") 返回目标地址。
•
Value 返回对解析为范围的内容的命名范围的引用。
单击示例中的 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 对象提供了各种与表(包括图表和工作表)相关的事件。下面的列表包含关于许多这样的事件的信息:
•
当任何一个表被激活时,SheetActivate 都会发生。Excel 将一个包含对被激活的表的引用的 Object 变量传递给事件处理程序。
提示 正如 Excel 中传递一个引用表的 Object 的任何情况一样,在可以使用这个引用之前,您需要将引用强制转换成一个正确类型(Worksheet 或 Chart,视具体的情况而定)的变量。然而,如果您已经禁用了 Visual Basic .NET 中的 Option Strict 设置,您可以利用晚期绑定。在您键入时,您将仍然不能利用 IntelliSense,这使得编写代码变得更加困难。本文档中所有在 Sheets 集合内使用项的示例都显式地将结果强制转换成所需要的特定类型的表(Worksheet 或 Chart)。
•
在 Excel 提供默认的双击处理之前,当任何表被双击时,SheetBeforeDoubleClick 都会发生。Excel 将下列参数传递给事件处理程序:一个包含对表的引用的 Object 变量、一个包含离双击位置最近的单元格的 Range 对象、一个允许您取消默认事件处理的 Boolean 值(默认为 False)。(此事件没有出现在图表中。)
提示 所有在它们的名称中包括单词“Before”的事件都允许您取消默认的事件处理。传递给您的事件处理程序的参数通常名为 Cancle,具有默认值 False。如果将这个参数设置为 True,Excel 将不会执行事件的默认处理。
•
在 Excel 提供默认的右键单击处理之前,当任何表被右键单击时,SheetBeforeRightClick 都会发生。Exce 将下列参数传递给事件处理程序:一个包含对表的引用的 Object 变量、一个包含离右击位置最近的单元格的 Range 对象、一个允许您取消默认事件处理的 Boolean 值(默认为 False)。(此事件没有出现在图表中。)
•
当任何表被重新计算时,SheetCalculate 都会出现。Excel 将一个包含对重新计算的表的引用的 Object 传递给事件处理程序。
•
当任何工作表中的单元格发生变化(通过用户或者通过运行代码)时,SheetChange 都会发生。Excel 将一个 Object 变量(包含对表的引用)和一个 Range 变量(引用改变的范围)传递给事件处理程序。
•
当任何表单被停用时(即当它不再有焦点时),SheetDeactivate 都会发生。只有当焦点转移到同一工作簿内的另一个表时,这个事件处理程序才会运行。Excel 将一个包含对已经停用的表的引用的 Object 变量传递给事件处理程序。
•
当您单击任何工作簿内的任何超级链接时,SheetFollowHyperlink 都会发生。Excel 将一个引用包含此链接的表的 Object 变量和一个包含对您所单击的链接的引用的 Hyperlink 对象传递给事件处理程序。(示例项目使用了这个事件,从而在示例内提供了导航。)
•
当工作表上的选择改变时,SheetSelectionChange 会发生(该事件没有出现在图表中)。Excel 将一个引用选择发生改变的表的 Object 变量和一个引用新选择的 Range 变量传递给事件处理程序。(注意,在最初的选择发生改变之前,Excel 没有传递关于最初的选择的信息。)
注 这一部分中的每个事件也可用作 Workbook 类提供的事件。如果该事件是由 Application 对象提供的,则它可以被 Excel 内当前打开的任何一个表引发。当它是由 Workbook 对象提供的,则该事件只有在它影响特定工作簿中的一个表时才会发生。此外,您还将发现 Worksheet 类提供的相同事件。在这种情况下,事件名不包含单词“Sheet”(例如,您将会找到 FollowHyperlink 而不是 SheetFollowHyperlink,等等),并且事件处理程序不传递对表的引用 — 这种信息隐含在接收事件的对象中。另外,事件及其使用方法和参数与此处您所看到的事件相同。
Window 行为
Application 对象(和相应的 Workbook 对象)提供了各种处理 Window 对象的行为的事件。下面的列表描述了这些事件:
•
当任何窗口被激活时,WindowActivate 都会发生。Excel 将下面两个参数传递给事件处理程序:一个是 Workbook 对象,这个对象引用提供窗口的工作簿;一个是引用被选择的窗口的 Window 对象。与其他激活事件一样,这个事件也是只有在 Excel 内的焦点移动时才激发。切换到另一个应用程序,然后再回到 Excel 时,不会引发此事件。
•
当任何窗口被停用时,WindowDeactivate 都会发生。有关更多信息,请参阅 WindowActivate 事件描述。
•
当任何工作簿窗口重新调整大小时,WindowResize 都会发生。Excel 将一个引用提供窗口的工作簿的 Workbook 对象和一个引用大小重新调整的窗口的 Window 对象传递给事件处理程序。
注由 Workbook 类提供的事件中,事件处理程序不会接收对 Workbook 的引用 — 这种信息隐含在引发此事件的对象中。
Workbook 管理
Application 对象提供了各种当您与任何 Workbook 对象交互时都会发生的事件。这些事件过程中的每一个都接收 Workbook 变量,该变量指示参与事件的特定工作簿。下面的列表描述了可用事件的一个子集:
•
当创建一个新的工作簿时,NewWorkbook 会发生。Excel 将一个引用新的工作簿的 Workbook 变量传递给事件处理程序。(此事件只由 Application 类提供。)
•
当任何工作簿被激活时,WorkbookActivate 都会发生。Excel 将一个引用被激活的工作簿的 Workbook 变量传递给事件处理程序。(与其他的“激活”事件一样,只有在您从一个工作簿切换到另一个工作簿时这个事件才发生。)
•
当一个打开的工作簿刚好在默认事件处理之前关闭时,WorkbookBeforeClose 会发生。Excel 将一个引用将要关闭的工作簿的 Workbook 变量以及一个允许事件处理程序取消默认事件处理(即保持工作簿打开)的 Boolean 值(默认为 False)传递给事件处理程序。
警告如果您草率地将 Cancel 参数设置为 True,而不考虑任何条件,则所有的工作簿将永远不会被关闭。
•
当工作簿内的打印刚好在默认事件处理之前开始时,WorkbookBeforePrint 会发生。Excel 将一个引用包含打印内容的工作簿的 Workbook 变量以及一个允许事件处理程序取消默认事件处理(即跳过请求的打印)的 Boolean 值(默认为 False)传递给事件处理程序。
•
当刚好在默认事件处理之前保存工作簿时,WorkbookBeforeSave 会发生。Excel 将一个引用保存的工作簿的 Workbook 变量以及一个允许事件处理程序取消默认事件处理(即取消保存)的 Boolean 值(默认为 False)传递给事件处理程序。
•
当任何工作簿被停用时,WorkbookDeactivate 都会发生。Excel 将一个引用已经停用的工作簿的 Workbook 变量传递给事件处理程序。(与其他的“激活”事件一样,这个事件只有在您从一个工作簿切换到另一个工作簿时才会发生。)
•
当将新的表添加到工作簿时,WorkbookNewSheet 会发生。Excel 将一个引用工作簿的 Workbook 变量和一个引用新表的 Object 变量传递给事件处理程序。
•
当一个工作簿打开时,WorkbookOpen 会发生。Excel 将一个引用新打开的工作簿的 Workbook 变量传递给事件处理程序。
注Workbook 类提供了自己的一组事件,与您在此处看到的事件非常相似。所有以“Workbook”开头的事件在没有该委托(“Activate”而不是“WorkbookActivate”,等等)的情况下出现在 Workbook 类的事件列表中。Workbook 类事件处理程序不接收 Workbook 变量作为参数;该信息隐含在引发这个事件的对象中。此外,Workbook 类还提供了其他 Application 对象事件的镜像,但是只为单个工作簿捕获它们,这与为所有的工作簿捕获这些事件形成了对比。本文档的剩余部分将不讨论事件,因为您现在已经了解了一些您最有可能会使用的事件。
Workbook 类
正如您可能想象到的那样,Workbook 类代表了 Excel 应用程序内的一个单一的工作簿。在这一部分,您将会了解这个类的一些成员,包括那些最常使用的属性和方法。
提示 许多 Application 类的成员也作为 Workbook 类的成员加以介绍。在这种情况下,其属性适用于特定的工作簿,而不适用于活动工作簿。这一部分所要讨论的成员远比上一部分中讨论的少,主要因为您对许多提到的成员已经有所了解。
Workbook 类的属性
Workbook 类提供了大量的属性(大约 90 个),并且有许多属性处理多数开发人员从不会考虑到的特殊情况;例如,AutoUpdateFrequency 属性返回共享工作簿的自动更新的分钟数;如果工作簿使用 1904 日期系统(一种日期顺序方案,它将 1904 年 1 月 2 日作为对应于值 1 的日期,通常使用于 Macintosh 计算机),Date1904 属性会返回 True 值;PasswordEncryptionAlgorithm 属性可以让您设置用于加密密码的确切算法,等等。
这一部分只是介绍您最可能用到的 Workbook 对象属性,而不是试图全面介绍其众多属性。通常的规则是:如果您需要工作簿的某一行为,而其他人可能已经请求该行为,实际上最可能的情况是一个属性允许该行为,而通常由一个方法提供该行为。在您向一个工作簿中添加自己的代码之前要仔细检查文档。
以下列表描述了一些最常使用的 Workbook 属性:
•
Name、FullName、Path(字符串,只读):这些属性分别返回不同版本的工作簿名称。FullName 返回完整路径名称,包括工作簿文件名。Name 只是返回名称部分,而 Path 则只返回路径部分。单击示例工作簿中的 Name Information 链接来运行以下代码,并返回信息,如图 5 所示:
' Visual Basic
ThisApplication.Range("WorkbookName").Value = _
ThisWorkbook.Name
ThisApplication.Range("WorkbookPath").Value = _
ThisWorkbook.Path
ThisApplication.Range("WorkbookFullName").Value = _
ThisWorkbook.FullName
// C#
ThisApplication.get_Range("WorkbookName", Type.Missing).
Value2 = ThisWorkbook.Name;
ThisApplication.get_Range("WorkbookPath", Type.Missing).
Value2 = ThisWorkbook.Path;
ThisApplication.get_Range("WorkbookFullName", Type.Missing).
Value2 = ThisWorkbook.FullName;
图 5. 使用 Workbook 属性检索名称的有关信息。
•
Password(字符串):获取或者设置和工作簿相关的密码。如果您指定了一个非空的密码,工作簿的 HasPassword 属性也会返回 True。您可以检索 Password 属性,但是它的值总是“********”。单击示例工作簿中的 Set Password 链接来运行以下代码,它可以根据您是提供文本还是只提供空字符串来设置或清除工作簿密码。这个示例使用示例项目中名为 Password 的窗体,它提供一个文本框和一个 Password 属性:
' Visual Basic
Private Sub SetPassword()
Dim frm As New Password
If frm.ShowDialog = DialogResult.OK Then
ThisWorkbook.Password = frm.Password
End If
frm.Dispose()
End Sub
// C#
private void SetPassword()
{
Password frm = new Password();
if (frm.ShowDialog() == DialogResult.OK)
ThisWorkbook.Password = frm.Value;
frm.Dispose();
}
•
PrecisionAsDisplayed(布尔值):如果为 True,则 Excel使用以十进制显示的数字进行计算。如果为 False(默认值),则 Excel 使用所有可用的十进制数进行计算,甚至包括那些根本没有显示的部分。图 6 显示此属性设置成 True 的示例工作簿。第 C 列中的每个值都是第 B 列中值的副本,但是第 C 列中的数字格式已经设置成了只显示两个十进制位。要注意到,如果将 PrecisionAsDisplayed 属性设置成 True,求和就会不一样,因为经过四舍五入后实际值会不同。。如果您单击 PrecisionAsDisplayed= False 链接,求和又一样了。单击会调用以下过程,传递 True 或 False 值(取决于您所单击的链接):
' Visual Basic
Private Sub TestPrecisionAsDisplayed( _
ByVal IsPrecisionAsDisplayedOn As Boolean)
ThisWorkbook.PrecisionAsDisplayed = IsPrecisionAsDisplayedOn
End Sub
// C#
private void TestPrecisionAsDisplayed(
bool IsPrecisionAsDisplayedOn)
{
ThisWorkbook.PrecisionAsDisplayed =
IsPrecisionAsDisplayedOn;
}
图 6. 将PrecisionAsDisplayed 属性设置成 True,Excel 只使用显示的十进制数进行计算。
•
ReadOnly(布尔值,只读):如果工作簿以只读的方式打开,则此属性返回 True 值。此时如果您无法将数据保存到工作簿,那么您可以在应用程序中采取其他不同操作。
•
Saved(布尔值):用来获取或设置工作簿的保存状态。如果用户已经对工作簿的内容或结构进行了修改,则 Saved 属性就为 True。如果试图关闭工作簿或者退出 Excel,将会出现一个警报提示您保存工作簿(除非您已经将 Application.DisplayAlerts 属性设置成 False)。如果您在代码中将 Saved 属性值设置成 False,Excel 就会认为您的工作簿已经保存,并且不会再次提醒您保存。
使用 Document 属性
正如其他的 Office 应用程序一样,Excel 允许您在保存工作簿的同时保存文档属性。Excel 提供了许多内置属性,并且您也可以添加自己的属性。选择“文件|属性”来显示如图 7 所示的对话框,并且您也可以选择“自定义”选项卡来创建和修改自定义属性。
图 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 类的成员会很有用。
•
Visible 属性可以让您在不删除和重建表的情况下显示或隐藏一个现有的表。可将 Visibility 属性设置成 XlSheetVisibility 枚举值(XlSheetHidden、XlSheetVeryHidden、xlSheetVisible)中的一个值。使用 XlSheetHidden 可以让用户通过 Excel 界面隐藏表;使用 XlSheetVeryHidden,则要求您运行代码来取消隐藏表:
' Visual Basic
DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet).Visible = _
Excel.XlSheetVisibility.xlSheetVeryHidden
// C#
((Excel.Worksheet) ThisWorkbook.Sheets[1]).Visible =
Excel.XlSheetVisibility.xlSheetVeryHidden;
•
Add 方法允许您将一个新表添加到工作簿中的表集合中,并且可以接受四个可选参数,这些参数可以指明表的位置、要添加的表数和表的类型(工作表、图表等):
' Visual Basic
Dim sh As Excel.Sheet = ThisWorkbook.Sheets.Add()
// C#
Excel.Sheet sh = ThisWorkbook.Sheets.Add(
Type.Missing, Type.Missing, Type.Missing, Type.Missing);
•
Copy 方法创建一个表的副本,并且将表插入到指定的位置。您可以随意指定将新表插入到现有表的前面或者后面。如果您不指定位置,Excel 会创建一个新的工作簿来存放这个新表。下面的代码片段复制当前工作簿中的第一个表,并且将副本放在第三个表的后面:
' Visual Basic
DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _
Copy(After:=ThisWorkbook.Sheets((3)))
// C#
((Excel.Worksheet) ThisWorkbook.Sheets[1]).
Copy(Type.Missing, ThisWorkbook.Sheets[3]);.
•
Delete 方法删除一个指定的表:
' Visual Basic
DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet).Delete
// C#
((Excel.Worksheet) ThisWorkbook.Sheets[1]).Delete();
•
FillAcrossSheets 方法将工作簿内一个表的范围中的数据复制到所有其他表。您需要指定一个范围,以及是否要复制数据、进行格式化,或全部,剩下的工作 Excel 会去处理。下面的代码片段在工作簿中,将一个表中名称为 Data 的范围的数据和复制到所有表的同一区域中,并对这些数据进行格式化:
' Visual Basic
ThisWorkbook.Sheets.FillAcrossSheets( _
ThisApplication.Range("Data"), Excel.XlFillWith.xlFillWithAll)
// C#
ThisWorkbook.Sheets.FillAcrossSheets(
ThisApplication.get_Range("Data", Type.Missing),
Excel.XlFillWith.xlFillWithAll);
•
Move 方法和 Copy 方法很类似,只不过最终您得到的是表的一个实例。您可以指定将表放到您的表的前面,或者放到后面(但不可以同时指定)。同样的,如果您没有为移动的表指定一个位置,Excel 会创建一个新的工作簿来包含它。下面的代码片段将第一个工作表移到最后:
' Visual Basic
Dim shts As Excel.Sheets = ThisWorkbook.Sheets
DirectCast(shts(1), Excel.Worksheet).Move(After:=shts(shts.Count))
// C#
Excel.Sheets shts = ThisWorkbook.Sheets;
((Excel.Worksheet)shts[1]).Move(Type.Missing, shts[shts.Count]);
提示 如果出于某种原因您要对工作簿中的表列表进行排序,您可以使用 Move 方法来进行一个低效的冒泡排序。当然,您不可能有太多的表,所以排序速度不是个问题。
•
PrintOut 方法允许您打印选择的对象(这个方法适用于多个不同的对象)。您可以指定许多可选的参数,包括:要打印的页数(起始页和终止页)、副本数量、打印前是否进行预览、要使用的打印机的名称、是否打印到一个文件、是否进行逐份打印以及您要打印到的文件名。下面的例子使用默认的打印机打印指定的表、只打印第一页、打印两份副本,并且在打印前预览文档:
' Visual Basic
DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _
PrintOut(From:=1, To:=1, Copies:=2, Preview:=True)
// C#
((Excel.Worksheet)ThisApplication.Sheets[1]).
PrintOut(1, 1, 2, true, Type.Missing, Type.Missing,
Type.Missing, Type.Missing);
•
PrintPreview 方法允许您在 PrintPreview 窗口显示指定的对象,并且可以选择禁止更改页面布局:
' Visual Basic
DirectCast(ThisWorkbook.Sheets(1), Excel.Worksheet). _
PrintPreview(False)
// C#
((Excel.Worksheet)ThisApplication.Sheets[1]).PrintPreview(false);
•
Select 方法选择指定的对象,并且改变用户的选择(可使用 Activate 方法使对象获得焦点,而不需改变用户的选择。)您可以有选择地提供一个被当前选择取代的对象的引用。下面的代码片段选择第一个工作表:
' Visual Basic
ActiveWorkbook.Sheets(1).Select()
// C#
((Excel.Worksheet)ThisApplication.Sheets[1]).Select(Type.Missing);
提示 在这一部分中列出的许多方法也适用于其他的类。例如,PrintOut 方法是由以下类提供的:Chart、Charts、Range、Sheets、 Window、Workbook、Worksheet 和 Worksheets。这些方法的具体使用是相同的,只不过是作用在不同的对象上而已。Select 方法几乎适用于任何一种可选择的对象(并且这样的对象有很多)。
Workbook 类的方法
Workbook 类提供了大量的方法,其中有许多方法处理一些非常特殊的情况。这一部分探讨一些在每个应用程序中您都可能会用到的方法,而不是详细介绍各种方法,并将一些难理解的方法放在后面的章节中介绍,下面的列表描述了一些您最可能使用的方法:
•
Activate 方法激活一个工作簿,并且选择工作簿中的第一个工作表:
' Visual Basic
ThisApplication.Workbooks(1).Activate