有什么关于画家的故事:Visual Studio 2010特性支持Office

来源:百度文库 编辑:中财网 时间:2024/04/29 23:14:48
 有道是“樱桃好吃树难栽,Office好用开发难”。做过Office开发的程序员常常会有这样的抱怨:
“这个垃圾C#,连个可选参数都不支持!”
“COM接口太复杂繁琐啦,我都快被折磨死了!”
“告诉客户,他的机器上必须也安装Office 2003!Office 2007不支持!!!!!”

  这是因为Office整个系统本身非常庞杂,Office对象模型难于全部掌握,开发语言对Office开发支持不够等等,这些都使得Office开发就像一头笨重的大象,虽然稳定,但是行动却有些迟缓,开发效率不高。

  不过这些抱怨很快就会成为历史了。在Visual Studio 2010 CTP中,它提供了很多新的特性,来解决上述的这些Office开发中的主要问题,以更好地支持Office开发。当然,这些特性也可以应用在其他场合。这些新的特性包括:

  • 动态编程
  在前面的文章中我们曾经介绍过,C# 4.0中引入了一种新的静态类型dynamic,这使得我们在使用COM进行Office开发的时候,无需再进行复杂而繁琐的对象类型转换。

  • 可选参数
  Office提供的函数往往有很多参数,在大多数情况下,很多参数使用默认值就可以了,无需全部指定。但是在C# 3.0中,因为它不支持可选参数,导致每次调用函数都必须给定全部参数,代码繁琐而臃肿。C# 4.0通过对可选参数的支持,很好地解决了这一问题。

  • No-PIA部署
  以前的Office开发,都要求目标机器(用户机器)上同样安装了Office,有时甚至对Office的版本还有特殊的要求。这给Office程序的部署带来了极大的困难:目标机器千差万别,有的安装了Office,有的没有安装,安装了Office的机器,Office版本又各不相同。在Visual Studio 2010中,因为有了No-PIA部署的支持,这些问题都得到了很好的解决。

  有了上述特性的支持,Visual Studio 2010中Office开发开始变得简洁而自然。为了让大家有更加深刻的体会,下面我们以一个实际的例子,来看看这些新特性是如何更好地支持Office开发的。在这个例子中,我们将创建一个表示银行账号的类,然后用这个类的数据填充一个Excel表格,最后将这个表格嵌入到一个Word文档中。(这里需要说明的是,虽然有了No-PIA部署的支持,目标机器上无需安装Office,但是开发机器上还是需要安装Office的。)

  1. 创建Office开发项目OfficeDev
  启动Visual Studio 2010 CTP,我们创建一个基于Visual C#的控制台应用程序(Console Application),同时确保目标平台是“.NET Framework 4.0”:

图1 创建Office开发项目 要使用托管代码调用 Office 应用程序功能,必须使用互操作程序集。互操作程序集使托管代码可以与 Office 应用程序的基于 COM 的对象模型进行交互。所以在项目创建完成后,为了进行Office开发,我们需要在项目中添加Office互操作程序集的引用。在解决方案浏览器(Solution Explorer)中的项目节点上,我们单击右键,在弹出的上下文菜单中我们选择“添加引用(Add Reference)”。在弹出的对话框中,我们选中.NET标签页中的Microsoft.Office.Interop.Excel, version 12.0和Microsoft.Office.Interop.Word, version 12.0,单击“OK”将它们添加到刚刚创建的项目OfficeDev中。 


图2 添加互操作程序集


  2. 创建和准备银行账号类Account
  在OfficeDev项目中,我们新建一个类Account,用来表示银行账号。我们将这个类实现如下:

public class Account {
    public string Name { get; set; }
    public double Balance { get; set; }
}

  这里需要注意的是,为了简化后面的流程,我们没有将这个类定义在任何名字空间内。

  接下来,为了进行Office开发,我们需要在代码中引入Office开发相关的名字空间。在Program.cs中添加如下代码以引入相应的名字空间:

using Microsoft.Office.Interop;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;

  最后,我们用数据初始化Account类的对象,并将它们添加到容器中,以备后用。在主函数Main()中添加如下代码:

var checkAccounts = new List {
                   new Account {
                                      Name = “陈良乔”,
                                      Balance = 541.27
                               },
                   new Account {
                                      Name = “贾玮”,
                                      Balance = -127.44
                               }
               };  这里我们用账号数据新创建了两个Account对象,并将它们添加到List容器checkAccounts中。3. 在Excel中显示账号数据
 
上一步我们完成了数据的准备,下面我们将把这些数据显示到Excel表格中。我们定义一个DisplayInExcel()函数用于创建Excel表格,然后将容器中的账号数据填充到Excel表格中:public static void DisplayInExcel(IEnumerable accounts,
                               Action DisplayFunc)
    {
        var xl = new Excel.Application();
                        
        xl.Workbooks.Add();
        xl.Visible = true;
        xl.Cells[1, 1].Value2 = "Name";
        xl.Cells[1, 2].Value2 = " Balance";
        xl.Cells[2, 1].Select();
        foreach (var ac in accounts)
        {
            DisplayFunc(ac, xl.ActiveCell);
            xl.ActiveCell.get_Offset(1, 0).Select();
        }

        xl.get_Range("A1:B3").Copy();
    }

  然后,我们在Main()函数的底部,按照如下的方式调用DisplayInExcel()函数,最终完成Excel表格的创建和数据的填充:

DisplayInExcel(checkAccounts, (account, cell) =>
                  {
                      // This multiline lambda will set
                      // custom processing rules.
                      cell.Value2=account.Name;
                      cell.get_Offset(0, 1).Value2 = account.Balance;

                      if (account.Balance < 0)
                      {
                          cell.Interior.Color = 255;
                          cell.get_Offset(0, 1).Interior.Color = 255;
                      }
                  });

  这里我们使用了Lambda表达式,由它来对数据填充的逻辑进行具体的定义,最终完成数据的填充。同时,它还会检查Balance的值,如果为负值,则将表格填充为红色,表示这个账号已经赤字了。最后,为了使得Excel表格更加美观,我们让Excel表格根据内容自动调整表格的宽度。在DisplayInExcel()函数的末尾,我们添加如下的代码:

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

  在这里,有过Office开发经验的朋友可能会感到奇怪,AutoFit()函数可以被Columns的返回结果直接调用而无需进行类型转换吗?的确,在C# 3.0中,要想调用AutoFit()函数,必须对Columns的返回值进行类型转换,上面的代码应该写成:

// C# 3.0 code. Not necessary in C# 4.0!
((Range)xl.Columns[1]).AutoFit();
((Range)xl.Columns[2]).AutoFit();  但是在C# 4.0中,因为有了dynamic类型的支持,繁琐的类型转换将不再需要。在C# 4.0中,从COM接口返回的Object类型的对象,被自动当做dynamic类型来处理。我们前面曾经介绍过,因为动态类型的迟绑定特性(late binding),dynamic类型可以调用任何函数,所以无需再进行类型转换,也不会产生编译错误。在运行的时候,动态语言运行时(DLR)会动态查找对象真正的类型而调用相应的函数。 4. 将Excel表格嵌入Word文档
  Excel表格填充完成后,我们将这个表格嵌入到Word文档中。在主函数Main()的末尾添加如下代码,它将创建一个空的Word文档,然后通过调用函数PasteSpecial(),将Excel表格以链接的形式粘贴到Woed文档中:var word = new Word.Application();
    word.Visible = true;
    word.Documents.Add();
    word.Selection.PasteSpecial(Link: true, DisplayAsIcon: true);

  这里需要说明的是,PasteSpecial()函数实际上有7个参数,这些参数都是可选的。如果是在C# 3.0中,我们必须全部给定这7个参数,不管这些参数是否真正需要。在C# 3.0中,PasteSpecial()函数的调用应该是这个样子:

// C# 3.0 code. Not necessary in C# 4.0!
object iconIndex = System.Reflection.Missing.Value;
object link = true;
object placement = System.Reflection.Missing.Value;
object displayAsIcon = true;
object dataType = System.Reflection.Missing.Value;
object iconFileName = System.Reflection.Missing.Value;
object iconLabel = System.Reflection.Missing.Value;
word.Selection.PasteSpecial(ref iconIndex,
                            ref link,
                            ref placement,
                            ref displayAsIcon,
                            ref dataType,
                            ref iconFileName,
                            ref iconLabel);

  而在C# 4.0中,因为它支持了可选参数,使得原来需要15行代码才能完成的工作,现在只需要一行代码。大象减肥了!

  5. 运行解决方案
  到此为止,我们就完成了所有的开发工作,按下F5,运行整个解决方案,我们应该可以看到如下的运行结果:



图3 运行结果
 

  6. No-PIA部署
  开发完成后,最后的任务就是将应用程序部署到目标机器上。在以前的Office开发中,我们除了需要在目标机器上部署我们的应用程序之外,还需要部署一大堆应用程序运行必需的互操作程序集,这使得Office应用开发起来困难,想用起来也不简单。

  为了让大家理解Office应用程序对Office互操作程序集的依赖,我们可以使用IL DASM工具查看应用程序所引用的程序集。通过在Visual Studio命令行工具(Visual Studio command prompt)中执行ildasm命令,我们可以运行IL DASM工具,然后打开我们刚刚开发的应用程序OfficeDev.exe,可以看到如下的界面:



图4 使用IL DASM查看应用程序


  双击其中的“Manifest”,我们可以在弹出的窗口中看到应用程序对Microsoft.Office.Interop.Excel和Microsoft.Office.Interop.Word的引用:


图5 应用程序所依赖的程序集


  这意味着如果想让我们的Office应用程序在目标机器上运行,目标机器上必需具有相应的程序集。这使得我们开发的Office应用程序,成为一头不折不扣的大象!

  但是在Visual Studio 2010中,我们可以通过将程序用到的程序集嵌入到应用程序中,为大象减减肥,从而实现No-PIA部署。在解决方案浏览器中,我们修改相应程序集的属性,使得“Embed Interop Types”的值为True:



图6 修改程序集的属性


  这样,我们就可以将我们真正使用到的程序集嵌入到应用程序中,从而不会对Excel或者Word的整个互操作程序集产生依赖。当我们再次使用IL DASM查看我们重新编译的OfficeDev.exe时,已经找不到对Microsoft.Office.Interop.Excel和Microsoft.Office.Interop.Word的依赖了:


图7 “大象瘦了!”


  相反的,我们可以在IL DASM中看到,我们真正所使用到的Excel.Application, Excel.Range等类型已经嵌入到应用程序中了:


图8 嵌入到应用程序中的新类型


  现在,我们就可以轻松地把应用程序部署到目标机器上,而不用去考虑目标机器上是否安装了Office,Office的版本是多少等等繁琐的问题。

  “老婆快来看啊,有头大象在跳舞耶!”