第 2 章 Office 解决方案介绍
Office 解决方案的三种基本模式
现在, 您了解 Office 对象模型的基本模式, 本章将介绍开发人员如何模拟和构建其 Office 解决方案. 使用 Office 构建的大多数解决方案都遵循三种模式之一
Office 自动化执行
Office 加载项
Office 文档背后的代码
一个自动化可执行文件是一个独立于 Office 控制和自动化 Office 应用程序的程序. 可以使用 Visual Studio 等开发工具创建自动化可执行文件. 一个典型的例子是独立的控制台应用程序或 Windows 窗体应用程序启动 Office 应用程序, 然后自动执行一些任务. 要启动以这种方式构建的解决方案, 解决方案的用户启动自动化可执行文件, 从而启动 Office 应用程序. 与其他两种模式不同, 自动化代码不会在 Office 进程中运行, 而是运行在自己的进程中, 并且会将自动化的 Office 进程交给过程.
加载项是 Office 加载并在需要时创建的程序集 (DLL) 中的类. 一个加载项与 Office 应用程序进程正在运行, 而不是要求自己的进程与 Office 应用程序进程分开. 要启动以这种方式构建的解决方案, 解决方案的用户启动与加载项关联的 Office 应用程序. Office 启动时检测到已注册的加载项, 并加载它们. 加载项可以以与文档背后的代码相同的方式自定义 Office 应用程序. 然而, 当与代码关联的文档是关闭的时候, 文档背后的代码会被卸载, 因此可以在 Office 应用程序的整个生命周期内保持加载.
模式后面的代码被 Visual Basic for Applications(VBA)推广, 该应用程序是一个包含在 Office 中的简单开发环境, 它使开发人员能够针对特定 Office 应用程序的对象模型编写 Visual Basic 代码, 并将该代码与特定文档相关联或模板. 文档可以使用 Visual Studio 2005 Tools for Office(VSTO)后面的 C#或 Visual Basic 代码关联. 要以这种方式构建解决方案, 解决方案的用户将打开一个文档, 该文档在其背后具有代码, 或者从具有代码的模板创建新文档. 文档背后的代码将在文档打开时以某种方式自定义 Office 应用程序. 例如, 文档后面的代码可能会添加仅当文档打开时才会显示的菜单项, 或者将文档与文档打开时发生的事件相关联.
本书稍后将讨论两种高级模式. 服务器文档模式涉及在服务器上运行代码来操作 Office 文档中存储的数据, 而不启动 Office 应用程序. VSTO 通过称为缓存数据的功能使这种情况成为可能. 第 18 章 "服务器数据场景" 讨论了这种模式. xml 和 XSLT 模式类似于服务器文档模式, 并涉及编写代码, 以 WordprocessingML 或 SpreadsheetML 格式生成 Word 或 Excel 文档, 而不启动 Office 应用程序. 您还可以通过将 XSLT 转换应用于某些 xml 数据来生成这些格式. 第 21 章 "在 Excel 中使用 XML" 和第 22 章 "在 Word 中使用 XML" 讨论了这些情况.
托管代码
模式中的加载项和代码有时称为托管代码, 这意味着您的代码与 Office 应用程序的运行方式相同.
发现托管代码
对于在 Office 应用程序进程中运行的代码, Office 应用程序必须能够发现代码, 将代码加载到其进程空间中并运行代码. Office 加载项在 Windows 注册表中注册, 以便 Office 可以找到并启动它们. 使用注册表似乎有点非. NET, 但这是必要的, 因为 Office 2003 会将加载项与通过 COM 互操作的 COM 对象进行通信.
文档模式背后的代码不需要注册表项. 相反, 通过向文档文件添加一些特殊属性, 代码与文档相关联. 当文档打开时, Office 会读取这些属性, 然后 Office 将加载与文档关联的代码.
提供给托管代码的上下文
至关重要的是, 您的托管代码获取上下文需要为要加载的 Office 应用程序获取 Application 对象或 Document 对象. COM 加载项通过由加载项类实现的接口提供上下文. 在 VSTO 中的 Outlook 加载项通过项目中创建的类来提供上下文, 代表正在定制的应用程序. 在 VSTO 中的文档背后的代码通过在项目中创建的类来提供上下文, 表示正在自定义的文档.
托管代码入口点
在启动时, Office 调用一个入口点, 您的代码可以首次运行, 并注册会话中稍后可能发生的事件. 对于 COM 加载项, 此入口点是由 COM 加载项实现的 IDTExtensibility2 接口的 OnConnection 方法. 对于文档背后的 VSTO Outlook 加载项和 VSTO 代码, 此入口点是启动事件处理程序.
启动后代码如何运行
托管代码启动后, 代码继续以一种或多种以下方式运行
代码运行响应 Office 提出的事件
启动后代码运行的最常见方法是响应 Office 应用程序中发生的事件. 例如, 当文档打开或电子表格中的单元格更改时, Office 会引发事件. 列表 1-24 显示了一个简单的类, 它监听 Excel 的 Worksheet 对象引发的 Activate 事件. 通常, 当调用代码的初始入口点时, 您将连接事件侦听器, 如清单 1-24 所示.
接口方法调用对象提供给 Office
诸如 COM 加载项的启动类的对象实现称为 IDTExtensibility2 的接口, 该接口具有 Office 应用程序运行期间 Office 调用的方法. 例如, 如果用户关闭 COM 加载项, Office 将调用由 COM 加载项实现的 IDTExtensibility2 接口上的 OnDisconnection 方法. 这样, 在运行初始入口点之后运行附加代码.
事件发生在类后面的事件
在 VSTO 项目中生成的代表定制应用程序或文档的类处理启动和关闭事件. 在类的构造函数执行后, Office 会引发启动事件. 当文档即将关闭时, Office 会引发关闭事件.
代码如何卸载
您的代码可以通过多种方式卸载, 具体取决于您使用的开发模式. 如果您使用自动化可执行模式, 则当您写入的自动化可执行文件退出时, 代码将被卸载. 如果您正在使用加载项模式, 则当 Office 应用程序退出或用户通过加载项管理对话框关闭加载项时, 代码将卸载. 如果您正在使用模式后面的代码, 与代码关联的文档关闭时, 您的代码将被卸载.
在运行代码的托管模式中, 有一些被称为或事件的方法通知您即将卸载. 对于 COM 加载项, Office 调用 OnDisconnection 方法. 对于文档和 Outlook 加载项后面的 VSTO 代码, Office 会在您卸载代码之前引发关闭事件.
办公自动化可执行文件
本部分将更详细地讨论这三种 Office 解决方案中的每一种. 使用自动化可执行文件的 Office 解决方案通过创建与 Office 应用程序相关联的 Application 对象的新实例, 以非常直接的方式启动 Office 应用程序. 因为自动化可执行程序控制 Office 应用程序, 所以自动化可执行文件在启动时运行代码, 此后任何时候执行控制返回到自动化可执行文件.
当自动化可执行文件使用新建创建 Application 对象时, 自动化可执行文件通过将创建的 Application 对象保存在变量中来控制应用程序的生命周期. Office 应用程序通过确定使用其 Application 对象的引用计数或客户端数量来确定是否可以关闭它.
在清单 2-1 中, 一旦使用 new 来创建 myExcelApp 变量, Excel 会启动它并添加一个它的客户端计数, 它知道它持有对 Excel 的 Application 对象的引用. 当 myExcelApp 变量超出范围(当 Main 退出时),.NET 垃圾收集器释放对象, 并通知 Excel 控制台应用程序不再需要 Excel 的 Application 对象. 这导致 Excel 对 Excel 的 Application 对象引用的客户端计数为零, 并且 Excel 退出, 因为没有客户端再次使用 Excel.
当您通过创建 Application 对象的新实例创建 Office 应用程序时, 应用程序将启动而不显示其窗口, 这被证明是有用的, 因为您可以通过弹出窗口来自动化应用程序而不会分散用户的注意力. 如果需要显示应用程序窗口, 可以将 Application 对象的 Visible 属性设置为 TRue. 如果使主窗口可见, 则用户控制应用程序的生命周期. 在 Excel 中, 应用程序将不会退出, 直到用户退出应用程序, 并且保存 Excel Application 对象的变量被垃圾回收. 当用户退出应用程序时, 即使变量仍保留 Word 应用程序对象的实例, Word 也会以不同的方式退出.
列表 2-1 将 Excel 的状态栏设置为 "Hello World", 并通过调用 Excel 的 Workbooks 集合的 Add 方法在 Excel 中打开一个新的空白工作簿. 第 3 章至第 5 章 "编程 Excel","使用 Excel 事件" 和 "使用 Excel 对象" 分别更详细地显示 Excel 对象模型.
清单 2-1 通过控制台应用程序自动化 Excel
- using System;
- using Excel = Microsoft.Office.Interop.Excel;
- using System.Windows.Forms;
- namespace ConsoleApplication
- {
- class Program
- {
- static bool exit = false;
- static void Main(string[] args)
- {
- Excel.Application myExcelApp = new Excel.Application();
- myExcelApp.Visible = true;
- myExcelApp.StatusBar = "Hello World";
- myExcelApp.Workbooks.Add(System.Type.Missing);
- myExcelApp.SheetBeforeDoubleClick +=
- new Excel.AppEvents_SheetBeforeDoubleClickEventHandler(
- myExcelApp_SheetBeforeDoubleClick);
- while (exit == false)
- System.Windows.Forms.Application.DoEvents();
- }
- static void myExcelApp_SheetBeforeDoubleClick(object sheet,
- Excel.Range target, ref bool cancel)
- {
- exit = true;
- }
- }
- }
清单 2-1 还说明了一个自动化可执行文件如何产生时间回到 Office 应用程序. 对 System.Windows.Forms 程序集的引用必须添加到项目中. 事件处理程序挂起后, System.Windows.Forms.Application.DoEvents()在循环中调用, 以允许 Excel 应用程序正常运行. 如果用户双击单元格, Office 会自动生成自动执行文件中的事件处理程序. 在 Double-Click 事件的处理程序中, 我们将静态变量 exit 设置为 true, 这将导致调用 DoEvents 的循环退出, 并且自动执行文件可以退出.
您可以通过运行清单 2-1 中的自动化可执行文件并退出 Excel, 而无需双击单元格即可查看 Excel 的生命周期管理. Excel 将继续运行在隐藏状态, 等待控制台应用程序释放其对 Excel 的 Application 对象的引用.
清单 2-2 表 2-1 的 WiKi 文本表示
- ||Property or Method||Name||Return Type||
- ||Property||Application||Application||
- ||Property||Autoload||Boolean||
- ||Property||Compiled||Boolean||
- ||Property||Creator||Int32||
- ||Method||Delete||Void||
- ||Property||Index||Int32||
- ||Property||Installed||Boolean||
- ||Property||Name||String||
- ||Property||Parent||Object||
- ||Property||Path||String||
表 2-1 显示 Word 的加载项对象的属性和方法的简单表
我们将使用 Visual Studio 2005 创建一个控制台应用程序. 启动 Visual Studio 后, 从文件菜单中选择新建项目. "新建项目" 对话框显示各种项目类型. 从项目类型列表中选择 Visual C#节点, 并选择 Visual C#节点下的 Windows 节点. 这样做有点违反直觉, 因为也有一个 Office 节点可用, 但 Office 节点只显示文档项目和 VSTO Outlook 加载项目后的 VSTO 代码.
选择 Windows 节点后, 您将在右侧的窗口中看到可用的模板. 选择控制台应用程序模板. 命名您的控制台应用程序项目, 然后单击确定按钮创建您的项目. 在图 2-1 中, 我们创建了一个名为 WordWiki 的控制台应用程序. 请注意, 新项目对话框的外观可能与图 2-1 所示的外观不同, 具体取决于您使用的配置文件. 在本书中, 我们假设您正在使用 Visual C#开发设置配置文件. 您可以通过从 "工具" 菜单中选择 "导入和导出设置" 来更改配置文件.
图 2-1 从 "新建项目" 对话框创建控制台应用程序.
当您单击确定按钮时, Visual Studio 为您创建一个控制台应用程序项目. Visual Studio 在 "解决方案资源管理器" 窗口中显示项目的内容, 如图 2-2 所示.
图 2-2. 控制台应用程序 WordWiki 在 Solution Explorer 中显示.
默认情况下, 新创建的控制台应用程序引用程序集 System,System.Data 和 System.xml. 我们还需要添加对 Word 2003 PIA 的引用. 我们通过右键单击 "引用" 文件夹, 然后从出现的弹出菜单中选择 "添加引用". 这将显示图 2-3 中的 "添加引用" 对话框. 单击 COM 选项卡并选择 Microsoft Word 11.0 对象库以添加对 Word 2003 PIA 的引用, 然后单击确定按钮.
图 2-3 添加对 Microsoft Word 2003 PIA 的引用.
Visual Studio 添加了对 Word 2003 PIA 的引用, 并添加了对 stdole,VBIDE 和 Microsoft.Office.Core PIAs 的其他引用, 如图 2-4 所示. 这些额外的 PIA 是 Word PIA 所依赖的. Stdole 是一个包含 COM 对象模型所需类型的定义的 PIA. VBIDE 是与集成到 Office 中的 VBA 编辑器关联的对象模型的 PIA. Microsoft.Office.Core(Office.dll)是所有 Office 应用程序共享的常用功能的 PIA, 例如工具栏和菜单的对象模型.
图 2-4 当您添加 Word 2003 PIA 时, 依赖的 PIA 引用将自动添加到项目中
现在, 正确的引用已经添加到控制台应用程序中, 让我们开始编写代码. 双击解决方案资源管理器窗口中的 Program.cs, 编辑控制台应用程序的主要源代码文件. 如果您打开了大纲, 您将在 Program.cs 文件的顶部看到文本 "using ...", 其旁边带有 + 号. 单击 + 号以展开放置 using 指令的代码. 使用指令添加以下三个, 以便您可以更轻松地使用 Word PIA 和 Microsoft.Office.Core PIA 中的对象以及 System.IO 命名空间中的类.
- using Office = Microsoft.Office.Core;
- using Word = Microsoft.Office.Interop.Word;
- using System.IO;
我们别名这些命名空间中的一些, 所以我们不需要输出整个命名空间, 例如 Microsoft.Office.Interop.Word, 每次我们要声明一个 Word 对象. 使用别名, 我们可以键入 Word 来指定命名空间. 我们为 Word 和 Office 保留别名命名空间, 而不是仅使用 Microsoft.Office.Interop.Word 键入并将所有类型导入全局范围. 这是因为 Word 和 Office 定义了数百种类型, 我们不希望所有这些类型名称可能与我们在代码中或与其他引用类型定义的类型相冲突. 也是为了本书的目的, 当代码说明 Word.Application 而不是应用程序时, 代码更清楚, 所以你知道应用程序类型来自哪个命名空间.
我们现在可以编写一些自动化 Word, 以阅读 wiki 表格式的文本输入文件来创建表格. 清单 2-3 显示了我们程序的完整列表. 而不是解释该列表中的每一行代码, 我们专注于自动化 Word 的代码行. 我们假设读者有一些了解如何在. NET 中读取文本文件, 并通过拆分方法解析字符串. 我们在这里简要介绍了 Word 对象模型中的某些对象, 但第 6 至 8 章 "编程 Word","使用 Word 事件" 和 "使用 Word 对象" 分别更详细地介绍了 Word 对象模型.
清单 2-3 中的第一件事是通过将这行代码添加到程序类的 Main 方法来声明 Word 应用程序对象的新实例.
Word.Application theApplication = new Word.Application();
虽然 Word.Application 是一个接口, 但我们可以创建一个这个接口的新实例, 因为编译器知道 Word.Application 接口与它知道如何启动的 COM 对象相关联. 当 Word 响应自动化可执行文件创建其 Application 对象的新实例时, 它将启动而不显示任何窗口. 当您想通过打开 Word 窗口来使用户自动化而不会混淆用户时, 您可以在此隐形状态下自动化 Word. 对于这个例子, 我们想让 Word 显示它的主窗口, 我们通过添加这一行代码:
theApplication.Visible = true;
接下来, 我们要创建一个新的空 Word 文档, 我们将生成我们的表. 我们通过调用 Word 应用程序对象返回的文档集合中的添加方法来执行此操作. Add 方法需要我们要省略的四个可选参数. 通过引用传递包含特殊值 Type.Missing 的变量来指定 Word 方法中的可选参数. 我们声明一个名为 missing 的变量, 我们设置为 Type.Missing, 并通过引用传递给我们要忽略的每个参数, 如下所示:
- object missing = Type.Missing;
- Word.Document theDocument = theApplication.Documents.Add(
- ref missing, ref missing, ref missing, ref missing);
创建文档时, 我们要读取传递给我们的控制台应用程序的命令行参数指定的输入文本文件. 我们要解析该文本文件来计算列和行的数量. 当我们知道列和行的数量时, 我们使用以下代码行从 Document 对象获取 Range 对象. 通过将我们的缺失变量传递给可选参数, Range 方法将返回一个包含文档整个文本的范围.
Word.Range range = theDocument.Range(ref missing, ref missing);
然后, 我们使用 Range 对象通过调用由 Range 对象返回的 Tables 集合的 Add 方法来添加表. 我们再次传递 Range 对象作为 Add 方法的第一个参数, 以指定我们要用表替换文档的全部内容. 我们还指定了我们想要的行数和列数:
Word.Table table = range.Tables.Add(range, rowCount,columnCount, ref missing, ref missing);
Table 对象有一个 Cell 方法, 它接受一行和一列, 并返回一个 Cell 对象. Cell 对象具有一个 Range 属性, 它返回所讨论的单元格的 Range 对象, 我们可以使用它来设置单元格的文本和格式. 此处显示了设置表格单元格的代码. 请注意, 与大多数 Office 对象模型一样, 索引为 1, 这意味着它们以 1 为起始值, 而不是以 0 为基, 从 0 开始为最小值:
- for (columnIndex = 1; columnIndex <= columnCount; columnIndex++)
- {
- Word.Cell cell = table.Cell(rowIndex, columnIndex);
- cell.Range.Text = splitRow[columnIndex];
- }
通过将表格设置为大小以适合内容并粗体显示标题行来设置表格格式的代码如下所示. 我们使用由 table.Rows [1]返回的 Row 对象, 该对象也有一个 Range 属性, 它返回该对象的 Range 对象. 另外, 我们遇到代码设置表的第一行为粗体. 人们期望能够编写代码表. Rows [1] .Range.Bold = true, 但是 Word 的对象模型需要一个 int 值 (0 为 false,1 为 true) 而不是一个 bool. Bold 属性不返回 bool, 因为文本的范围可以是大胆的, 全部不是粗体或部分粗体. Word 使用枚举的常量 WdConstants.WdUndefined 来指定部分粗体大小写.
- // Format table
- table.Rows[1].Range.Bold = 1;
- table.AutoFitBehavior(Word.WdAutoFitBehavior.wdAutoFitContent);
最后, 程序结束时的一些代码强制 Word 退出而不保存更改:
- // Quit without saving changes
- object saveChanges = false;
- theApplication.Quit(ref saveChanges, ref missing, ref missing);
如果您不写此代码, 即使在控制台应用程序退出后, Word 仍将保持运行. 当您通过将 Application 对象的 Visible 属性设置为 TRue 来显示 Word 窗口时, Word 会将应用程序的生命周期放在最终用户的手中, 而不是自动化程序. 所以即使自动执行程序可以退出, Word 也会继续运行. 要强制 Word 退出, 您必须在 Word 的 Application 对象上调用 Quit 方法. 如果这个程序没有使 Word 窗口可视化, 例如, 它创建了文档与表, 然后将其保存到一个文件, 而不显示 Word 窗口将不必调用退出, 因为 Word 将退出, 当程序退出和释放所有对 Word 对象的引用.
要运行清单 2-3 中的控制台应用程序, 必须创建一个文本文件, 其中包含代码 2-2 中的文本. 然后将文本文件的文件名作为命令行参数传递给控制台应用程序. 您可以通过右键单击解决方案资源管理器中的 WordWiki 项目并选择属性来设置调试器. 然后单击调试选项卡, 并将命令行参数字段设置为文本文件的名称.
清单 2-3 完整的 WordWiki 实现
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.IO;
- using Office = Microsoft.Office.Core;
- using Word = Microsoft.Office.Interop.Word;
- namespace WordWiki
- {
- class Program
- {
- static void Main(string[] args)
- {
- Word.Application theApplication = new Word.Application();
- theApplication.Visible = true;
- object missing = System.Type.Missing;
- Word.Document theDocument = theApplication.Documents.Add(
- ref missing, ref missing, ref missing, ref missing);
- TextReader reader = new System.IO.StreamReader(args[0]);
- string[] separators = new string[1];
- separators[0] = "||";
- int rowCount = 0;
- int columnCount = 0;
- // Read rows and calculate number of rows and columns
- System.Collections.Generic.List<string> rowList =
- new System.Collections.Generic.List<string>();
- string row = reader.ReadLine();
- while (row != null)
- {
- rowCount++;
- rowList.Add(row);
- // If this is the first row,
- // calculate the number of columns
- if (rowCount == 1)
- {
- string[] splitHeaderRow = row.Split(
- separators, StringSplitOptions.None);
- // Ignore the first and last separator
- columnCount = splitHeaderRow.Length - 2;
- }
- row = reader.ReadLine();
- }
- // Create a table
- Word.Range range = theDocument.Range(ref missing,
- ref missing);
- Word.Table table = range.Tables.Add(range, rowCount,
- columnCount, ref missing, ref missing);
- // Populate table
- int columnIndex = 1;
- int rowIndex = 1;
- foreach (string r in rowList)
- {
- string[] splitRow = r.Split(separators,
- StringSplitOptions.None);
- for (columnIndex = 1; columnIndex <= columnCount;
- columnIndex++)
- {
- Word.Cell cell = table.Cell(rowIndex, columnIndex);
- cell.Range.Text = splitRow[columnIndex];
- }
- rowIndex++;
- }
- // Format table
- table.Rows[1].Range.Bold = 1;
- table.AutoFitBehavior(Word.WdAutoFitBehavior.
- wdAutoFitContent);
- // Wait for input from the command line before exiting
- System.Console.WriteLine("Table complete.");
- System.Console.ReadLine();
- // Quit without saving changes
- object saveChanges = false;
- theApplication.Quit(ref saveChanges, ref missing,
- ref missing);
- }
- }
- }
来源: http://www.bubuko.com/infodetail-3158910.html