为什么在此示例中附加AutoFilter会损坏我的excel文件?

问题描述 投票:6回答:2

嗨,我使用以下方法来应用自动筛选器:

public static void ApplyAutofilter(string fileName, string sheetName, string reference)
        {
            using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, true))
            {
                IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().Where(s => s.Name == sheetName);
                var arrSheets = sheets as Sheet[] ?? sheets.ToArray();

                string relationshipId = arrSheets.First().Id.Value;
                var worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(relationshipId);

                var autoFilter = new AutoFilter() { Reference = reference };
                worksheetPart.Worksheet.Append(autoFilter);

                worksheetPart.Worksheet.Save();
            }
        }

而且我有这个简单的Excel工作表:

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS9HYThqSC5qcGcifQ==” alt =“在此处输入图像描述”>

问题是当我将此方法称为:

ApplyAutofilter(@".\Test.xlsx", "Foo", "A0:A200");

ApplyAutofilter(@".\Test.xlsx", "Foo", "A1:A200");

未应用过滤器,并且在以下情况下尝试打开它时文件被损坏:

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS9PTnM5cS5qcGcifQ==” alt =“在此处输入图像描述”>

问题:知道如何将此自动文件管理器应用于Excel工作表而不损坏它吗?

c# .net excel openxml openxml-sdk
2个回答
10
投票

这是由于您在XML中编写autoFilter元素而导致的[[where。我打开了一个运行了您的代码的文件,并在Open XML Productivity Tool中将其打开。这表明错误是

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS81cFBsMC5wbmcifQ==” alt =“错误消息:意外的子元素”>

here看ECMA-376标准,Worksheet的XML看起来像这样:

<xsd:complexType name="CT_Worksheet"> <xsd:sequence> <xsd:element name="sheetPr" type="CT_SheetPr" minOccurs="0" maxOccurs="1"/> <xsd:element name="dimension" type="CT_SheetDimension" minOccurs="0" maxOccurs="1"/> <xsd:element name="sheetViews" type="CT_SheetViews" minOccurs="0" maxOccurs="1"/> <xsd:element name="sheetFormatPr" type="CT_SheetFormatPr" minOccurs="0" maxOccurs="1"/> <xsd:element name="cols" type="CT_Cols" minOccurs="0" maxOccurs="unbounded"/> <xsd:element name="sheetData" type="CT_SheetData" minOccurs="1" maxOccurs="1"/> <xsd:element name="sheetCalcPr" type="CT_SheetCalcPr" minOccurs="0" maxOccurs="1"/> <xsd:element name="sheetProtection" type="CT_SheetProtection" minOccurs="0" maxOccurs="1"/> <xsd:element name="protectedRanges" type="CT_ProtectedRanges" minOccurs="0" maxOccurs="1"/> <xsd:element name="scenarios" type="CT_Scenarios" minOccurs="0" maxOccurs="1"/> <xsd:element name="autoFilter" type="CT_AutoFilter" minOccurs="0" maxOccurs="1"/> <xsd:element name="sortState" type="CT_SortState" minOccurs="0" maxOccurs="1"/> <xsd:element name="dataConsolidate" type="CT_DataConsolidate" minOccurs="0" maxOccurs="1"/> <xsd:element name="customSheetViews" type="CT_CustomSheetViews" minOccurs="0" maxOccurs="1"/> <xsd:element name="mergeCells" type="CT_MergeCells" minOccurs="0" maxOccurs="1"/> <xsd:element name="phoneticPr" type="CT_PhoneticPr" minOccurs="0" maxOccurs="1"/> <xsd:element name="conditionalFormatting" type="CT_ConditionalFormatting" minOccurs="0" maxOccurs="unbounded"/> <xsd:element name="dataValidations" type="CT_DataValidations" minOccurs="0" maxOccurs="1"/> <xsd:element name="hyperlinks" type="CT_Hyperlinks" minOccurs="0" maxOccurs="1"/> <xsd:element name="printOptions" type="CT_PrintOptions" minOccurs="0" maxOccurs="1"/> <xsd:element name="pageMargins" type="CT_PageMargins" minOccurs="0" maxOccurs="1"/> <xsd:element name="pageSetup" type="CT_PageSetup" minOccurs="0" maxOccurs="1"/> <xsd:element name="headerFooter" type="CT_HeaderFooter" minOccurs="0" maxOccurs="1"/> <xsd:element name="rowBreaks" type="CT_PageBreak" minOccurs="0" maxOccurs="1"/> <xsd:element name="colBreaks" type="CT_PageBreak" minOccurs="0" maxOccurs="1"/> <xsd:element name="customProperties" type="CT_CustomProperties" minOccurs="0" maxOccurs="1"/> <xsd:element name="cellWatches" type="CT_CellWatches" minOccurs="0" maxOccurs="1"/> <xsd:element name="ignoredErrors" type="CT_IgnoredErrors" minOccurs="0" maxOccurs="1"/> <xsd:element name="smartTags" type="CT_SmartTags" minOccurs="0" maxOccurs="1"/> <xsd:element name="drawing" type="CT_Drawing" minOccurs="0" maxOccurs="1"/> <xsd:element name="drawingHF" type="CT_DrawingHF" minOccurs="0" maxOccurs="1"/> <xsd:element name="picture" type="CT_SheetBackgroundPicture" minOccurs="0" maxOccurs="1"/> <xsd:element name="oleObjects" type="CT_OleObjects" minOccurs="0" maxOccurs="1"/> <xsd:element name="controls" type="CT_Controls" minOccurs="0" maxOccurs="1"/> <xsd:element name="webPublishItems" type="CT_WebPublishItems" minOccurs="0" maxOccurs="1"/> <xsd:element name="tableParts" type="CT_TableParts" minOccurs="0" maxOccurs="1"/> <xsd:element name="extLst" type="CT_ExtensionList" minOccurs="0" maxOccurs="1"/> </xsd:sequence> </xsd:complexType>

注意,它是sequence,因此项目的顺序很重要。在我的示例文件中,在autoFilter元素之后添加了pageMargins元素,这与上述架构相反。

更改代码以将autoFilter元素写入正确的位置会修复您的代码。我不确定这是否是最有效的方法,但这应该可行。本质上,它向后推动workbook的子元素,直到找到autoFilter之后的第一个元素。一旦发现它使用InsertAfter方法将autoFilter插入正确的位置:

public static void ApplyAutofilter(string fileName, string sheetName, string reference) { using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileName, true)) { IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().Where(s => s.Name == sheetName); var arrSheets = sheets as Sheet[] ?? sheets.ToArray(); string relationshipId = arrSheets.First().Id.Value; var worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(relationshipId); var autoFilter = new AutoFilter() { Reference = reference }; OpenXmlElement preceedingElement = GetPreceedingElement(worksheetPart); worksheetPart.Worksheet.InsertAfter(autoFilter, preceedingElement); worksheetPart.Worksheet.Save(); } } public static OpenXmlElement GetPreceedingElement(WorksheetPart worksheetPart) { List<Type> elements = new List<Type>() { typeof(Scenarios), typeof(ProtectedRanges), typeof(SheetProtection), typeof(SheetCalculationProperties), typeof(SheetData) }; OpenXmlElement preceedingElement = null; foreach (var item in worksheetPart.Worksheet.ChildElements.Reverse()) { if (elements.Contains(item.GetType())) { preceedingElement = item; break; } } return preceedingElement; }


0
投票
不错!这可行!它帮助我解决了我的问题。使用以下内容有所不同

OpenXmlElement preceedingElement = GetPreceedingElement(worksheetPart); worksheetPart.Worksheet.InsertAfter(autoFilter, preceedingElement);

© www.soinside.com 2019 - 2024. All rights reserved.