演练: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 外接应用程序

  1. 启动 Visual Studio。

  2. 在**“文件”菜单上指向“新建”,再单击“项目”**。

  3. 在**“已安装的模板”窗格中,展开“Visual Basic”“Visual C#”,展开“Office”,然后单击“2010”(如果使用的是 Office 2007,则单击“2007”**)。

  4. 在**“模板”窗格中,单击“Excel 2010 外接程序”(或“Excel 2007 外接程序”**)。

  5. 查看**“模板”窗格的顶部,确保“.NET Framework 4”显示在“目标框架”**框中。

  6. 如果需要,在**“名称”**框中键入项目的名称。

  7. 单击**“确定”**。

  8. 新项目出现在**“解决方案资源管理器”**中。

添加引用

  1. 在**“解决方案资源管理器”中,右击项目的名称,然后单击“添加引用”。 将显示“添加引用”**对话框。

  2. 在**“.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 版)。

  3. 单击**“确定”**。

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

  1. 在**“解决方案资源管理器”中,右击“ThisAddIn.vb”“ThisAddIn.cs”文件,然后单击“查看代码”**。

  2. 将以下 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;
    

创建银行帐户的列表

  1. 在**“解决方案资源管理器”中,右击项目的名称,单击“添加”,然后单击“类”。 如果使用 Visual Basic,请将类命名为 Account.vb,或者,如果使用 C#,则命名为 Account.cs。 单击“添加”**。

  2. 用下面的代码替换 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; }
    }
    
  3. 若要创建包含两个帐户的 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

  1. 在相同文件中,将以下方法添加到 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# 编程指南)

  2. 将以下代码添加到 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 范围 方法。 如果没有 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

  1. 将以下代码添加到 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;
        }
    });
    
  2. 若要运行程序,请按 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 引用

  1. 重新运行应用程序,但是不要单击**“清理解决方案”**。

  2. 在**“开始”菜单上,单击“所有程序”。 然后依次单击“Microsoft Visual Studio 2010”“Visual Studio 工具”“Visual Studio 命令提示(2010)”**。

  3. 在“Visual Studio 命令提示(2010)”窗口中键入 ildasm,然后按 Enter。 此时将出现“IL DASM”窗口。

  4. 在 IL DASM 窗口中的**“文件”菜单上,单击“打开”。 双击“Visual Studio 2010”,然后双击“项目”**。 打开项目的文件夹,并查找 您的项目名称.dll 的 bin/Debug 文件夹。 双击 您的项目名称.dll。 一个新窗口将显示项目的特性,以及对其他模块和程序集的引用。 请注意,程序集中包括了命名空间 Microsoft.Office.Interop.Excel 和 Microsoft.Office.Interop.Word。 在 Visual Studio 2010 中,默认情况下编译器会将所需的类型从引用的 PIA 导入程序集中。

    有关更多信息,请参见如何:查看程序集内容

  5. 双击**“清单”**图标。 此时将出现一个包含程序集列表的窗口,这些程序集包含项目所引用的项。 列表中未包括 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 引用。

  6. 关闭清单窗口和程序集窗口。

还原 PIA 依赖项

  1. 在**“解决方案资源管理器”中,单击“显示所有文件”按钮。 展开“引用”文件夹,并选择“Microsoft.Office.Interop.Excel”。 按 F4 以显示“属性”**窗口。

  2. 在**“属性”窗口中,将“嵌入互操作类型”属性从“True”更改为“False”**。

  3. 为 Microsoft.Office.Interop.Word 重复此过程中的步骤 1 和 2。

  4. 在 C# 中,将 DisplayInExcel 方法末尾对 Autofit 的两个调用注释掉。

  5. 按 F5 验证项目是否仍然能够正常运行。

  6. 重复前面过程中的步骤 1-3 以打开程序集窗口。 请注意,嵌入程序集的列表中不再有 Microsoft.Office.Interop.Word 和 Microsoft.Office.Interop.Excel。

  7. 双击**“清单”图标,并滚动浏览引用的程序集的列表。 Microsoft.Office.Interop.Word 和 Microsoft.Office.Interop.Excel 均在列表中。 由于应用程序引用 Excel 和 Word PIA,并且“嵌入互操作类型”属性设置为“False”**,因此两个程序集都必须存在于最终用户的计算机上。

  8. 在 Visual Studio 中,单击**“生成”菜单上的“清理解决方案”**以清理完成的项目。

请参见

任务

如何:在 COM 互操作编程中使用索引属性(C# 编程指南)

演练:嵌入 Microsoft Office 程序集中的类型信息(C# 和 Visual Basic)

演练:嵌入托管程序集中的类型(C# 和 Visual Basic)

演练:创建您的第一个 Excel 应用程序级外接程序

参考

自动实现的属性(C# 编程指南)

对象和集合初始值设定项(C# 编程指南)

dynamic(C# 参考)

Lambda 表达式(C# 编程指南)

互操作性(C# 编程指南)

概念

自动实现的属性 (Visual Basic)

集合初始值设定项 (Visual Basic)

可选参数 (Visual Basic)

按位置和名称传递参数 (Visual Basic)

命名实参和可选实参(C# 编程指南)

早期绑定和后期绑定 (Visual Basic)

Lambda 表达式 (Visual Basic)

其他资源

使用类型 dynamic(C# 编程指南)

COM 互操作 (Visual Basic)