简体   繁体   中英

OpenXML Create “Table/Pivot Table” Dynamically

I'm creating a sample program that generates an excel on memory. The program already generates the excel, however i can't put the table working...

Program:

public class Program
{
    static void Main(string[] args)
    {
        CreateCurrentAccount();
    }

    static void CreateCurrentAccount()
    {
        byte[] _buffer = ExcelGenerator.CurrentAccount.GetExcel();
        File.WriteAllBytes("CurrentAccount.xlsx", _buffer);
    }
}

ExcelGenerator Class:

public class ExcelGenerator
{
    static uint? _generalStyle = 0;
    static uint? _dateStyle = 1;
    static uint? _currencyStyle = 2;
    static uint? _percentageStyle = 3;
    static int maxStyleSheetName = 31;

    public static class CurrentAccount
    {
        static uint? _headerStyle = 4;
        static uint? _tableHeaderStyle = 5;
        static uint? _totalHeaderStyle = 6;

        public static byte[] GetExcel()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                SpreadsheetDocument spreadsheet;
                Worksheet worksheet;

                spreadsheet = OpenXML.CreateWorkbook(ms);

                // styles
                OpenXML.AddBasicStyles(spreadsheet);
                AddAdvancedStyles(spreadsheet);

                string styleSheetName = ("Conta Corrente" /* + customer.Name*/);
                OpenXML.AddWorksheet(spreadsheet, styleSheetName.Length > maxStyleSheetName ? styleSheetName.Substring(0, maxStyleSheetName) : styleSheetName);
                worksheet = spreadsheet.WorkbookPart.WorksheetParts.First().Worksheet;

                // Sheet 1 Header
                OpenXML.MergeTwoCells(worksheet, "A1", "F1");
                OpenXML.SetCellValue(spreadsheet, worksheet, 1, 1, CellValues.String, "Conta Corrente", _headerStyle, true);

                // Header Table
                OpenXML.SetCellValue(spreadsheet, worksheet, 1, 3, CellValues.String, "Tipo de Doc.", _tableHeaderStyle, true);
                OpenXML.SetCellValue(spreadsheet, worksheet, 2, 3, CellValues.String, "Nr. Doc.", _tableHeaderStyle, true);
                OpenXML.SetCellValue(spreadsheet, worksheet, 3, 3, CellValues.String, "Valor Doc.", _tableHeaderStyle, true);
                OpenXML.SetCellValue(spreadsheet, worksheet, 4, 3, CellValues.String, "Valor Pendente", _tableHeaderStyle, true);
                OpenXML.SetCellValue(spreadsheet, worksheet, 5, 3, CellValues.String, "Data Registo", _tableHeaderStyle, true);
                OpenXML.SetCellValue(spreadsheet, worksheet, 6, 3, CellValues.String, "Data Vencimento", _tableHeaderStyle, true);

                // Table
                var currentAccountData = GetData();

                uint rowIdx = 4;
                foreach (var item in currentAccountData)
                {
                    OpenXML.SetCellValue(spreadsheet, worksheet, 1, rowIdx, CellValues.String, item.TypeDoc, _generalStyle, true);
                    OpenXML.SetCellValue(spreadsheet, worksheet, 2, rowIdx, CellValues.String, item.NrDoc, _generalStyle, true);
                    if (item.DocValue != null)
                        OpenXML.SetCellValue(spreadsheet, worksheet, 3, rowIdx, Convert.ToDouble(item.DocValue), _currencyStyle, true);
                    if (item.PendingValue != null)
                        OpenXML.SetCellValue(spreadsheet, worksheet, 4, rowIdx, Convert.ToDouble(item.PendingValue), _currencyStyle, true);
                    if (item.RecordDate != null)
                        OpenXML.SetCellValue(spreadsheet, worksheet, 5, rowIdx, item.RecordDate, _dateStyle, true);
                    if (item.SalaryDate != null)
                        OpenXML.SetCellValue(spreadsheet, worksheet, 6, rowIdx, item.SalaryDate, _dateStyle, true);

                    rowIdx++;
                }

                TableParts tableParts1 = new TableParts() { Count = (uint)1U };
                TablePart tablePart1 = new TablePart() { Id = "rId1" };
                tableParts1.Append(tablePart1);
                worksheet.Append(tableParts1);

                OpenXML.CreateTable(spreadsheet, worksheet, "table1", 3U, 1U, (uint)currentAccountData.Count + 3, 6U);
                TableDefinitionPart tableDefinitionPart1 = worksheet.WorksheetPart.TableDefinitionParts.FirstOrDefault<TableDefinitionPart>();

                // Total
                OpenXML.SetCellValue(spreadsheet, worksheet, 8, 2, CellValues.String, "Total Documento", _totalHeaderStyle, true);
                OpenXML.SetCellFormula(spreadsheet, worksheet, 9, 2, "=SUM(C4:C1048576)", 2, true);

                OpenXML.SetCellValue(spreadsheet, worksheet, 8, 3, CellValues.String, "Total Pendente", _totalHeaderStyle, true);
                OpenXML.SetCellFormula(spreadsheet, worksheet, 9, 3, "=SUM(D4:D1048576)", 2, true);

                // Last Sync
                var lastSync = DateTime.Now;
                OpenXML.SetCellValue(spreadsheet, worksheet, 8, 1, CellValues.String, "Última Sincronização", _totalHeaderStyle, true);
                OpenXML.SetCellValue(spreadsheet, worksheet, 9, 1, lastSync, _dateStyle, true);

                // Set column widths
                OpenXML.SetColumnWidth(worksheet, 1, 22);
                OpenXML.SetColumnWidth(worksheet, 2, 22);
                OpenXML.SetColumnWidth(worksheet, 3, 22);
                OpenXML.SetColumnWidth(worksheet, 4, 22);
                OpenXML.SetColumnWidth(worksheet, 5, 22);
                OpenXML.SetColumnWidth(worksheet, 6, 22);

                OpenXML.SetColumnWidth(worksheet, 8, 22);
                OpenXML.SetColumnWidth(worksheet, 9, 22);

                worksheet.Save();
                spreadsheet.Close();

                return ms.ToArray();
            }
        }
    }
}

CreateTable Method

public static void CreateTable(SpreadsheetDocument document, Worksheet worksheet, string TableName, uint rowStart, uint columnStart, uint rowEnd, uint columnEnd)
    {
        string cellAddressStart = ColumnNameFromIndex(columnStart) + rowStart;
        string cellAddressEnd = ColumnNameFromIndex(columnEnd) + rowEnd;
        string tableAddress = cellAddressStart + ":" + cellAddressEnd;

        TableDefinitionPart tableDefinitionPart1 = worksheet.WorksheetPart.AddNewPart<TableDefinitionPart>("rId1");

        Table table1 = new Table() { Id = (UInt32Value)2U, Name = TableName, DisplayName = TableName, Reference = tableAddress, TotalsRowShown = false };
        AutoFilter autoFilter1 = new AutoFilter() { Reference = tableAddress };

        uint cellRangeCount = columnEnd - columnStart;
        TableColumns tableColumns1 = new TableColumns() { Count = (UInt32Value)(cellRangeCount) };
        uint cellCount = 1;
        for (uint idxColumns = columnStart; idxColumns < columnEnd; idxColumns++)
        {
            CellValue cell = GetCellValue(document, worksheet, idxColumns, rowStart);
            TableColumn tableColumn1 = new TableColumn() { Id = (UInt32Value)cellCount, Name = cell.InnerText };
            tableColumns1.Append(tableColumn1);
            cellCount++;
        }

        TableStyleInfo tableStyleInfo1 = new TableStyleInfo() { Name = "TableStyleMedium2", 
                                                                ShowFirstColumn = false, 
                                                                ShowLastColumn = false, 
                                                                ShowRowStripes = true, 
                                                                ShowColumnStripes = false };

        table1.Append(autoFilter1);
        table1.Append(tableColumns1);
        table1.Append(tableStyleInfo1);

        tableDefinitionPart1.Table = table1;
    }

I'm adding the cells (rows) with

var currentAccountData = GetData();

uint rowIdx = 4;
foreach (var item in currentAccountData)
{
    OpenXML.SetCellValue(spreadsheet, worksheet, 1, rowIdx, CellValues.String, item.TypeDoc, _generalStyle, true);
    OpenXML.SetCellValue(spreadsheet, worksheet, 2, rowIdx, CellValues.String, item.NrDoc, _generalStyle, true);
    if (item.DocValue != null)
        OpenXML.SetCellValue(spreadsheet, worksheet, 3, rowIdx, Convert.ToDouble(item.DocValue), _currencyStyle, true);
    if (item.PendingValue != null)
        OpenXML.SetCellValue(spreadsheet, worksheet, 4, rowIdx, Convert.ToDouble(item.PendingValue), _currencyStyle, true);
    if (item.RecordDate != null)
        OpenXML.SetCellValue(spreadsheet, worksheet, 5, rowIdx, item.RecordDate, _dateStyle, true);
    if (item.SalaryDate != null)
        OpenXML.SetCellValue(spreadsheet, worksheet, 6, rowIdx, item.SalaryDate, _dateStyle, true);

    rowIdx++;
}

and then creating the tablepart

TableParts tableParts1 = new TableParts() { Count = (uint)1U };
TablePart tablePart1 = new TablePart() { Id = "rId1" };
tableParts1.Append(tablePart1);
worksheet.Append(tableParts1);

after that, i use the method OpenXML.CreateTable

OpenXML.CreateTable(spreadsheet, worksheet, "table1", 3U, 1U, (uint)currentAccountData.Count + 3, 6U);

what am i doing wrong?

If you need more code, i will upload the application. Thank you.

EDIT:

When i open the excel it says:

Removed Part: /xl/tables/table.xml part with XML error. (Table) A document must contain exactly one root element. Line 1, column 0.

what am I doing wrong?

your debugging approach seems wrong to me. I suggest one of the following:

  • a. compare generated xml to a hand-crafted xlsx file.
  • b. start from code, generated by OpenXML productivity tool (so it generates static table in your application) and modify/re-factor it step-by-step to use dynamic data from your app and match your coding standards - so you spot your error on one of the iterations or just get a clean working solution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM