演练:用 C# 进行 Office 编程

C# 提供了改进 Microsoft Office 编程的功能。 有用的 C# 功能包括命名参数和可选参数以及类型为 dynamic 的返回值。 在 COM 编程中,可以省略 ref 关键字并获得索引属性的访问权限。

两种语言都支持嵌入类型信息,从而允许在不向用户的计算机部署主互操作程序集 (PIA) 的情况下部署与 COM 组件交互的程序集。 有关详细信息,请参见演练:嵌入托管程序集中的类型

本演练演示 Office 编程上下文中的这些功能,但其中许多功能在常规编程中也极为有用。 本演练将使用 Excel 外接应用程序创建 Excel 工作簿。 然后,将创建包含工作簿链接的 Word 文档。 最后,将介绍如何启用和禁用 PIA 依赖项。

重要

VSTO (Visual Studio Tools for Office) 依赖于 .NET Framework。 COM 加载项也可以使用 .NET Framework 编写。 不能使用 .NET Core 和 .NET 5+(最新版本的 .NET)创建 Office 加载项。 这是因为 .NET Core/.NET 5+ 无法在同一进程中与 .NET Framework 协同工作,并可能导致加载项加载失败。 可以继续使用 .NET Framework 编写适用于 Office 的 VSTO 和 COM 加载项。 Microsoft 不会更新 VSTO 或 COM 加载项平台以使用 .NET Core 或 .NET 5+。 可以利用 .NET Core 和 .NET 5+(包括 ASP.NET Core)创建 Office Web 加载项的服务器端。

先决条件

若要完成本演练,计算机上必须安装 Microsoft Office Excel 和 Microsoft Office Word。

注意

以下说明中的某些 Visual Studio 用户界面元素在计算机上出现的名称或位置可能会不同。 这些元素取决于你所使用的 Visual Studio 版本和你所使用的设置。 有关详细信息,请参阅个性化设置 IDE

设置 Excel 加载项应用程序

  1. 启动 Visual Studio。
  2. “文件” 菜单上,指向 “新建”,然后选择 “项目”
  3. 在“安装的模板”窗格中,展开“C#”,再展开“Office”,然后选择 Office 产品的版本年份。
  4. 在“模板”窗格中,选择“Excel <version> 加载项”。
  5. 查看“模板”窗格的顶部,确保“.NET Framework 4”或更高版本出现在“目标框架”框中。
  6. 如果需要,在“名称”框中键入项目的名称。
  7. 选择“确定”。
  8. 新项目将出现在“解决方案资源管理器”中。

添加引用

  1. 在“解决方案资源管理器”中,右键单击你的项目名称,然后选择“添加引用”。 此时会显示“添加引用”对话框。
  2. 在“程序集”选项卡上,在“组件名称”列表中选择“Microsoft.Office.Interop.Excel”版本 <version>.0.0.0(有关 Office 产品版本号的键,请参阅 Microsoft 版本),然后按住 Ctrl 键并选择“Microsoft.Office.Interop.Word”,version <version>.0.0.0。 如果未看到程序集,可能需要安装它们(请参阅如何:安装 Office 主互操作程序集)。
  3. 选择“确定”。

添加必要的 Imports 语句或 using 指令

在“解决方案资源管理器”中,右键单击“ThisAddIn.cs”文件,然后选择“查看代码”。 将下列 using 指令 (C#) 添加到代码文件的顶部(如果它们尚不存在)。

using System.Collections.Generic;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;

创建银行帐户列表

在“解决方案资源管理器”中,右键单击你的项目名称,选择“添加”,然后选择“类”。 将类命名为 Account.cs。 选择 添加 。 将 Account 类的定义替换为以下代码。 类定义使用自动实现的属性。

class Account
{
    public int ID { get; set; }
    public double Balance { get; set; }
}

若要创建包含两个帐户的 bankAccounts 列表,请将以下代码添加到 ThisAddIn.cs 中的 ThisAddIn_Startup 方法。 列表声明使用集合初始值设定项。

var bankAccounts = new List<Account>
{
    new Account
    {
        ID = 345,
        Balance = 541.27
    },
    new Account
    {
        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();
}
  • 方法 Add 有一个可选参数,用于指定特定的模板。 如果希望使用形参的默认值,你可以借助可选形参以忽略该形参的实参。 由于上一个示例没有参数,Add 将使用默认模板并创建新的工作簿。 C# 早期版本中的等效语句要求提供一个占位符参数:excelApp.Workbooks.Add(Type.Missing)。 有关详细信息,请参阅命名参数和可选参数
  • Range 对象的 RangeOffset 属性使用“索引属性”功能。 此功能允许你通过以下典型 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();

你不能创建自己的索引属性。 该功能仅支持使用现有索引属性。

DisplayInExcel 的末尾添加以下代码以将列宽调整为适合内容。

excelApp.Columns[1].AutoFit();
excelApp.Columns[2].AutoFit();

这些新增内容介绍了 C# 中的另一功能:处理从 COM 主机返回的 Object 值(如 Office),就像它们具有 dynamic 类型一样。 当嵌入互操作类型具有其默认值时,COM 对象将被自动视为 dynamic;当你使用 EmbedInteropTypes 编译器选项引用程序集时,该对象被视为 True 或等效值。 有关嵌入互操作类型的详细信息,请参阅本文后面部分的“查找 PIA 引用”和“还原 PIA 依赖项”程序。 有关 dynamic 的详细信息,请参阅 dynamic使用类型 dynamic

调用 DisplayInExcel

ThisAddIn_StartUp 方法的末尾添加以下代码。 对 DisplayInExcel 的调用包含两个参数。 第一个参数是已处理的帐户列表的名称。 第二个参数是定义如何处理数据的多行 Lambda 表达式。 每个帐户的 IDbalance 值都显示在相邻的单元格中,如果余额小于零,则相应的行显示为红色。 有关详细信息,请参阅 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;
    }
});

若要运行程序,请按 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);

此代码演示 C# 中的几个功能:在 COM 编程、命名参数和可选参数中省略 ref 关键字的功能。PasteSpecial 方法有七个参数,所有这些参数都是可选的引用参数。 通过命名实参和可选实参,你可以指定希望按名称访问的形参并仅将实参发送到这些形参。 在本示例中,实参指示创建指向剪贴板上工作簿的链接(形参 Link)并在 Word 文档中将该链接显示为图标(形参 DisplayAsIcon)。 C# 还允许忽略这些参数的 ref 关键字。

运行应用程序

按 F5 运行该应用程序。 Excel 启动并显示包含 bankAccounts 中两个帐户的信息的表。 然后,出现包含指向 Excel 表的 Word 文档。

清理已完成的项目

在 Visual Studio 中,选择“生成”菜单上的“清理解决方案”。 否则,每次在计算机上打开 Excel 时都会运行加载项。

查找 PIA 引用

  1. 再次运行应用程序,但不选择“清理解决方案”。
  2. 选择“开始”。 找到“Microsoft Visual Studio <版本>”,然后打开开发人员命令提示。
  3. 在“Visual Studio 的开发人员命令提示”窗口中键入 ildasm,然后按 Enter。 此时将出现 IL DASM 窗口。
  4. 在 IL DASM 窗口的“文件”菜单上,选择“文件”>“打开”。 双击“Visual Studio <版本>”,然后双击“项目”。 打开项目的文件夹,在 bin/Debug 文件夹中查找项目名称.dll。 双击 项目名称.dll。 新窗口将显示项目的属性以及对其他模块和程序集的引用。 程序集包括命名空间 Microsoft.Office.Interop.ExcelMicrosoft.Office.Interop.Word。 在 Visual Studio 中,编译器默认将所需的类型从引用的 PIA 导入程序集。 有关详细信息,请参阅如何:查看程序集内容
  5. 双击“清单”图标。 此时将出现包含程序集列表的窗口,这些程序集包含项目所引用的项。 Microsoft.Office.Interop.ExcelMicrosoft.Office.Interop.Word 不在列表中。 由于已将项目所需的类型导入程序集,因此无需安装对 PIA 的引用。 将类型导入程序集可简化部署。 用户计算机上不必存在 PIA。 应用程序不需要部署特定版本的 PIA。 应用程序可以使用多个版本的 Office,前提是所有版本中都存在必要的 API。 由于不再需要部署 PIA,你可以提前创建可与多个版本的 Office(包括之前的版本)配合使用的应用程序。 你的代码不能使用你正在使用的 Office 版本中不可用的任何 API。 特定 API 在早期版本中是否可用并不总是很清楚。 不建议使用早期版本的 Office。
  6. 关闭清单窗口和程序集窗口。

还原 PIA 依赖项

  1. 在解决方案资源管理器中选择“显示所有文件”按钮。 展开“引用”文件夹并选择“Microsoft.Office.Interop.Excel”。 按 F4 以显示“属性”窗口。
  2. 在“属性”窗口中,将“嵌入互操作类型”属性从“True”更改为“False”。
  3. Microsoft.Office.Interop.Word 重复此程序中的步骤 1 和 2。
  4. 在 C# 中,在 Autofit 方法的末尾注释掉对 DisplayInExcel 的两次调用。
  5. 按 F5 以验证项目是否仍正确运行。
  6. 重复上一个程序的步骤 1-3 以打开程序集窗口。 注意,Microsoft.Office.Interop.WordMicrosoft.Office.Interop.Excel 不再位于嵌入程序集列表中。
  7. 双击“清单”图标并滚动引用程序集的列表。 Microsoft.Office.Interop.WordMicrosoft.Office.Interop.Excel 均位于列表中。 由于应用程序引用 Excel 和 Word PIA 并且“嵌入互操作类型”属性为“False”,因此最终用户的计算机上必须存在两个程序集。
  8. 在 Visual Studio 中,选择“生成”菜单上的“清理解决方案”以清理完成的项目。

请参阅