使用 Open XML SDK 2.0 检索 Excel 2010 工作簿中隐藏的行或列的列表

Office 可视操作方法

**摘要:**使用 Open XML SDK 2.0 中的强类型类可检索 Microsoft Excel 2007 或 Excel 2010 工作表中隐藏的行或列的列表,而无需将该文档加载到 Excel 中。

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

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

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

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

概述

利用 Open XML 文件格式,可以检索与 Excel 工作表中隐藏的行和列有关的信息。Open XML SDK 2.0 添加了旨在简化对 Open XML 文件格式的访问的强类型类。SDK 简化了检索有关工作簿的信息并查找适当的 XML 内容这一任务。此直观操作方法附带的代码示例将介绍如何使用 SDK 做到这一点。

编码

此直观操作方法附带的代码示例包含检索 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
Imports System.Collections.Generic
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Collections.Generic;

检查过程

XLGetHiddenRowsOrCols 过程接受三个参数:

  • 要检查的文档的名称(字符串)

  • 要检查的工作表的名称(字符串)

  • 是检测行 (true) 还是列 (false)(布尔值)

Public Function XLGetHiddenRowsOrCols(
  ByVal fileName As String, ByVal sheetName As String,
  ByVal detectRows As Boolean) As List(Of UInteger)
public static List<uint> XLGetHiddenRowsOrCols(
  string fileName, string sheetName, bool detectRows)

如果指定的工作表包含任何隐藏的行或列,则此过程将返回一个无符号整数的列表,这些整数包含隐藏的行或列的每个索引。若要调用该过程,请传递所有参数值,如下面的代码示例所示。

> [!NOTE] >

从 1(而非 0)开始对行和列进行编号。

Const fileName As String = "C:\temp\HiddenRowsCols.xlsx"
Dim items As List(Of UInteger) =
  XLGetHiddenRowsOrCols(fileName, "Sheet1", False)

Dim sw As New StringWriter
For Each item In items
  sw.WriteLine(item)
Next
Console.WriteLine(sw.ToString())
const string fileName = @"C:\temp\HiddenRowsCols.xlsx";
List<uint> items = XLGetHiddenRowsOrCols(fileName, "Sheet1", true);
var sw = new StringWriter();
foreach (var item in items)
  sw.WriteLine(item);
Console.WriteLine(sw.ToString());

访问工作表

下面的代码示例演示代码首先创建将包含返回值的变量。

Dim itemList As New List(Of UInteger)
List<uint> itemList = new List<uint>();

接下来,代码将使用 SpreadsheetDocument.Open 方法打开文档,并指示应打开该文档以供只读访问(最后的 false 参数)。然后,代码将使用文档的 WorkbookPart 属性检索对工作簿部分的引用,如以下代码示例中所示。

Using document As SpreadsheetDocument =
  SpreadsheetDocument.Open(fileName, False)

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

若要查找隐藏的行或列,则代码必须先检索对指定工作表的引用(假定已提供其名称)。代码必须仔细查看工作簿部分 Workbook 属性的所有工作表类型后代,并检查它找到的每个工作表的 Name 属性。

> [!NOTE] >

此搜索只会检查工作簿的关系,而不会实际查找工作表部分。它仅查找对工作表的引用,其中包含工作表的名称和 ID 等信息。为此,请使用 LINQ 查询。

Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)(). 
  Where(Function(s) s.Name = sheetName).FirstOrDefault()

If theSheet Is Nothing Then
  Throw New ArgumentException("sheetName")
End If
Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
  Where(s => s.Name == sheetName).FirstOrDefault();
if (theSheet == null)
{
  throw new ArgumentException("sheetName");
}
> [!NOTE] >

FirstOrDefault 方法将返回第一个匹配引用(在此示例中,为一个工作表)或空引用(如果未找到匹配项)。代码将验证空引用,如果您传递的工作表名称无效,则将引发异常。

现在,您已具有有关工作表的信息,代码必须检索对相应的工作表部分的引用。您检索到的工作表信息提供了一个 Id 属性,在提供此 Id 属性的情况下,代码可通过调用 WorkbookPart GetPartById 属性来检索对相应的 WorksheetPart 的引用。

Else
  ' The sheet does exist.
  Dim wsPart As WorksheetPart =
    CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
  Dim ws As Worksheet = wsPart.Worksheet
  ' Code removed here…
End If
else
{
  // The sheet does exist.
  WorksheetPart wsPart = 
    (WorksheetPart)(wbPart.GetPartById(theSheet.Id));
  Worksheet ws = wsPart.Worksheet;
  // Code removed here…
}

检索隐藏的行或列的索引值的列表

如以下代码示例中所示,代码将使用您在调用此过程时指定的 detectRows 参数来确定是否检索有关行或列的信息。

If detectRows Then
  ' Retrieve hidden rows.
  ' Code removed here…
Else
  ' Retrieve hidden columns.
  ' Code removed here…
End If
if (detectRows)
{
  // Retrieve hidden rows.
  // Code removed here…
}
else
{
  // Retrieve hidden columns.
  // Code removed here…
}

下面的代码示例演示了检索隐藏的行的列表的代码只需一个代码行。

itemList = ws.Descendants(Of Row).
  Where(Function(r) r.Hidden IsNot Nothing AndAlso
          r.Hidden.Value).
  Select(Function(r) r.RowIndex.Value).ToList()
itemList = ws.Descendants<Row>().
  Where((r) => r.Hidden != null && r.Hidden.Value).
  Select(r => r.RowIndex.Value).ToList<uint>();

此一行代码就完成了大部分任务。它首先调用工作表的 Descendants 方法,并检索所有行的列表。Where 方法将结果限制为以下行:其中,项目的 Hidden 属性不为 null,且 Hidden 属性的值为 True。Select 方法预测每个行的返回值,并返回 RowIndex 属性的值。最后,ToList 方法将生成的 IEnumerable 转换为无符号整数的列表。如果没有隐藏的行,则返回的列表将为空。

检索隐藏列的列表会更复杂一些,这是因为 Excel 会将隐藏列的组折叠成一个元素,并会提供 Min 和 Max 属性来描述组中的第一列和最后一列。因此,虽然检索隐藏列的列表的代码首先执行的操作与检索隐藏行的代码首先执行的操作相同,但它必须循环访问索引值(循环访问隐藏列集合中的每个项,并添加介于 Min 和 Max 值(包含这两个值)之间的每个索引)。

Dim cols = ws.Descendants(Of Column).
  Where(Function(c) c.Hidden IsNot Nothing AndAlso
          c.Hidden.Value)
For Each item As Column In cols
  For i As UInteger = item.Min.Value To item.Max.Value
    itemList.Add(i)
  Next
Next
var cols = ws.Descendants<Column>().
  Where((c) => c.Hidden != null && c.Hidden.Value);
foreach (Column item in cols)
{
  for (uint i = item.Min.Value; i <= item.Max.Value; i++)
  {
    itemList.Add(i);
  }
}

示例过程

下面的代码示例是完整的示例过程。

Public Function XLGetHiddenRowsOrCols(
  ByVal fileName As String, ByVal sheetName As String,
  ByVal detectRows As Boolean) As List(Of UInteger)

  ' Given a workbook and a worksheet name, return either 
  ' a list of hidden row numbers, or a list of hidden 
  ' column numbers. If detectRows is true, return
  ' hidden rows. If detectRows is false, return hidden columns. 
  ' Rows and columns are numbered starting with 1.

  Dim itemList As New List(Of UInteger)

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

    Dim wbPart As WorkbookPart = document.WorkbookPart

    Dim theSheet As Sheet = wbPart.Workbook.Descendants(Of Sheet)().
      Where(Function(s) s.Name = sheetName).FirstOrDefault()
    If theSheet Is Nothing Then
      Throw New ArgumentException("sheetName")
    Else
      ' The sheet does exist.
      Dim wsPart As WorksheetPart =
        CType(wbPart.GetPartById(theSheet.Id), WorksheetPart)
      Dim ws As Worksheet = wsPart.Worksheet

      If detectRows Then
        ' Retrieve hidden rows.
        itemList = ws.Descendants(Of Row).
          Where(Function(r) r.Hidden IsNot Nothing AndAlso 
                  r.Hidden.Value).
          Select(Function(r) r.RowIndex.Value).ToList()
      Else
        ' Retrieve hidden columns.
        Dim cols = ws.Descendants(Of Column).
          Where(Function(c) c.Hidden IsNot Nothing AndAlso 
                  c.Hidden.Value)
        For Each item As Column In cols
          For i As UInteger = item.Min.Value To item.Max.Value
            itemList.Add(i)
          Next
        Next
      End If
    End If
  End Using
  Return itemList
End Function
public static List<uint> XLGetHiddenRowsOrCols(
  string fileName, string sheetName, bool detectRows)
{
  // Given a workbook and a worksheet name, return 
  // either a list of hidden row numbers, or a list 
  // of hidden column numbers. If detectRows is true, return
  // hidden rows. If detectRows is false, return hidden columns. 
  // Rows and columns are numbered starting with 1.

  List<uint> itemList = new List<uint>();

  using (SpreadsheetDocument document = 
    SpreadsheetDocument.Open(fileName, false))
  {
    WorkbookPart wbPart = document.WorkbookPart;

    Sheet theSheet = wbPart.Workbook.Descendants<Sheet>().
      Where((s) => s.Name == sheetName).FirstOrDefault();
    if (theSheet == null)
    {
      throw new ArgumentException("sheetName");
    }
    else
    {
      // The sheet does exist.
      WorksheetPart wsPart = 
        (WorksheetPart)(wbPart.GetPartById(theSheet.Id));
      Worksheet ws = wsPart.Worksheet;

      if (detectRows)
      {
        // Retrieve hidden rows.
        itemList = ws.Descendants<Row>().
          Where((r) => r.Hidden != null && r.Hidden.Value).
          Select(r => r.RowIndex.Value).ToList<uint>();
      }
      else
      {
        // Retrieve hidden columns.
        var cols = ws.Descendants<Column>().
          Where((c) => c.Hidden != null && c.Hidden.Value);
        foreach (Column item in cols)
        {
          for (uint i = item.Min.Value; i <= item.Max.Value; i++)
          {
            itemList.Add(i);
          }
        }
      }
    }
  }
  return itemList;
}
读取

此直观操作方法附带的示例代码将检索 Excel 2007 或 Excel 2010 工作簿中指定工作表中的隐藏行或列的列表。若要使用该示例,必须安装 Open XML SDK 2.0(可通过"浏览"一节中列出的链接获得)。该示例还将包含的代码用作 Open XML SDK 2.0 的代码示例集的一部分。"浏览"一节还包括指向完整代码示例集的链接,但您无需下载并安装代码示例即可使用该示例。

示例应用程序将检索您提供的文档中隐藏的行的列表,在此示例中将调用 XLGetHiddenRowsOrCols 方法来执行此操作。此方法将返回无符号整数的泛型列表。调用代码必须解释并循环访问返回的列表。下面的代码示例中演示了该方法调用。

const string fileName = @"C:\temp\HiddenRowsCols.xlsx";
List<uint> items = XLGetHiddenRowsOrCols(fileName, "Sheet1", false);
var sw = new StringWriter();
foreach (var item in items)
  sw.WriteLine(item);
Console.WriteLine(sw.ToString());

了解 Excel 存储有关隐藏的行或列的信息的方式很重要。Open XML SDK 2.0 在其工具目录中包含一个名为 OpenXmlSdkTool.exe 的有用应用程序,如图 1 所示。此工具使您能够打开一个文档,并查看该文档的各个部分及其层次结构。图 1 演示了一个测试文档。在左窗格中,该文档已展开到工作表节点;在右窗格中,此工具显示了该部分的 XML 和可用于生成该部分的内容的反射 C# 代码。

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

图 1. Open XML SDK 2.0 Productivity Tool

Open XML SDK 2.0 生产力工具

如果您检查图 1 中的左窗格(部分的层次结构)和 XML 内容,您将意识到必须获知哪些内容才能了解此直观操作方法中的代码:

  • 若要检索隐藏的行/列的列表,请先找到包含单元格的特定工作表。

  • 在右侧的突出显示的 XML 内容中,您会发现,标记包含了一组列信息,并且一系列隐藏列包含了用于将这些列标记为隐藏的信息。Open XML SDK 2.0 将此属性 (Attribute) 公开为 Column 对象的 Hidden 属性 (Property)。该属性 (Property) 可能为 null(如果从不隐藏列),也可能为 true 或 false。每个 Column 对象均公开了一个 Min 属性和 Max 属性,示例代码可使用这两个属性分别确定列组中的第一列和最后一列。

  • 图 2 显示了单个行的信息,您可根据隐藏属性 (Attribute) 确认该行已隐藏。Open XML SDK 2.0 将此属性 (Attribute) 公开为 Row 对象的 Hidden 属性 (Property)。

图 2 显示了各行包含有关其隐藏状态的信息的方式。

图 2. 各行中的隐藏状态信息。

单个行中的隐藏状态信息
观看

观看视频

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

单击以获取代码

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

浏览

关于作者
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)的合著者。