使用OpenXML从表格生成电子表格

目标:生成一个电子表格,其中的工作表命名为每个工作表第一行中列出的表名和列名。 这些实际上将基于从这些表构build的类。

问题:打开电子表格后,它声称发现“无法读取的内容”,并询问是否要恢复。 如果select“是”,则完全恢复电子表格,但用户不应该这样做。

观察:使用SDK我能够看到电子表格的内部工作,我注意到工作表的XML名称是真正的问题。 在最初的下载版本中,他们被命名为Sheet1到Sheet9,但是他们通过Sheet1f进入Sheet1a。 所以基本上用hex数1-f来编号。 在我修复它之后,他们将这些名字改为纯数字。

不知道你是否可以看到图像,看起来像我的工作阻止网站:/ SDKView

注:我看了我的代码,我没有看到任何地方我可以专门命名这些内部的XML表,所以我不知道什么代码分享给你,但我张贴代码部分是创build这些表。

构build电子表格的方法:

public MemoryStream CreatePartsFromAssembly(MemoryStream spreadsheetStrem, Type typeInAssembly) { using (SpreadsheetDocument document = SpreadsheetDocument.Create(spreadsheetStrem, SpreadsheetDocumentType.Workbook)) { //List<Type> types = null; var types = ExcelTypeHelper.GetTableTypes(typeInAssembly); ExtendedFilePropertiesPart extendedFilePropertiesPart1 = document.AddNewPart<ExtendedFilePropertiesPart>("rId" + (types.Count).ToString()); GenerateExtendedFilePropertiesPart1Content(extendedFilePropertiesPart1, types); WorkbookPart workbookPart1 = document.AddWorkbookPart(); GenerateWorkbookPart1Content(workbookPart1, types); var sharedStringOffsets = new List<int>(); foreach (var type in types) { sharedStringOffsets.Add(ExcelTypeHelper.GetPropertyCount(type)); } //int j = 0; int sharedStringOffset = 0; int sharedStringSum = sharedStringOffsets.Sum(); int sharedStringRunningSum = 0; for (int i = types.Count; i > 0; i--) { var type = types[i - 1]; sharedStringRunningSum += sharedStringOffsets[i - 1]; sharedStringOffset = sharedStringSum - sharedStringRunningSum; WorksheetPart worksheetPart = workbookPart1.AddNewPart<WorksheetPart>("rId" + (i).ToString()); GenerateWorksheetPartContent(worksheetPart, types[i - 1], sharedStringOffset); //j++; } SharedStringTablePart sharedStringTablePart1 = workbookPart1.AddNewPart<SharedStringTablePart>("rId" + (types.Count + 3).ToString()); GenerateSharedStringTablePartContent(sharedStringTablePart1, types); WorkbookStylesPart workbookStylesPart1 = workbookPart1.AddNewPart<WorkbookStylesPart>("rId" + (types.Count + 2).ToString()); GenerateWorkbookStylesPart1Content(workbookStylesPart1); ThemePart themePart1 = workbookPart1.AddNewPart<ThemePart>("rId" + (types.Count + 1).ToString()); GenerateThemePart1Content(themePart1); SetPackageProperties1(document); } return spreadsheetStrem; } 

构build工作表的方法:

  private void GenerateWorksheetPartContent(WorksheetPart worksheetPart, Type type, int sharedStringOffset) { var propertyNames = ExcelTypeHelper.GetPropertyNames(type); string referenceRange = "A1:" + ColumnHeaderFromRowColumn((uint)1, (uint)propertyNames.Count); Worksheet worksheet = new Worksheet(); worksheet.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); SheetDimension sheetDimension = new SheetDimension() { Reference = referenceRange }; SheetViews sheetViews = new SheetViews(); SheetView sheetView = new SheetView() { WorkbookViewId = (UInt32Value)0U }; Selection selection = new Selection() { ActiveCell = "A2", SequenceOfReferences = new ListValue<StringValue>() { InnerText = "A2" } }; sheetView.Append(selection); sheetViews.Append(sheetView); SheetFormatProperties sheetFormatProperties2 = new SheetFormatProperties() { DefaultRowHeight = 15D }; SheetData sheetData = new SheetData(); Row row = new Row() { RowIndex = (UInt32Value)1U, Spans = new ListValue<StringValue>() { InnerText = "1:" + propertyNames.Count } }; for (int i = 0; i < propertyNames.Count; i++) { Cell cell = new Cell() { CellReference = ColumnHeaderFromRowColumn((uint)1, (uint)i + 1), DataType = CellValues.SharedString }; CellValue cellValue = new CellValue(); cellValue.Text = (i + sharedStringOffset).ToString(); //Shared string reference cell.Append(cellValue); row.Append(cell); } sheetData.Append(row); PageMargins pageMargins = new PageMargins() { Left = 0.7D, Right = 0.7D, Top = 0.75D, Bottom = 0.75D, Header = 0.3D, Footer = 0.3D }; worksheet.Append(sheetDimension); worksheet.Append(sheetViews); worksheet.Append(sheetFormatProperties2); worksheet.Append(sheetData); worksheet.Append(pageMargins); worksheetPart.Worksheet = worksheet; } 

所以这个问题的答案是..不要使用OPENXML!

我find了一个使用OpenXML的库,但把它全部变成了对象。 我在不到一天的时间里就重写了一个相当大的实现。 我能够用20行代码从头开始生成电子表格。 对模块的支持是非常好的,拥有者快速回应。

如果您正在使用OpenXML垃圾桶,并获得ClosedXML,他们有很好的文档,这是一个很棒的实现!

http://closedxml.codeplex.com/

你不会后悔的:)

为了澄清一下,一旦我使用了ClosedXML,我能够在最初没有任何错误的情况下生成工作簿。 它也使得改变单元格背景颜色变得非常简单,我可以很容易地改变单元格的数据types。 你真的不能出错! 🙂

你可以尝试没有明确设置关系ID? 具体这个部分:

 WorksheetPart worksheetPart = workbookPart1.AddNewPart<WorksheetPart>("rId" + (i).ToString()); 

做就是了:

 WorksheetPart worksheetPart = workbookPart1.AddNewPart<WorksheetPart>(); 

这只是意味着SDK自动为您分配一个关系ID。 如果你真的需要这个ID,那就用这个来得到它:

 workbookPart1.GetIdOfPart(worksheetPart); 

如果您正在构build一个相对较小的电子表格,ClosedXML是一个很好的库。 但是,性能会在几百行/列/标签之后下降。 一旦你过去了,你会想要继续使用OpenXML,特别是使用OpenXMLWriter类WriteStartElement(),WriteElement()和WriteEndElement()方法。 这就是所谓的SAX(Simple API for XML)方法。

阅读这个MSDN文章更多的背景: SAX与DOM