演练:Office 编程(C# 和 Visual Basic)
Visual Studio 2010 在 C# 和 Visual Basic 中引入了可改善 Microsoft Office 编程的新功能。 每种语言都新增了其他语言中已存在的功能。
C# 中的新功能包括命名参数和可选参数、类型为 dynamic 的返回值、在 COM 编程中省略 ref 关键字以及访问索引属性的功能。 Visual Basic 中的新功能包括自动实现的属性、lambda 表达式中的语句以及集合初始值设定项。
这两种语言都能够嵌入类型信息,从而允许部署与 COM 组件交互的程序集,而无需将主互操作程序集 (PIA) 部署到用户的计算机。 有关更多信息,请参见演练:嵌入托管程序集中的类型(C# 和 Visual Basic)。
此演练在 Office 编程的上下文中演示了这些新功能,但其中许多功能在一般编程中也非常有用。 在演练中,将首先使用 Excel 外接应用程序创建一个 Excel 工作簿。 然后将创建一个 Word 文档,其中包含指向该工作簿的链接。 最后,您将了解如何能够启用和禁用 PIA 依赖项。
系统必备
计算机上必须安装了 Microsoft Office Excel 2010 或 2007 和 Microsoft Office Word 2010 或 2007 才能完成此演练。
如果使用的操作系统低于 Windows Vista,请务必安装 .NET Framework 2.0。
提示
对于在以下说明中使用的某些 Visual Studio 用户界面元素,您的计算机可能会显示不同的名称或位置。这些元素取决于您所使用的 Visual Studio 版本和您所使用的设置。有关更多信息,请参见 Visual Studio 设置。
安装 Excel 外接应用程序
启动 Visual Studio。
在**“文件”菜单上指向“新建”,再单击“项目”**。
在**“已安装的模板”窗格中,展开“Visual Basic”或“Visual C#”,展开“Office”,然后单击“2010”(如果使用的是 Office 2007,则单击“2007”**)。
在**“模板”窗格中,单击“Excel 2010 外接程序”(或“Excel 2007 外接程序”**)。
查看**“模板”窗格的顶部,确保“.NET Framework 4”显示在“目标框架”**框中。
如果需要,在**“名称”**框中键入项目的名称。
单击**“确定”**。
新项目出现在**“解决方案资源管理器”**中。
添加引用
在**“解决方案资源管理器”中,右击项目的名称,然后单击“添加引用”。 此时将出现“添加引用”**对话框。
在**“.NET”选项卡上的“组件名称”列表中选择“Microsoft.Office.Interop.Excel”14.0.0.0 版(对于 Excel 2007,则为 12.0.0.0 版),然后按住 Ctrl 键并选择“Microsoft.Office.Interop.Word”**14.0.0.0 版(对于 Word 2007,则为 12.0.0.0 版)。
单击**“确定”**。
添加必要的 Imports 语句或 using 指令
在**“解决方案资源管理器”中,右击“ThisAddIn.vb”或“ThisAddIn.cs”文件,然后单击“查看代码”**。
将以下 Imports 语句 (Visual Basic) 或 using 指令 (C#) 添加到代码文件的顶部(如果这些语句尚不存在)。
Imports Microsoft.Office.Interop
using System.Collections.Generic; using Excel = Microsoft.Office.Interop.Excel; using Word = Microsoft.Office.Interop.Word;
创建银行帐户的列表
在**“解决方案资源管理器”中,右击项目的名称,单击“添加”,然后单击“类”。 如果使用 Visual Basic,请将类命名为 Account.vb,或者,如果使用 C#,则命名为 Account.cs。 单击“添加”**。
用下面的代码替换 Account 类的定义。 类定义使用“自动实现的属性”,这是 Visual Studio 2010 中的 Visual Basic 的一项新功能。 有关更多信息,请参见自动实现的属性 (Visual Basic)。
Public Class Account Property ID As Integer = -1 Property Balance As Double End Class
class Account { public int ID { get; set; } public double Balance { get; set; } }
若要创建包含两个帐户的 bankAccounts 列表,请将以下代码添加到 ThisAddIn.vb 或 ThisAddIn.cs 中的 ThisAddIn_Startup 方法。 列表声明使用“集合初始值设定项”,这是 Visual Studio 2010 中的 Visual Basic 的一项新功能。 有关更多信息,请参见集合初始值设定项概述 (Visual Basic)。
Dim bankAccounts As New List(Of Account) From { New Account With { .ID = 345, .Balance = 541.27 }, New Account With { .ID = 123, .Balance = -127.44 } }
var bankAccounts = new List<Account> { new Account { ID = 345, Balance = 541.27 }, new Account { ID = 123, Balance = -127.44 } };
将数据导出到 Excel
在相同文件中,将以下方法添加到 ThisAddIn 类。 该方法设置 Excel 工作簿并将数据导出到其中。
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
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(); }
此方法中使用了两个新的 C# 功能。 这两种功能都是 Visual Basic 中的已有功能。
方法 Add 具有用于指定特定模板的可选参数。 利用可选形参(Visual C# 2010 中的新功能),您将能够在希望使用形参的默认值的情况下省略该形参的实参。 由于在前面的示例中未发送参数,因此 Add 使用默认模板并创建一个新工作簿。 C# 早期版本中的等效语句需要占位符参数:excelApp.Workbooks.Add(Type.Missing)。
有关更多信息,请参见命名实参和可选实参(C# 编程指南)。
Range 对象的 Range 和 Offset 属性使用索引属性功能。 利用此功能,您可以通过使用以下典型的 C# 语法从 COM 类型中使用这些属性。 还可利用索引属性使用 Range 对象的 Value 属性,而不必使用 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 互操作编程中使用索引属性(C# 编程指南)。
将以下代码添加到 DisplayInExcel 的末尾,用于根据内容的需要调整列宽。
' Add the following two lines at the end of the With statement. .Columns(1).AutoFit() .Columns(2).AutoFit()
excelApp.Columns[1].AutoFit(); excelApp.Columns[2].AutoFit();
这些新增内容演示了 C# 2010 中的另一项新功能:将从 COM 宿主(例如 Office)返回的 Object 值按照其具有类型 dynamic 来处理。 如果**“嵌入互操作类型”**设置为其默认值 True,或者,同样地,如果 /link 编译器选项引用了程序集,则此情况会自动发生。 类型 dynamic 允许后期绑定(Visual Basic 中的已有功能),并可避免 Visual C# 2008 及该语言的早期版本中所需的显式强制转换。
例如,excelApp.Columns[1] 返回 Object,而 AutoFit 是 Excel Range 方法。 如果没有 dynamic,您必须在调用方法 AutoFit 之前将 excelApp.Columns[1] 返回的对象强制转换为 Range 的实例。
// 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(C# 参考)或使用类型 dynamic(C# 编程指南)。
调用 DisplayInExcel
将以下代码添加到 ThisAddIn_StartUp 方法的末尾。 对 DisplayInExcel 的调用包含两个参数。 第一个参数是要处理的帐户列表的名称。 第二个参数是一个多行 lambda 表达式,该表达式定义要如何处理数据。 每个帐户的 ID 和 balance 值显示在相邻的单元格中,如果余额小于零,则该行显示为红色。 多行 lambda 表达式是 Visual Basic 2010 中的一项新功能。 有关更多信息,请参见 Lambda 表达式 (Visual Basic)。
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)
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; } });
若要运行程序,请按 F5。 将出现一个 Excel 工作表,其中包含帐户中的数据。
添加 Word 文档
将以下代码添加到 ThisAddIn_StartUp 方法的末尾,以创建一个 Word 文档,其中包含指向 Excel 工作簿的链接。
Dim wordApp As New Word.Application wordApp.Visible = True wordApp.Documents.Add() wordApp.Selection.PasteSpecial(Link:=True, DisplayAsIcon:=True)
var wordApp = new Word.Application(); wordApp.Visible = true; wordApp.Documents.Add(); wordApp.Selection.PasteSpecial(Link: true, DisplayAsIcon: true);
此代码演示 C# 中的若干新功能:在 COM 编程中省略 ref 关键字的功能、命名参数以及可选参数。 这些功能是 Visual Basic 中的已有功能。 PasteSpecial 方法有七个参数,所有这些参数均定义为可选的引用参数。 在 Visual C# 2010 之前,您必须为这七个形参定义要用作实参的对象变量,即使您没有要传递的有意义值也是如此。 利用命名实参和可选实参,您可以指定要按名称访问的形参,并仅向这些形参发送实参。 在此示例中,将发送参数以指示应创建指向剪贴板上的工作簿的链接(参数 Link),并且该链接将在 Word 文档中显示为图标(参数 DisplayAsIcon)。 利用 Visual C# 2010,您还可以为这些参数省略 ref 关键字。 将 Visual C# 2008 中的以下代码段与 Visual C# 2010 中所需的单行进行比较:
// Call to PasteSpecial in Visual C# 2008. object iconIndex = Type.Missing; object link = true; object placement = Type.Missing; object displayAsIcon = true; object dataType = Type.Missing; object iconFileName = Type.Missing; object iconLabel = Type.Missing; wordApp.Selection.PasteSpecial(ref iconIndex, ref link, ref placement, ref displayAsIcon, ref dataType, ref iconFileName, ref iconLabel); // Call to PasteSpecial in Visual C# 2010. wordApp.Selection.PasteSpecial(Link: true, DisplayAsIcon: true);
运行应用程序
- 按 F5 运行应用程序。 Excel 将启动并显示一个表格,其中包含 bankAccounts 中的两个帐户的信息。 然后,将出现一个 Word 文档,其中包含指向该 Excel 表格的链接。
清理完成的项目
- 在 Visual Studio 中,单击**“生成”菜单上的“清理解决方案”**。 否则,每次在计算机上打开 Excel 时,外接程序都将运行。
查找 PIA 引用
重新运行应用程序,但是不要单击**“清理解决方案”**。
在**“开始”菜单上,单击“所有程序”。 然后依次单击“Microsoft Visual Studio 2010”、“Visual Studio 工具”、“Visual Studio 命令提示(2010)”**。
在“Visual Studio 命令提示(2010)”窗口中键入 ildasm,然后按 Enter。 此时将出现“IL DASM”窗口。
在 IL DASM 窗口中的**“文件”菜单上,单击“打开”。 双击“Visual Studio 2010”,然后双击“项目”**。 打开项目的文件夹,并查找 您的项目名称.dll 的 bin/Debug 文件夹。 双击 您的项目名称.dll。 一个新窗口将显示项目的特性,以及对其他模块和程序集的引用。 请注意,程序集中包括了命名空间 Microsoft.Office.Interop.Excel 和 Microsoft.Office.Interop.Word。 在 Visual Studio 2010 中,默认情况下编译器会将所需的类型从引用的 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# 中,将 DisplayInExcel 方法末尾对 Autofit 的两个调用注释掉。
按 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 中,单击**“生成”菜单上的“清理解决方案”**以清理完成的项目。
请参见
任务
演练:嵌入 Microsoft Office 程序集中的类型信息(C# 和 Visual Basic)
演练:嵌入托管程序集中的类型(C# 和 Visual Basic)