添加自动过滤器和排序会导致 Excel 崩溃

Adding autofilter and sorting causes Excel to crash

我正在开发一个应用程序,您可以在其中使用 OpenXML 将一些数据导出到 Excel 文件。除自动过滤器外,一切正常。这个想法是向数据的主体添加一个自动过滤器,以便用户自动拥有对数据进行过滤和排序的控件。所以在代码中,我做了这样的事情:

var filter = new AutoFilter() { Reference = string.Format("{0}:{1}", topLeftCellReference, bottomRightCellReference ) };
worksheet.AppendChild(filter);

在导出的 XLSX 中,它看起来像这样:

<x:autoFilter ref="A4:L33" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" />

并在 sheetDatamergeCells 之间添加到工作表中。

然后我可以在 Excel 中打开这个过滤器,它工作正常。如果您尝试对列进行排序,预计该列会排序,然后 Excel 崩溃。保存并重新加载文件(强制 Excel 清理所有内容)并不能解决问题。但是,如果您先应用过滤器(比如将列过滤为 > 10,然后删除该过滤器,您现在可以排序而不会崩溃。我在应用过滤器并删除它后保存了一个文件,现在该文件很好,但是查看 "repaired" 文件的 XML,我没有看到任何明显的区别。

有谁知道可能导致问题的原因是什么?除了将其添加到工作表之外,在应用自动筛选器时我还应该做些什么吗?

注意:我们使用的是 Excel 2010(版本 14.0.7153.5000)

这是一个示例 file(单击下载,它将作为 .zip 下载。重命名为 .xlsx 以在 Excel 中打开。启用编辑,select 其中一列并尝试排序)。

编辑:再尝试一下。如果您在 Excel 中重新保存文件,它仍然损坏。但是,如果您首先应用过滤器(然后清除它)然后重新保存到 Excel,您将得到一个工作文件。仔细观察这两个文件(仍然损坏的重新保存文件和现在工作的文件),我确实注意到在应用(并清除)过滤器后工作簿中添加了这个额外的位:

  <x:definedNames>
    <x:definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'Sheet 1'!$A:$E</x:definedName>
  </x:definedNames>

不确定这是否是某种东西...

好的,看来这里的神奇公式是按照我在编辑中的建议添加 DefinedNames 部分:

<x:definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'Sheet 1'!$A:$E</x:definedName>

显然 _xlmn._FilterDatabase 是自动过滤器工作所必需的(至少排序是这样)。我想如果你过滤时它不存在,它就会被创建,但如果你排序时它不存在,它就会爆炸 Excel.

因此您需要sheet名称和单元格引用来填写它。

查看 Open XML 标准,在 definedName 的第 18.2.5 节中,我看到了:

Filter & Advanced Filter

_xlnm .Criteria: this defined name refers to a range containing the criteria values to be used in applying an advanced filter to a range of data.

_xlnm ._FilterDatabase: can be one of the following

a. this defined name refers to a range to which an advanced filter has been applied. This represents the source data range, unfiltered.

b. This defined name refers to a range to which an AutoFilter has been applied.

因此,您似乎需要为每个具有过滤器的 sheet 添加一个 _xlnm._FilterDatabase(似乎无法在单个 [=36= 上设置多个过滤器) ]).无论您使用过滤器有多少个 sheet,名称都是相同的 _xlmn_FilterDatabase,因为我猜只有名称和 localSheetId 的组合需要是唯一的。

所以最后,我有这样的事情:

var filter = new AutoFilter() { Reference = string.Format("{0}:{1}", topLeftCellReference, bottomRightCellReference ) };
worksheet.AppendChild(filter);

workbookPart.Wookbook.DefinedNames.AppendChild(new DefinedName(string.Format("'{0}'!$A:",
    sheet.Name,
    leftColumnLetter,
    topRowIndex,
    rightColumnLetter,
    bottomRowIndex))
{
    Name = "_xlnm._FilterDatabase",
    LocalSheetId = sheet.SheetId - 1,
    Hidden = true
});

这似乎是在解决 Excel 中的错误。 Excel 应该在排序之前检查名称是否已定义,并在需要时自动创建它(如果您过滤而不是排序,它似乎会这样做)。