【问题标题】:Adding autofilter and sorting causes Excel to crash添加自动筛选和排序会导致 Excel 崩溃
【发布时间】:2015-10-12 13:26:23
【问题描述】:

我正在开发一个应用程序,您可以在其中使用 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 清理所有内容)并不能解决问题。但是,如果您先应用过滤器(例如将列过滤到&gt; 10,然后删除该过滤器,您现在可以排序而不会崩溃。我在应用过滤器并将其删除后保存了一个文件,现在该文件很好,但看起来在“修复”文件的 XML 中,我没有看到任何明显的区别。

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

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

这是一个示例 file(单击下载,它将以 .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>

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

【问题讨论】:

  • @mason:OpenXML 让您始终可以制作无效的 Excel 文件。因为有效 Excel 文件的规则比有效 OpenXML 文件的规则更严格(并且完全没有记录)(例如,标准中没有任何内容规定worksheet 下的子级顺序,但 Excel 对某些子级的位置非常挑剔应该出现)。通常,当您第一次打开文件时,Excel 会抱怨(带有非常无用的错误消息)。但是这个文件可以正常打开,并且只有在您尝试对列进行排序时才会弹出问题。
  • 您的元素顺序看起来是正确的(有关订单文档,请参阅我的autoFilter answer here)。生产力工具报告的唯一问题是Font 上的Color 元素之一不正确 - 我有another answer 有关于Colors 的信息,但我怀疑它会帮助解决这个问题。我去看看能不能搞清楚……
  • @petelids:感谢您查看并感谢您在实际指定订单位置的提示。我没有意识到那是存在的,它可以为我省去很多悲伤。
  • @petelids:我解决了颜色问题(再次感谢您的提醒),但正如您所怀疑的那样,它没有任何区别。
  • 如果把worksheet.AppendChild(filter);换成worksheet.Append(filter);,有什么变化吗?

标签: c# excel openxml


【解决方案1】:

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

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

显然_xlmn._FilterDatabase 需要自动过滤器才能工作(至少对于排序)。我猜如果你过滤时它不存在,它会被创建,但如果你排序时它不存在,它会炸毁 Excel。

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

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

过滤器和高级过滤器

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

_xlnm ._FilterDatabase:可以是以下之一

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

b.此定义的名称是指自动过滤器已被应用到的范围 已申请。

因此,您似乎需要为每个具有过滤器的工作表添加一个_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
});

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

【讨论】:

  • 非常感谢!在尝试增强 ExcelBuilder 库时,我在 javascript 中生成 Excel 时遇到了同样的问题。成功了!
猜你喜欢
  • 2016-01-25
  • 1970-01-01
  • 2015-04-17
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
  • 2016-12-23
相关资源
最近更新 更多