0

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

问题:打开电子表格后,它声称发现“不可读的内容”并询问我是否要恢复。如果我选择是,它确实可以完美地恢复电子表格,但用户不必这样做。

观察:使用 SDK,我能够看到电子表格的内部工作原理,并且我注意到工作表的 xml 名称是真正的问题。在最初下载的版本中,它们被命名为 Sheet1 到 Sheet9,但随后它们进入到 Sheet1a 到 Sheet1f。所以基本上它用十六进制1-f对它们进行编号。在我对其进行修复后,他们将名称更正为纯数字。

不确定您是否可以看到图片,看起来我的工作阻止了该网站:/ SDK查看

注意:我查看了我的代码,但没有看到任何可以具体命名这些内部 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;
    }
4

3 回答 3

2

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

我找到了一个使用 OpenXML 的库,但将其全部转换为对象。我在不到一天的时间内重写了一个相当大的实现。我能够用 20 行代码从头开始生成电子表格。对模块的支持很棒,业主反应迅速。

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

http://closedxml.codeplex.com/

你不会后悔的:)

还要澄清一下,一旦我使用 ClosedXML,我一开始就能够生成工作簿而没有任何错误。它还使更改单元格背景颜色变得非常简单,我能够非常轻松地更改单元格数据类型。你真的不会出错!:)

于 2012-10-10T14:35:50.580 回答
0

您可以尝试不显式设置关系 ID 吗?具体这部分:

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

做就是了:

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

这只是意味着 SDK 会自动为您分配关系 ID。如果您确实需要该 ID,请使用此 ID 获取它:

workbookPart1.GetIdOfPart(worksheetPart);
于 2012-10-10T12:04:12.613 回答
0

如果您正在构建一个相对较小的电子表格,ClosedXML 是一个很好的库。但是,数百行/列/选项卡后性能下降。一旦你超越了这些,你就会想要继续使用 OpenXML,尤其是使用 OpenXMLWriter 类的 WriteStartElement()、WriteElement() 和 WriteEndElement() 方法。这称为 SAX(XML 的简单 API)方法。

阅读这篇 MSDN 文章了解更多背景信息:SAX 与 DOM

于 2016-02-05T14:19:29.073 回答