添加自动筛选器和排序会导致Excel崩溃

Mat*_*and 15 c# excel openxml

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

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

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

<x:autoFilter ref="A4:L33" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main" />
Run Code Online (Sandbox Code Playgroud)

而且它加入之间的工作sheetDatamergeCells.

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

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

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

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

编辑:再玩这个.如果您在Excel中重新保存文件,它仍然会被破坏.但是,如果您首先应用过滤器(然后清除它)然后在Excel中重新保存,您将获得一个工作文件.仔细观察两个文件(仍然破坏的重新存档文件和现在正在运行的文件),我注意到在应用过滤器(并清除)后,这个额外的位被添加到工作簿中:

  <x:definedNames>
    <x:definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'Sheet 1'!$A$1:$E$11</x:definedName>
  </x:definedNames>
Run Code Online (Sandbox Code Playgroud)

不确定这可能是不是......

Mat*_*and 5

好的,所以这里的神奇公式似乎是添加DefinedNames我在编辑中建议的部分:

<x:definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'Sheet 1'!$A$1:$E$11</x:definedName>
Run Code Online (Sandbox Code Playgroud)

显然,_xlmn._FilterDatabase自动过滤器需要工作(至少用于排序)。我想如果你过滤时它不在那里,它就会被创建,但如果你排序时它不在那里,它就会炸毁 Excel。

所以你需要工作表名和单元格引用来填充它。

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

过滤器和高级过滤器

_xlnm .Criteria:这个定义的名称指的是一个范围,其中包含在将高级过滤器应用于一系列数据时要使用的标准值。

_xlnm ._FilterDatabase:可以是以下之一

一种。这个定义的名称是指应用了高级过滤器的范围。这表示未过滤的源数据范围。

湾 此定义的名称是指已应用自动筛选的范围。

因此,您似乎需要_xlnm._FilterDatabase为每个具有过滤器的工作表添加一个(似乎无法在一张工作表上拥有多个过滤器)。_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${1}:${2}${3}",
    sheet.Name,
    leftColumnLetter,
    topRowIndex,
    rightColumnLetter,
    bottomRowIndex))
{
    Name = "_xlnm._FilterDatabase",
    LocalSheetId = sheet.SheetId - 1,
    Hidden = true
});
Run Code Online (Sandbox Code Playgroud)

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