Writing to Excel with Open Xml gives issue after certain columns

偶尔善良 提交于 2021-01-29 02:57:08

问题


I am using C# with Open XML Format SDK 2.0, Trying to write an Excel report file. My problem is when I cross the Z column (reaching AA) the file goes corrupted and cannot be open manually by the user. Any ideas?

  static void Main(string[] args)
            {
                PortfolioReport report = new PortfolioReport("Keywords");
                report.CreateReport();
            }
 public PortfolioReport(string client)
        {
            string newFileName = path + client + ".xlsx";
            if (File.Exists(newFileName))
            {
                File.Delete(newFileName);
            }
            FileInfo newFile = new FileInfo(newFileName);
            using (ExcelPackage package = new ExcelPackage(newFile))
            {
                ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Keywords");
                package.Save();
            }
            document = SpreadsheetDocument.Open(newFileName, true);
            wbPart = document.WorkbookPart;
        }

               static readonly string[] Columns = new[] { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV", "AW", "AX", "AY", "AZ", "BA", "BB", "BC", "BD", "BE", "BF", "BG", "BH" };
                    public static string IndexToColumn(int index)
                    {
                        if (index < 0)
                            throw new IndexOutOfRangeException("index must be a positive number");

                        return Columns[index];
                    }
                    public static object GetPropValue(object src, string propName)
                    {
                        return src.GetType().GetProperty(propName).GetValue(src, null);
                    }
                    // Create a new Portfolio report
                    public void CreateReport()
                    {
                        string wsName = "Report Summary";
                        wbPart.Workbook.Descendants<Sheet>().FirstOrDefault().Name = wsName;

                        var currentUser =  UsersInfo.Keywords;

                        //set the domainAge
                        KeywordsModule.SetYearsAndMonths(currentUser.Keywords);
                        //set the url site relevency in words
                        KeywordsModule.SetAverageUrlSiteRelevencyLiteral(currentUser.Keywords);
                        //set a model for the excel which will convert the keyword to the custom model according to gui names
                        List<KeywordModelForExcelExport> keywordsForExports = KeywordsModule.PrepreKeywordSforExport(currentUser.Keywords);

                        //we set the column headings
                        var properties = typeof(KeywordModelForExcelExport).GetProperties();
                        for (int i = 0; i < properties.Length; i++)
                        {
                            var cell = IndexToColumn(i) + 1;
                            UpdateValue(wsName, cell, properties[i].Name, 0, true);

                        }

                        //now we set the keyword values
                        int row = 2;
                        foreach (var keywordForExport in keywordsForExports)
                        {
                            for (int i = 0; i < properties.Length; i++)
                            {

                                var val = GetPropValue(keywordForExport, properties[i].Name);
                                var cell = IndexToColumn(i) + row;
                                if (val != null)
                                    UpdateValue(wsName, cell, val.ToString(), 0, true);

                            }
                            row++;
                        }

                        // All done! Close and save the document.
                        document.Close();
                    }

                    // Given a Worksheet and an address (like "AZ254"), either return a cell reference, or 
                    // create the cell reference and return it.
                    private Cell InsertCellInWorksheet(Worksheet ws, string addressName)
                    {
                        SheetData sheetData = ws.GetFirstChild<SheetData>();
                        Cell cell = null;

                        UInt32 rowNumber = GetRowIndex(addressName);
                        Row row = GetRow(sheetData, rowNumber);

                        // If the cell you need already exists, return it.
                        // If there is not a cell with the specified column name, insert one.  
                        Cell refCell = row.Elements<Cell>().
                            Where(c => c.CellReference.Value == addressName).FirstOrDefault();
                        if (refCell != null)
                        {
                            cell = refCell;
                        }
                        else
                        {
                            cell = CreateCell(row, addressName);
                        }
                        return cell;
                    }

                    private Cell CreateCell(Row row, String address)
                    {
                        Cell cellResult;
                        Cell refCell = null;

                        // Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
                        foreach (Cell cell in row.Elements<Cell>())
                        {
                            if (string.Compare(cell.CellReference.Value, address, true) > 0)
                            {
                                refCell = cell;
                                break;
                            }
                        }

                        cellResult = new Cell();
                        cellResult.CellReference = address;

                        row.InsertBefore(cellResult, refCell);
                        return cellResult;
                    }

                    private Row GetRow(SheetData wsData, UInt32 rowIndex)
                    {
                        var row = wsData.Elements<Row>().
                        Where(r => r.RowIndex.Value == rowIndex).FirstOrDefault();
                        if (row == null)
                        {
                            row = new Row();
                            row.RowIndex = rowIndex;
                            wsData.Append(row);
                        }
                        return row;
                    }

                    private UInt32 GetRowIndex(string address)
                    {
                        string rowPart;
                        UInt32 l;
                        UInt32 result = 0;

                        for (int i = 0; i < address.Length; i++)
                        {
                            if (UInt32.TryParse(address.Substring(i, 1), out l))
                            {
                                rowPart = address.Substring(i, address.Length - i);
                                if (UInt32.TryParse(rowPart, out l))
                                {
                                    result = l;
                                    break;
                                }
                            }
                        }
                        return result;
                    }

                    public bool UpdateValue(string sheetName, string addressName, string value, UInt32Value styleIndex, bool isString)
                    {
                        // Assume failure.
                        bool updated = false;

                        Sheet sheet = wbPart.Workbook.Descendants<Sheet>().Where((s) => s.Name == sheetName).FirstOrDefault();

                        if (sheet != null)
                        {
                            Worksheet ws = ((WorksheetPart)(wbPart.GetPartById(sheet.Id))).Worksheet;
                            Cell cell = InsertCellInWorksheet(ws, addressName);

                            if (isString)
                            {
                                // Either retrieve the index of an existing string,
                                // or insert the string into the shared string table
                                // and get the index of the new item.
                                int stringIndex = InsertSharedStringItem(wbPart, value);

                                cell.CellValue = new CellValue(stringIndex.ToString());
                                cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
                            }
                            else
                            {
                                cell.CellValue = new CellValue(value);
                                cell.DataType = new EnumValue<CellValues>(CellValues.Number);
                            }

                            if (styleIndex > 0)
                                cell.StyleIndex = styleIndex;

                            // Save the worksheet.
                            ws.Save();
                            updated = true;
                        }

                        return updated;
                    }

                    // Given the main workbook part, and a text value, insert the text into the shared
                    // string table. Create the table if necessary. If the value already exists, return
                    // its index. If it doesn't exist, insert it and return its new index.
                    private int InsertSharedStringItem(WorkbookPart wbPart, string value)
                    {
                        int index = 0;
                        bool found = false;
                        var stringTablePart = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();

                        // If the shared string table is missing, something's wrong.
                        // Just return the index that you found in the cell.
                        // Otherwise, look up the correct text in the table.
                        if (stringTablePart == null)
                        {
                            // Create it.
                            stringTablePart = wbPart.AddNewPart<SharedStringTablePart>();
                            stringTablePart.SharedStringTable = new SharedStringTable();
                        }

                        var stringTable = stringTablePart.SharedStringTable;
                        //            if (stringTable == null)
                        //            {
                        //                stringTable = new SharedStringTable();
                        //            }

                        // Iterate through all the items in the SharedStringTable. If the text already exists, return its index.
                        foreach (SharedStringItem item in stringTable.Elements<SharedStringItem>())
                        {
                            if (item.InnerText == value)
                            {
                                found = true;
                                break;
                            }
                            index += 1;
                        }

                        if (!found)
                        {
                            stringTable.AppendChild(new SharedStringItem(new Text(value)));
                            stringTable.Save();
                        }

                        return index;
                    }

回答1:


Your ordering is broken in CreateCell for cell references after Z. You are currently using string.Compare but that will do an alpha comparison meaning AA1 is before Z1 rather than after it.

There are various ways you could fix this - one way would be to convert the cellReference to a column index and then compare those instead of comparing the cell references directly. For example:

private static int? GetColumnIndex(string cellRef)
{
    if (string.IsNullOrEmpty(cellRef))
        return null;

    cellRef = cellRef.ToUpper();

    int columnIndex = -1;
    int mulitplier = 1;

    foreach (char c in cellRef.ToCharArray().Reverse())
    {
        if (char.IsLetter(c))
        {
            columnIndex += mulitplier * ((int)c - 64);
            mulitplier = mulitplier * 26;
        }
    }

    return columnIndex;
}

Then instead of

if (string.Compare(cell.CellReference.Value, address, true) > 0)
{
    refCell = cell;
    break;
}

You can do

if (GetColumnIndex(cell.CellReference.Value) > GetColumnIndex(address))
{
    refCell = cell;
    break;
}


来源:https://stackoverflow.com/questions/34156735/writing-to-excel-with-open-xml-gives-issue-after-certain-columns

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!