Генерация электронной таблицы из таблиц с использованием OpenXML

Цель: создать электронную таблицу с рабочими таблицами, названными по именам таблиц и столбцов, перечисленных в первой строке каждой таблицы. Они будут основаны на классах, созданных из этих таблиц.

Проблема: после открытия таблицы она утверждает, что обнаружила "нечитаемый контент", и спрашивает, хочу ли я восстановить ее. Если я выбираю "да", он отлично восстанавливает электронную таблицу, но пользователь не должен этого делать.

Наблюдение: с помощью SDK я могу видеть внутреннюю работу электронной таблицы, и я заметил, что XML-имена для рабочих таблиц являются реальной проблемой. В исходной загруженной версии они называются от Sheet1 до Sheet9, но затем они переходят в Sheet1a через Sheet1f. Так что в основном это нумерация их шестнадцатеричными числами 1-ф. После того, как я сделаю ремонт, они исправляют имена до чисто числовых.

Не уверен, что вы можете увидеть изображение, похоже, моя работа блокирует сайт:/SDKView

Примечание. Я посмотрел свой код и нигде не вижу, чтобы я мог конкретно назвать эти внутренние листы 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;
    }

3 ответа

Решение

Так что ответ на этот вопрос... НЕ ИСПОЛЬЗУЙТЕ OPENXML!!!!

Я нашел библиотеку, которая использует OpenXML, но превращает все это в объекты. Я переписал довольно большую реализацию менее чем за день. Мне удалось создать электронную таблицу с нуля с 20 строками кода. Поддержка на модуле отличная, владелец быстро реагирует.

Если вы работаете с OpenXML, мусорят его и получают ClosedXML, у них есть отличная документация, и это потрясающая реализация!

http://closedxml.codeplex.com/

Вы не пожалеете об этом:)

Чтобы уточнить также, как только я использовал ClosedXML, я был в состоянии генерировать рабочую книгу без каких-либо ошибок изначально. Это также сделало чрезвычайно простым изменение цвета фона ячейки, я смог изменить ячейку DataType действительно легко. Вы честно не можете пойти не так, как надо!:)

ClosedXML - хорошая библиотека, если вы строите сравнительно небольшую электронную таблицу. Тем не менее, производительность падает после нескольких сотен строк / столбцов / вкладок. Пройдя мимо этого, вы захотите остаться с OpenXML и, в частности, использовать методы класса WriteStartElement(), WriteElement() и WriteEndElement() класса OpenXMLWriter. Это известно как SAX (простой API для XML) подход.

Прочитайте эту статью MSDN для получения дополнительной информации: SAX против DOM

Можете ли вы попробовать без явной установки идентификаторов отношений? Конкретно эта часть:

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

Просто делать:

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

Это просто означает, что SDK автоматически назначит вам идентификатор отношения. Если вам действительно нужен идентификатор, используйте его, чтобы получить его:

workbookPart1.GetIdOfPart(worksheetPart);
Другие вопросы по тегам