使用 Open XML SDK 2.0 将页眉或页脚插入 Excel 2010 工作簿中

Office 可视操作方法

**摘要:**使用 Open XML SDK 2.0 for Microsoft Office 中的强类型类可将新的页眉或页脚插入 或 Microsoft Excel 2010 文档中,而无需将该文档加载到 Excel 中。

上次修改时间: 2015年3月9日

适用范围: Excel 2010 | Office 2007 | Office 2010 | Open XML | PowerPoint 2010 | VBA | Word 2010

**发布时间:**2010 年 7 月

**供稿人:**Ken Getz,MCW Technologies, LLC(该链接可能指向英文页面)

概述

可利用 Open XML 文件格式在 Excel 2007 或 Excel 2010 文档中使用页眉和页脚。Open XML SDK 2.0 添加了旨在简化对 Open XML 文件格式的访问的强类型类。SDK 简化了使用页眉和页脚这一任务,而此直观操作方法附带的代码示例将介绍如何使用 SDK 来实现这个目标。

此直观操作方法附带的示例包含一些代码,可利用这些代码将新的页眉或页脚插入 Excel 2007 或 Excel 2010 文档中。若要使用该示例,请安装 Open XML SDK 2.0(可通过"浏览"一节中列出的链接获得)。该示例还将包含的代码用作 Open XML SDK 2.0 的代码段集的一部分。"浏览"一节还包括指向完整代码段集的链接,尽管您无需下载并安装代码段即可使用示例。

示例应用程序将在您提供的文档中添加页眉或页脚,并在示例中调用 XLInsertHeaderFooter 方法来执行此操作。此方法使您能够添加页眉或页脚,以指示将显示页眉的页面组。对此方法的调用与以下代码示例类似。

Const fileName As String = "C:\temp\test.xlsx"
XLInsertHeaderFooter(fileName, "Sheet1",
  "This is my header", HeaderType.EvenHeader)
XLInsertHeaderFooter(fileName, "Sheet1",
  "This is my footer", HeaderType.AllFooter)
const string fileName = @"C:\temp\test.xlsx"
XLInsertHeaderFooter(fileName, "Sheet1", 
  "This is my header", HeaderType.EvenHeader);
XLInsertHeaderFooter(fileName, "Sheet1", 
  "This is my footer", HeaderType.AllFooter);

此示例代码还包括一个枚举,该枚举定义了页眉或页脚的各种可能的位置 - XLInsertHeaderFooter 过程要求您在向其传递工作簿、工作表名称和页眉文本时提供下列值之一。

Public Enum HeaderType
  AllHeader
  AllFooter
  OddHeader
  OddFooter
  EvenHeader
  EvenFooter
  FirstHeader
  FirstFooter
End Enum
public enum HeaderType : int
{
  AllHeader,
  AllFooter,
  OddHeader,
  OddFooter,
  EvenHeader,
  EvenFooter,
  FirstHeader,
  FirstFooter
}

请注意,该枚举不是由 SDK 提供的。可利用它指明要在其中添加页眉或页脚的过程。AllHeader 和 AllFooter 用例不匹配任何内部 Excel 功能。相反,它们将处理已在其中指定偶数或奇数页眉/页脚的用例,并希望每个页面上都有一个页眉或页脚。这样一来,您便能将 DifferentOddEven 属性设置为 True。但您仍可进行管理以使所有页面上都显示一个页眉或页脚。虽然可以在用户界面中执行此操作,但并未直接采用 Open XML SDK 2.0 和 Excel 文件格式内置任何内容以支持此操作。

在使用代码之前,了解将页眉和页脚存储到 Excel 文档中的方式很重要。Open XML SDK 2.0 在其工具目录中包含一个名为 OpenXmlSdkTool.exe 的有用应用程序,如图 1 所示。此工具使您能够打开一个文档,并查看该文档的各个部分及其层次结构。图 1 显示了在此示例中运行代码后的测试文档,在右侧窗格中,此工具显示了部分的 XML 以及可用来生成部分内容的反映的 C# 代码。

图 1 显示的是 Open XML SDK 2.0 Productivity Tool,可利用此工具查看文档的 Open XML 内容。

图 1. Productivity Tool

运行代码后测试文档

如果您查看图 1 中的 XML 内容,您将发现与此直观操作方法中的代码有关的信息类似于以下内容:

  • Excel 文件格式将页眉和页脚信息存储在 HeaderFooter 类型的部分中。

  • HeaderFooter 部分是 Worksheet 类型的部分(它是代码修改的特定工作表的子级)的子级。

  • HeaderFooter 部分具有一个布尔 DifferentOddEven 属性,该属性指示工作表是否在奇数和偶数页上使用不同的页眉和页脚。(这将说明此示例代码包含 AllFooter 和 AllHeader 选项的原因。如果不包含这两个选项,则在您将 DifferentOddEven 属性设置为 True,且希望所有页面上都有一个页眉或页脚的情况下,您必须指定相同的页眉或页脚两次。)

  • 虽然该示例未使用此功能,但 Excel 支持用于为首页包含不同的页眉或页脚的选项。示例代码包含设置此选项的功能,采用的方法是在创建页眉或页脚时指定 FirstHeader 或 FirstFooter 枚举值。

编码

此直观操作方法附带的示例包含向 Excel 2007 或 Excel 2010 文档添加页眉或页脚所需的代码。以下各节将详细介绍这些代码。

设置引用

若要使用 Open XML SDK 2.0 中的代码,您必须向您的项目添加几个引用。虽然示例项目已包含这些引用,但您需要在您的代码中显式引用以下程序集:

  • WindowsBase - 可以根据您创建的项目的类型为您设置此引用。

  • DocumentFormat.OpenXml - 由 Open XML SDK 2.0 安装。

您还应将下面的 using/Imports 语句添加到代码文件的顶部。

Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

检查过程

XLInsertHeaderFooter 过程接受四个参数,它们分别指示:

  • 要修改的工作簿的名称(字符串)。

  • 要将页眉或页脚添加到的工作表的名称(字符串)。

  • 页眉或页脚的内容(字符串)。

  • 页眉或页脚的类型(HeaderType 枚举中的值之一)。

Public Sub XLInsertHeaderFooter(
  ByVal fileName As String, ByVal sheetName As String, _
  ByVal textToInsert As String, ByVal type As HeaderType)
public static void XLInsertHeaderFooter(
  string fileName, string sheetName, 
  string textToInsert, HeaderType type)

若要调用该过程,请传递所有参数值,如下面的代码示例所示。

Const fileName As String = "C:\temp\test.xlsx"
XLInsertHeaderFooter(fileName, "Sheet1",
  "This is my header", HeaderType.EvenHeader)
XLInsertHeaderFooter(fileName, "Sheet1",
  "This is my footer", HeaderType.AllFooter)
const string fileName = @"C:\temp\test.xlsx"
XLInsertHeaderFooter(fileName, "Sheet1", 
  "This is my header", HeaderType.EvenHeader);
XLInsertHeaderFooter(fileName, "Sheet1", 
  "This is my footer", HeaderType.AllFooter);

检索对工作表部分的引用

代码首先会在读/写模式中打开指定的工作簿,然后检索对工作簿部分的引用。

Using document As SpreadsheetDocument =
  SpreadsheetDocument.Open(fileName, True)
  Dim wbPart As WorkbookPart = document.WorkbookPart
  ' Code removed here.
End Using
using (SpreadsheetDocument document = 
  SpreadsheetDocument.Open(fileName, true))
{
  WorkbookPart wbPart = document.WorkbookPart;
  // Code removed here.
}

然后,代码会在工作簿部分中的工作簿后代(工作表类型)中进行查找,这里的名称与您在调用此过程时指定的名称匹配。由于工作表不存在,因此代码将使用 FirstOrDefault 方法,该方法在搜索失败时将返回空引用(而不是像 First 方法一样引发异常)。如果搜索失败,代码将安静返回而不执行操作。请注意,此代码只会检查 Workbook 对象中的 XML 内容,并查找具有正确名称的 Sheet 元素。它不检索对相应部分的引用。

Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)(). _
  Where(Function(s) s.Name = sheetName).FirstOrDefault()
If theSheet Is Nothing Then
  Return
End If
Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
  Where(s => s.Name == sheetName).FirstOrDefault();
if (theSheet == null)
{
  return;
}

在给定对 Sheet 元素的引用的情况下,代码需要先检索对工作表部分的引用,然后检索工作表本身。代码将使用 Id 属性和工作簿部分 GetPartById 方法来检索对相应的 WorksheetPart 的引用。然后,代码将检索对该部分的 Worksheet 属性的引用以获取该部分中包含的数据。如果代码无法检索工作表,则表示工作簿存在问题,并且代码将再次安静返回。

Dim wsPart As WorksheetPart =
  CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
Dim ws As Worksheet = wsPart.Worksheet

' Worksheet is nothing? You have a damaged workbook!
If ws Is Nothing Then
  Return
End If
WorksheetPart wsPart = 
  (WorksheetPart)(wbPart.GetPartById(theSheet.Id));
Worksheet ws = wsPart.Worksheet;

// Worksheet is nothing? You have a damaged workbook!
if (ws == null)
{
  return;
}

使用页眉或页脚

在给定对工作表的引用的情况下,代码将会查看其 XML 内容以查找第一个 HeaderFooter 元素。在此示例下,如果工作表当前不包含该元素,则代码将创建它。

Dim hf As HeaderFooter = ws.Descendants(Of HeaderFooter).FirstOrDefault()
If hf Is Nothing Then
  hf = New HeaderFooter()
  ws.AppendChild(Of HeaderFooter)(hf)
End If
HeaderFooter hf = ws.Descendants<HeaderFooter>().FirstOrDefault();
if (hf == null)
{
  hf = new HeaderFooter();
  ws.AppendChild<HeaderFooter>(hf);
}

现在,假定该元素存在。但是,代码仍会确认这一点。

If hf IsNot Nothing Then
  ' Code removed here.
End If
if (hf != null)
{
  // Code removed here.
}

代码将处理 HeaderFooter 元素的属性,并相应地设置 DifferentOddEven 或 DifferentFirst 属性。

Select Case type
  Case HeaderType.EvenHeader,
    HeaderType.EvenFooter, HeaderType.OddHeader,
    HeaderType.OddFooter
    ' Even or odd only? Add a differentOddEven attribute 
    ' and set it to "1".
    hf.DifferentOddEven = True

  Case HeaderType.FirstFooter, HeaderType.FirstHeader
    ' First page only? Add a differentFirst attribute and set it to "1".
    hf.DifferentFirst = True
End Select
switch (type)
{
  case HeaderType.EvenHeader:
  case HeaderType.EvenFooter:
  case HeaderType.OddHeader:
  case HeaderType.OddFooter:
    // Even or odd only? Add a differentOddEven attribute and set 
    // it to "1".
    hf.DifferentOddEven = true;
    break;

  case HeaderType.FirstFooter:
  case HeaderType.FirstHeader:
    hf.DifferentFirst = true;
    break;
}

最后,代码将根据您在调用此过程时指定的页眉或页脚的类型,来创建一个表示页眉或页脚的新对象,然后相应地设置其 Text 属性。

Select Case type
  ' This code creates new header elements, even if they
  ' already exist. Either way, you end up with a "fresh" element.
  Case HeaderType.AllHeader
    hf.EvenHeader = New EvenHeader
    hf.EvenHeader.Text = textToInsert

    hf.OddHeader = New OddHeader
    hf.OddHeader.Text = textToInsert

  Case HeaderType.AllFooter
    hf.EvenFooter = New EvenFooter
    hf.EvenFooter.Text = textToInsert

    hf.OddFooter = New OddFooter
    hf.OddFooter.Text = textToInsert

  Case HeaderType.EvenFooter
    hf.EvenFooter = New EvenFooter
    hf.EvenFooter.Text = textToInsert

  Case HeaderType.EvenHeader
    hf.EvenHeader = New EvenHeader
    hf.EvenHeader.Text = textToInsert

  Case HeaderType.OddFooter
    hf.OddFooter = New OddFooter
    hf.OddFooter.Text = textToInsert

  Case HeaderType.OddHeader
    hf.OddHeader = New OddHeader
    hf.OddHeader.Text = textToInsert

  Case HeaderType.FirstHeader
    hf.FirstHeader = New FirstHeader
    hf.FirstHeader.Text = textToInsert

  Case HeaderType.FirstFooter
    hf.FirstFooter = New FirstFooter
    hf.FirstFooter.Text = textToInsert
End Select
switch (type)
{
  // This code creates new header elements, even if they
  // already exist. Either way, you end up with a "fresh" element.

  case HeaderType.AllHeader:
    hf.EvenHeader = new EvenHeader();
    hf.EvenHeader.Text = textToInsert;

    hf.OddHeader = new OddHeader();
    hf.OddHeader.Text = textToInsert;
    break;

    case HeaderType.AllFooter:
      hf.EvenFooter = new EvenFooter();
      hf.EvenFooter.Text = textToInsert;

    hf.OddFooter = new OddFooter();
    hf.OddFooter.Text = textToInsert;
    break;

  case HeaderType.EvenFooter:
    hf.EvenFooter = new EvenFooter();
    hf.EvenFooter.Text = textToInsert;
    break;

  case HeaderType.EvenHeader:
    hf.EvenHeader = new EvenHeader();
    hf.EvenHeader.Text = textToInsert;
    break;

  case HeaderType.OddFooter:
    hf.OddFooter = new OddFooter();
    hf.OddFooter.Text = textToInsert;
    break;

  case HeaderType.OddHeader:
    hf.OddHeader = new OddHeader();
    hf.OddHeader.Text = textToInsert;
    break;

  case HeaderType.FirstHeader:
    hf.FirstHeader = new FirstHeader();
    hf.FirstHeader.Text = textToInsert;
    break;

  case HeaderType.FirstFooter:
    hf.FirstFooter = new FirstFooter();
    hf.FirstFooter.Text = textToInsert;
    break;
}

在最后一步中,代码将保存工作表部分。

ws.Save()
ws.Save();

示例过程

完整的示例过程包含以下代码。

Public Sub XLInsertHeaderFooter(
  ByVal fileName As String, ByVal sheetName As String, _
  ByVal textToInsert As String, ByVal type As HeaderType)

  Using document As SpreadsheetDocument = SpreadsheetDocument.Open(fileName, True)
    Dim wbPart As WorkbookPart = document.WorkbookPart

    ' Find the sheet with the supplied name, and then use 
    ' that Sheet object to retrieve a reference to the appropriate 
    ' worksheet.
    Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)().
      Where(Function(s) s.Name = sheetName).FirstOrDefault()
    If theSheet Is Nothing Then
      Return
    End If

    Dim wsPart As WorksheetPart = 
      CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
    Dim ws As Worksheet = wsPart.Worksheet

    ' Worksheet is nothing? You have a damaged workbook!
    If ws Is Nothing Then
      Return
    End If

    ' Retrieve a reference to the header/footer node, if it exists.
    Dim hf As HeaderFooter = 
      ws.Descendants(Of HeaderFooter).FirstOrDefault()
    If hf Is Nothing Then
      hf = New HeaderFooter()
      ws.AppendChild(Of HeaderFooter)(hf)
    End If

    ' The HeaderFooter node should be there, at this point!
    If hf IsNot Nothing Then
      ' You've found the node. Now add the header or footer.
      ' Deal with the attributes first:
      Select Case type
        Case HeaderType.EvenHeader, HeaderType.EvenFooter, 
          HeaderType.OddHeader, HeaderType.OddFooter
          ' Even or odd only? Add a differentOddEven 
          ' attribute and set it to "1".
          hf.DifferentOddEven = True

       Case HeaderType.FirstFooter, HeaderType.FirstHeader
          ' First page only? Add a differentFirst attribute 
          ' and set it to "1".
          hf.DifferentFirst = True
      End Select

      Select Case type
        ' This code creates new header elements, even if they
        ' already exist. Either way, you end up with a "fresh" element.
        Case HeaderType.AllHeader
          hf.EvenHeader = New EvenHeader
          hf.EvenHeader.Text = textToInsert

          hf.OddHeader = New OddHeader
          hf.OddHeader.Text = textToInsert

        Case HeaderType.AllFooter
          hf.EvenFooter = New EvenFooter
          hf.EvenFooter.Text = textToInsert

          hf.OddFooter = New OddFooter
          hf.OddFooter.Text = textToInsert

        Case HeaderType.EvenFooter
          hf.EvenFooter = New EvenFooter
          hf.EvenFooter.Text = textToInsert

        Case HeaderType.EvenHeader
          hf.EvenHeader = New EvenHeader
          hf.EvenHeader.Text = textToInsert

        Case HeaderType.OddFooter
          hf.OddFooter = New OddFooter
          hf.OddFooter.Text = textToInsert

        Case HeaderType.OddHeader
          hf.OddHeader = New OddHeader
          hf.OddHeader.Text = textToInsert

        Case HeaderType.FirstHeader
          hf.FirstHeader = New FirstHeader
          hf.FirstHeader.Text = textToInsert

        Case HeaderType.FirstFooter
          hf.FirstFooter = New FirstFooter
          hf.FirstFooter.Text = textToInsert
      End Select
    End If

    ws.Save()
  End Using
End Sub
public static void XLInsertHeaderFooter(
  string fileName, string sheetName, 
  string textToInsert, HeaderType type)
{
  using (SpreadsheetDocument document = 
    SpreadsheetDocument.Open(fileName, true))
  {
    WorkbookPart wbPart = document.WorkbookPart;

    // Find the sheet with the supplied name, and then use 
    // that Sheet object to retrieve a reference to 
    // the appropriate worksheet.
    Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
      Where(s => s.Name == sheetName).FirstOrDefault();
    if (theSheet == null)
    {
      return;
    }
    WorksheetPart wsPart = 
      (WorksheetPart)(wbPart.GetPartById(theSheet.Id));
    Worksheet ws = wsPart.Worksheet;

    // Worksheet is nothing? You have a damaged workbook!
    if (ws == null)
    {
      return;
    }

    // Retrieve a reference to the header/footer node, if it exists.
    HeaderFooter hf = ws.Descendants<HeaderFooter>().FirstOrDefault();
    if (hf == null)
    {
      hf = new HeaderFooter();
      ws.AppendChild<HeaderFooter>(hf);
    }

    // The HeaderFooter node should be there, at this point!
    if (hf != null)
    {
      // You've found the node. Now add the header or footer.
      // Deal with the attributes first:
      switch (type)
      {
        case HeaderType.EvenHeader:
        case HeaderType.EvenFooter:
        case HeaderType.OddHeader:
        case HeaderType.OddFooter:
          // Even or odd only? Add a differentOddEven attribute and set 
          // it to "1".
          hf.DifferentOddEven = true;
          break;

        case HeaderType.FirstFooter:
        case HeaderType.FirstHeader:
          hf.DifferentFirst = true;
          break;
      }

      switch (type)
      {
        // This code creates new header elements, even if they
        // already exist. Either way, you end up with a 
        // "fresh" element.
        case HeaderType.AllHeader:
          hf.EvenHeader = new EvenHeader();
          hf.EvenHeader.Text = textToInsert;

          hf.OddHeader = new OddHeader();
          hf.OddHeader.Text = textToInsert;
          break;

        case HeaderType.AllFooter:
          hf.EvenFooter = new EvenFooter();
          hf.EvenFooter.Text = textToInsert;

          hf.OddFooter = new OddFooter();
          hf.OddFooter.Text = textToInsert;
          break;

        case HeaderType.EvenFooter:
          hf.EvenFooter = new EvenFooter();
          hf.EvenFooter.Text = textToInsert;
          break;

        case HeaderType.EvenHeader:
          hf.EvenHeader = new EvenHeader();
          hf.EvenHeader.Text = textToInsert;
          break;

        case HeaderType.OddFooter:
          hf.OddFooter = new OddFooter();
          hf.OddFooter.Text = textToInsert;
          break;

        case HeaderType.OddHeader:
          hf.OddHeader = new OddHeader();
          hf.OddHeader.Text = textToInsert;
          break;

        case HeaderType.FirstHeader:
          hf.FirstHeader = new FirstHeader();
          hf.FirstHeader.Text = textToInsert;
          break;

        case HeaderType.FirstFooter:
          hf.FirstFooter = new FirstFooter();
          hf.FirstFooter.Text = textToInsert;
          break;
      }
    }
    ws.Save();
  }
}
读取

此处呈现的示例包含您在使用 Open XML SDK 2.0 时将遇到的许多问题。虽然每个示例都存在些许差异,但基本概念都是一样的。就绝大部分而言,除非您了解尝试使用的部分的结构,否则即使使用 Open XML SDK 2.0 也无法与该部分进行交互。一个需要花时间来真正理解的问题是,使用 SDK 获取表示文档中的部分的强类型对象与处理这些部分的内容中的 XML 之间的区别。此示例演示了这两种方法。如果您在编写代码之前调查对象,则将为您节省时间。

观看

观看视频

观看视频(该链接可能指向英文页面) | 时长:00:10:06

单击以获取代码

获取代码(该链接可能指向英文页面)

浏览

关于作者
Ken Getz 是 MCW Technologies 的高级顾问。他是 ASP.NET Developers Jumpstart(《ASP.NET 开发人员入门》,Addison-Wesley,2002)、Access Developer's Handbook(《Access 开发人员手册》,Sybex,2001)和 VBA Developer's Handbook, 2nd Edition(《VBA 开发人员手册第 2 版》,Sybex,2001)的合著者。