目标:生成一个电子表格,其中包含以每个工作表第一行中列出的表名和列名命名的工作表。这些实际上将基于从这些表构建的类。
问题:打开电子表格后,它声称发现了“不可读的内容”,并询问我是否要恢复。如果我选择是,它确实可以完美地恢复电子表格,但用户不应该这样做。
观察:使用SDK我能够看到电子表格的内部工作方式,我注意到工作表的xml名称是真正的问题。在最初下载的版本中,它们被命名为Sheet1到Sheet9,但随后它们通过Sheet1f进入Sheet1a。所以基本上用十六进制数1-f对它们进行编号。在我修复它之后,他们将名称更正为纯数字。
不确定您是否可以看到图片,看起来我的工作阻止了网站:/
注意:我查看了我的代码,我没有看到任何地方我可以专门命名这些内部xml表,所以我不确定与你分享的代码,但我发布的是哪个部分代码正在创建这些工作表。
构建电子表格的方法:
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;
}
构建工作表的方法:
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;
}
答案 0 :(得分:2)
所以这个问题的答案是......不要使用OPENXML !!!!
我找到了一个使用OpenXML但将其全部转换为对象的库。我在不到一天的时间内重写了一个相当大的实现。我能够从头开始生成一个包含20行代码的电子表格。对模块的支持非常棒,所有者可以快速响应。
如果你正在使用OpenXML垃圾并获得ClosedXML,他们有很棒的文档,这是一个很棒的实现!
http://closedxml.codeplex.com/
你不会后悔:)
为了澄清一下,一旦我使用了ClosedXML,我就能够在没有任何错误的情况下生成工作簿。它还使得更改单元格背景颜色非常简单,我能够轻松地更改单元格DataType。老实说你不会出错! :)
答案 1 :(得分:0)
您可以在没有明确设置关系ID的情况下尝试吗?特别是这部分:
WorksheetPart worksheetPart = workbookPart1.AddNewPart<WorksheetPart>("rId" + (i).ToString());
只是做:
WorksheetPart worksheetPart = workbookPart1.AddNewPart<WorksheetPart>();
这只是意味着SDK会自动为您分配关系ID。如果你真的需要这个ID,那么用它来获取它:
workbookPart1.GetIdOfPart(worksheetPart);
答案 2 :(得分:0)
如果要构建一个相对较小的电子表格,ClosedXML是一个很好的库。但是,在几百行/列/标签之后性能下降。一旦你超越了它,你就会想要继续使用OpenXML,尤其是使用OpenXMLWriter类WriteStartElement(),WriteElement()和WriteEndElement()方法。这称为SAX(Simple API for XML)方法。
阅读此MSDN文章了解更多背景信息:SAX vs DOM