演练:Office 编程(C# 和 Visual Basic)
Visual Studio 在 C# 和 Visual Basic 中提供了改进 Microsoft Office 编程的功能。 有用的 C# 功能包括命名参数和可选参数以及类型为 dynamic
的返回值。 在 COM 编程中,可以省略 ref
关键字并获得索引属性的访问权限。 Visual Basic 中的功能包括自动实现的属性、Lambda 表达式语句和集合初始值设定项。
两种语言都支持嵌入类型信息,从而允许在不向用户的计算机部署主互操作程序集 (PIA) 的情况下部署与 COM 组件交互的程序集。 有关详细信息,请参见演练:嵌入托管程序集中的类型。
本演练演示 Office 编程上下文中的这些功能,但其中许多功能在常规编程中也极为有用。 本演练将使用 Excel 外接应用程序创建 Excel 工作簿。 然后,将创建包含工作簿链接的 Word 文档。 最后,将介绍如何启用和禁用 PIA 依赖项。
先决条件
若要完成本演练,计算机上必须安装 Microsoft Office Excel 和 Microsoft Office Word。
注意
以下说明中的某些 Visual Studio 用户界面元素在计算机上出现的名称或位置可能会不同。 这些元素取决于你所使用的 Visual Studio 版本和你所使用的设置。 有关详细信息,请参阅个性化设置 IDE。
设置 Excel 外接应用程序
启动 Visual Studio。
在 “文件” 菜单上,指向 “新建” ,然后单击 “项目” 。
在“安装的模板”窗格中,展开“Visual Basic”或“Visual C#”,再展开“Office”,然后单击 Office 产品的版本年份。
在“模板”窗格中,单击“Excel<版本>加载项”。
查看“模板”窗格的顶部,确保“.NET Framework 4”或更高版本出现在“目标框架”框中。
如果需要,在“名称”框中键入项目的名称。
单击 “确定” 。
新项目将出现在“解决方案资源管理器”中。
添加引用
在“解决方案资源管理器”中,右键单击你的项目名称,然后单击“添加引用”。 此时会显示“添加引用”对话框。
在“程序集”选项卡上,在“组件名称”列表中选择“Microsoft.Office.Interop.Excel”版本
<version>.0.0.0
(有关 Office 产品版本号的键,请参阅 Microsoft 版本),然后按住 Ctrl 键并选择“Microsoft.Office.Interop.Word”,version <version>.0.0.0
。 如果未看到程序集,则可能需要确保安装并显示它们(参阅如何:安装 Office 主互操作程序集)。单击 “确定” 。
添加必要的 Imports 语句或 using 指令
在“解决方案资源管理器”中,右键单击“ThisAddIn.vb”或“ThisAddIn.cs”文件,然后单击“查看代码”。
将以下
Imports
语句 (Visual Basic) 或using
指令 (C#) 添加到代码文件的顶部(如果不存在)。using System.Collections.Generic; using Excel = Microsoft.Office.Interop.Excel; using Word = Microsoft.Office.Interop.Word;
Imports Microsoft.Office.Interop
创建银行帐户列表
在“解决方案资源管理器”中,右键单击你的项目名称,单击“添加”,然后单击“类”。 如果使用的是 Visual Basic,则将类命名为 Account.vb;如果使用的是 C#,则将类命名为 Account.cs。 单击 添加。
将
Account
类的定义替换为以下代码。 类定义使用自动实现的属性。 有关详细信息,请参阅自动实现的属性。class Account { public int ID { get; set; } public double Balance { get; set; } }
Public Class Account Property ID As Integer = -1 Property Balance As Double End Class
若要创建包含两个帐户的
bankAccounts
列表,请将以下代码添加到 ThisAddIn.vb 或 ThisAddIn.cs 中的ThisAddIn_Startup
方法。 列表声明使用集合初始值设定项。 有关详细信息,请参阅集合初始值设定项。var bankAccounts = new List<Account> { new Account { ID = 345, Balance = 541.27 }, new Account { ID = 123, Balance = -127.44 } };
Dim bankAccounts As New List(Of Account) From { New Account With { .ID = 345, .Balance = 541.27 }, New Account With { .ID = 123, .Balance = -127.44 } }
将数据导出到 Excel
在相同的文件中,将以下方法添加到
ThisAddIn
类。 该方法设置 Excel 工作薄并将数据导出到工作簿。void DisplayInExcel(IEnumerable<Account> accounts, Action<Account, Excel.Range> DisplayFunc) { var excelApp = this.Application; // Add a new Excel workbook. excelApp.Workbooks.Add(); excelApp.Visible = true; excelApp.Range["A1"].Value = "ID"; excelApp.Range["B1"].Value = "Balance"; excelApp.Range["A2"].Select(); foreach (var ac in accounts) { DisplayFunc(ac, excelApp.ActiveCell); excelApp.ActiveCell.Offset[1, 0].Select(); } // Copy the results to the Clipboard. excelApp.Range["A1:B3"].Copy(); }
Sub DisplayInExcel(ByVal accounts As IEnumerable(Of Account), ByVal DisplayAction As Action(Of Account, Excel.Range)) With Me.Application ' Add a new Excel workbook. .Workbooks.Add() .Visible = True .Range("A1").Value = "ID" .Range("B1").Value = "Balance" .Range("A2").Select() For Each ac In accounts DisplayAction(ac, .ActiveCell) .ActiveCell.Offset(1, 0).Select() Next ' Copy the results to the Clipboard. .Range("A1:B3").Copy() End With End Sub
此方法使用 C# 的两项新功能。 Visual Basic 中已存在这两项功能。
方法 Add 有一个可选参数,用于指定特定的模板。 如果希望使用形参的默认值,你可以借助可选形参以忽略该形参的实参。 由于上一个示例中未发送任何参数,
Add
将使用默认模板并创建新的工作簿。 C# 早期版本中的等效语句要求提供一个占位符参数:excelApp.Workbooks.Add(Type.Missing)
。有关详细信息,请参阅命名参数和可选参数。
Range 对象的
Range
和Offset
属性使用“索引属性”功能。 此功能允许你通过以下典型 C# 语法从 COM 类型使用这些属性。 索引属性还允许你使用Value
对象的Range
属性,因此不必使用Value2
属性。Value
属性已编入索引,但索引是可选的。 在以下示例中,可选自变量和索引属性配合使用。// Visual C# 2010 provides indexed properties for COM programming. excelApp.Range["A1"].Value = "ID"; excelApp.ActiveCell.Offset[1, 0].Select();
在早期版本的语言中,需要以下特殊语法。
// In Visual C# 2008, you cannot access the Range, Offset, and Value // properties directly. excelApp.get_Range("A1").Value2 = "ID"; excelApp.ActiveCell.get_Offset(1, 0).Select();
你不能创建自己的索引属性。 该功能仅支持使用现有索引属性。
有关详细信息,请参阅如何在 COM 互操作编程中使用索引属性。
在
DisplayInExcel
的末尾添加以下代码以将列宽调整为适合内容。excelApp.Columns[1].AutoFit(); excelApp.Columns[2].AutoFit();
' Add the following two lines at the end of the With statement. .Columns(1).AutoFit() .Columns(2).AutoFit()
这些新增内容介绍了 C# 中的另一功能:处理从 COM 主机返回的
Object
值(如 Office),就像它们具有 dynamic 类型一样。 当“嵌入互操作类型”设置为其默认值True
时,或者由 EmbedInteropTypes 编译器选项引用程序集时,会自动发生这种情况。例如,
excelApp.Columns[1]
返回Object
,并且AutoFit
是 Excel Range 方法。 如果没有dynamic
,你必须将excelApp.Columns[1]
返回的对象强制转换为Range
的实例,然后才能调用AutoFit
方法。// Casting is required in Visual C# 2008. ((Excel.Range)excelApp.Columns[1]).AutoFit(); // Casting is not required in Visual C# 2010. excelApp.Columns[1].AutoFit();
有关嵌入互操作类型的详细信息,请参阅本主题后面部分的“查找 PIA 引用”和“还原 PIA 依赖项”程序。 有关
dynamic
的详细信息,请参阅 dynamic 或使用类型 dynamic。
调用 DisplayInExcel
在
ThisAddIn_StartUp
方法的末尾添加以下代码。 对DisplayInExcel
的调用包含两个参数。 第一个参数是要处理的帐户列表的名称。 第二个参数是定义如何处理数据的多行 lambda 表达式。 每个帐户的ID
和balance
值都显示在相邻的单元格中,如果余额小于零,则相应的行显示为红色。 有关详细信息,请参阅 Lambda 表达式。DisplayInExcel(bankAccounts, (account, cell) => // This multiline lambda expression sets custom processing rules // for the bankAccounts. { cell.Value = account.ID; cell.Offset[0, 1].Value = account.Balance; if (account.Balance < 0) { cell.Interior.Color = 255; cell.Offset[0, 1].Interior.Color = 255; } });
DisplayInExcel(bankAccounts, Sub(account, cell) ' This multiline lambda expression sets custom ' processing rules for the bankAccounts. cell.Value = account.ID cell.Offset(0, 1).Value = account.Balance If account.Balance < 0 Then cell.Interior.Color = RGB(255, 0, 0) cell.Offset(0, 1).Interior.Color = RGB(255, 0, 0) End If End Sub)
若要运行程序,请按 F5。 出现包含帐户数据的 Excel 工作表。
添加 Word 文档
在
ThisAddIn_StartUp
方法末尾添加以下代码,以创建包含指向 Excel 工作簿的链接的 Word 文档。var wordApp = new Word.Application(); wordApp.Visible = true; wordApp.Documents.Add(); wordApp.Selection.PasteSpecial(Link: true, DisplayAsIcon: true);
Dim wordApp As New Word.Application wordApp.Visible = True wordApp.Documents.Add() wordApp.Selection.PasteSpecial(Link:=True, DisplayAsIcon:=True)
此代码展示 C# 中的几项新功能:省略 COM 编程中的
ref
关键字、命名参数以及可选参数的能力。 Visual Basic 中已存在这些功能。 PasteSpecial 方法有七个参数,所有参数都定义为可选引用参数。 通过命名实参和可选实参,你可以指定希望按名称访问的形参并仅将实参发送到这些形参。 在本示例中,发送实参以指示应创建指向剪贴板上工作簿的链接(形参Link
)并指示该链接应在 Word 文档中显示为图标(形参DisplayAsIcon
)。 Visual C# 还允许忽略这些参数的ref
关键字。
要运行应用程序
- 按 F5 运行该应用程序。 Excel 启动并显示包含
bankAccounts
中两个帐户的信息的表。 然后,出现包含指向 Excel 表的 Word 文档。
清理已完成的项目
- 在 Visual Studio 中,单击“生成”菜单上的“清理解决方案”。 否则,每次在计算机上打开 Excel 时都会运行外接应用程序。
查找 PIA 引用
再次运行应用程序,但不单击“清理解决方案”。
选择“开始”。 找到“Microsoft Visual Studio <版本>”,然后打开开发人员命令提示。
在“Visual Studio 的开发人员命令提示”窗口中键入
ildasm
,然后按 Enter。 此时将出现 IL DASM 窗口。在 IL DASM 窗口的“文件”菜单上,选择“文件”>“打开”。 双击“Visual Studio <版本>”,然后双击“项目”。 打开项目的文件夹,在 bin/Debug 文件夹中查找项目名称.dll。 双击 项目名称.dll。 新窗口将显示项目的属性以及对其他模块和程序集的引用。 注意,命名空间
Microsoft.Office.Interop.Excel
和Microsoft.Office.Interop.Word
包含在程序集中。 在 Visual Studio 中,编译器默认将所需的类型从引用的 PIA 导入程序集。有关详细信息,请参阅如何:查看程序集内容。
双击“清单”图标。 此时将出现包含程序集列表的窗口,这些程序集包含项目所引用的项。
Microsoft.Office.Interop.Excel
和Microsoft.Office.Interop.Word
未包含在列表中。 由于项目需要的类型已导入程序集中,因此不需要引用 PIA。 这使得部署变得更加容易。 用户的计算机上不必存在 PIA,因为应用程序不需要部署特定版本的 PIA,应用程序可设计为与多个版本的 Office 配合使用,前提是所有版本中都存在必要的 API。由于不再需要部署 PIA,你可以提前创建可与多个版本的 Office(包括之前的版本)配合使用的应用程序。 但是,仅当你的代码不使用你当前所使用 Office 版本中不可用的任何 API 时,此情况才适用。 特殊 API 在早期版本中是否可用并不始终明确,因此不建议使用早期版本的 Office。
注意
在 Office 2003 以前,Office 并不发布 PIA。 因此,生成适用于 Office 2002 或早期版本的互操作程序集的唯一方法是导入 COM 引用。
关闭清单窗口和程序集窗口。
还原 PIA 依赖项
在“解决方案资源管理器”中,单击“显示所有文件”按钮。 展开“引用”文件夹并选择“Microsoft.Office.Interop.Excel”。 按 F4 以显示“属性”窗口。
在“属性”窗口中,将“嵌入互操作类型”属性从“True”更改为“False”。
对
Microsoft.Office.Interop.Word
重复此程序中的步骤 1 和 2。在 C# 中,在
Autofit
方法的末尾注释掉对DisplayInExcel
的两次调用。按 F5 以验证项目是否仍正确运行。
重复上一个程序的步骤 1-3 以打开程序集窗口。 注意,
Microsoft.Office.Interop.Word
和Microsoft.Office.Interop.Excel
不再位于嵌入程序集列表中。双击“清单”图标并滚动引用程序集的列表。
Microsoft.Office.Interop.Word
和Microsoft.Office.Interop.Excel
均位于列表中。 由于应用程序引用 Excel 和 Word PIA 并且“嵌入互操作类型”属性设置为“False”,因此最终用户的计算机上必须存在两个程序集。在 Visual Studio 中,单击“生成”菜单上的“清理解决方案”以清理完成的项目。