可利用 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 中的代码,您必须向您的项目添加几个引用。虽然示例项目已包含这些引用,但您需要在您的代码中显式引用以下程序集:
您还应将下面的 using/Imports 语句添加到代码文件的顶部。
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
检查过程
XLInsertHeaderFooter 过程接受四个参数,它们分别指示:
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 之间的区别。此示例演示了这两种方法。如果您在编写代码之前调查对象,则将为您节省时间。 |