Search code examples
c#excelpowershellopenxmlworksheet

OpenXML Add Cell to WorkSheet


I am currently doing my head in working over the OpenXML 2.5 Framework on the MSDN site here, https://msdn.microsoft.com/en-us/library/office/cc861607.aspx

All methods I have tried to add a cell to an existing worksheet corrupt the workbook as the MSDN site only outlines creating the worksheet and not modifying it.

Everytime I add a cell the system wants a whole new worksheet and will not allow the addition of a cell to an existing worksheet. I have been banging my head for hours going over MSDN and Googling this with no luck.

The problem is I need a class that can receiving strings and update the excel file. Has anyone been able to add a cell to an existing worksheet? My issue seems to be due to a string by string solution.

Working input (PowerShell) only works if a new Worksheet is created for the Cell,

[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
$cSharpData = (
    [Reflection.Assembly]::LoadWithPartialName("DocumentFormat.OpenXml"),
    [Reflection.Assembly]::LoadWithPartialName("WindowsBase"),
    [Reflection.Assembly]::LoadWithPartialName("System.Linq")
)
[String]$cSharpClass = Get-Content .\method.cs
$cSharpType = Add-Type -ReferencedAssemblies $cSharpData -TypeDefinition $cSharpClass

$testData = Get-WmiObject Win32_QuickFixEngineering
[DoExcelMethod]::CreateXLSX('.\test.xlsx')

$locNo = 1
[DoExcelMethod]::AddSheetData('.\test.xlsx', $testData, 'TestWS', 'A', $locNo)

The file this is point at has the following,

using System;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

public class DoExcelMethod {

    private static int SharedDataItem(string sData, SharedStringTablePart ssPart) {
        if (ssPart.SharedStringTable == null) {
            ssPart.SharedStringTable = new SharedStringTable();
        }
        int cnt = 0;
        foreach (SharedStringItem sspItem in ssPart.SharedStringTable.Elements<SharedStringItem>()) {
            if (sspItem.InnerText == sData) {
                return cnt;
            }
            cnt++;
        }
        ssPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(sData)));
        ssPart.SharedStringTable.Save();
        return cnt;
    }

    private static WorksheetPart InsertWorksheet(string wsName, WorkbookPart wbPart) {
        WorksheetPart newWsPart = wbPart.AddNewPart<WorksheetPart>();
        newWsPart.Worksheet = new Worksheet(new SheetData());
        newWsPart.Worksheet.Save();
        Sheets sheets = wbPart.Workbook.GetFirstChild<Sheets>();
        string relId = wbPart.GetIdOfPart(newWsPart);
        uint sheetId = 1;
        if (sheets.Elements<Sheet>().Count() > 0) {
            sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
        }
        Sheet sheet = new Sheet() { Id = relId, SheetId = sheetId, Name = wsName };
        sheets.Append(sheet);
        wbPart.Workbook.Save();
        return newWsPart;
    }

    private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart) {
        Worksheet worksheet = worksheetPart.Worksheet;
        SheetData sheetData = worksheet.GetFirstChild<SheetData>();
        string cellReference = columnName + rowIndex;
        Row row;
        if (sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0) {
            row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
        } else {
            row = new Row() { RowIndex = rowIndex };
            sheetData.Append(row);
        }
        if (row.Elements<Cell>().Where(c => c.CellReference.Value == columnName + rowIndex).Count() > 0) {
            return row.Elements<Cell>().Where(c => c.CellReference.Value == cellReference).First();
        } else {
            Cell refCell = null;
            foreach (Cell cell in row.Elements<Cell>()) {
                if (string.Compare(cell.CellReference.Value, cellReference, true) > 0) {
                    refCell = cell;
                    break;
                }
            }
            Cell newCell = new Cell() { CellReference = cellReference };
            row.InsertBefore(newCell, refCell);
            worksheet.Save();
            return newCell;
        }
    }

    public static void CreateXLSX(string xlsxFile) {
        SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(xlsxFile, SpreadsheetDocumentType.Workbook);
        WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
        workbookpart.Workbook = new Workbook();
        WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
        worksheetPart.Worksheet = new Worksheet(new SheetData());
        Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
        Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Default" };
        sheets.Append(sheet);
        workbookpart.Workbook.Save();
        spreadsheetDocument.Close();
    }

    public static void AddSheetData(string xlsxFile, string psData, string wsName, string psCol, uint psRow) {
        using (SpreadsheetDocument sSheet = SpreadsheetDocument.Open(xlsxFile, true)) {
            SharedStringTablePart ssPart;
            if (sSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().Count() > 0) {
                ssPart = sSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
            } else {
                ssPart = sSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
            }
            int ssIns = SharedDataItem(psData, ssPart);
            WorksheetPart wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);
            Cell cell = InsertCellInWorksheet(psCol, psRow, wsPart);
            cell.CellValue = new CellValue(ssIns.ToString());
            cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
            wsPart.Worksheet.Save();
        }
    }
}

So despite this working I cannot get a cell into an existing worksheet, can anyone help as I am going insane :(

Thanks all


Solution

  • The issue you have is in the call to InsertWorksheet in AddSheetData. You are calling the InsertWorksheet method irrespective of whether or not the worksheet already exists. Instead of doing that, you can first search for the worksheet then if it exists you can use it and if it doesn't you can create a new one.

    Firstly, you can search for a WorksheetPart by its name using a method such as this one (taken from my answer here):

    private static WorksheetPart GetWorksheetPartBySheetName(WorkbookPart workbookPart, string sheetName)
    {
        WorksheetPart worksheetPart = null;
    
        //find the sheet (note this is case-sensitive)
        IEnumerable<Sheet> sheets = workbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().Where(s => s.Name == sheetName);
    
        if (sheets.Count() > 0)
        {
            string relationshipId = sheets.First().Id.Value;
            worksheetPart = (WorksheetPart)workbookPart.GetPartById(relationshipId);
        }
    
        return worksheetPart;
    }
    

    If that method finds the WorksheetPart then it will return it, if not it will return null.

    Once you have that you just need a small tweak to AddSheetData to call GetWorksheetPartBySheetName then only call InsertWorksheet if that method returns null. To do that you can replace this line

    WorksheetPart wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);
    

    with this

    WorksheetPart wsPart = GetWorksheetPartBySheetName(sSheet.WorkbookPart, wsName);
    if (wsPart == null)
        wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);