Question

I have followed the routine used here to copy a worksheet in a workbook into the same workbook by using .AddPart<>() and a temporary workbook to serve as a middleman, but have come up with some issues. Prior to copying the worksheet, I have made modifications to it. However, the copied version of the worksheet retains the original sheet, not the modified one. Here's the code:

Document = SpreadsheetDocument.Open(filename, true);
SetupPages(0, nSections);
CopySheet(1);

SetupPages() simply gets a worksheet which has a table with only one row and then copies that row nSections - 1 times, so that the table ends up with nSections rows. This works.

CopySheet() has the following code:

private void CopySheet(int sNum)
{
    var tempSheet = SpreadsheetDocument.Create(new MemoryStream(), SpreadsheetDocumentType.Workbook);
    WorkbookPart tempWBP = tempSheet.AddWorkbookPart();
    var part = Document.XGetWorkSheetPart(sNum);
    WorksheetPart tempWSP = tempWBP.AddPart<WorksheetPart>(part);
    var copy = Document.WorkbookPart.AddPart<WorksheetPart>(tempWSP);

    var sheets = Document.WorkbookPart.Workbook.GetFirstChild<Sheets>();
    var sheet = new Sheet();
    sheet.Id = Document.WorkbookPart.GetIdOfPart(copy);
    sheet.Name = "AAA";
    sheet.SheetId = (uint)sheets.ChildElements.Count + 1;
    sheets.Append(sheet);
}

This method gets the desired workSheetPart and uses tempSheet.AddPart<>() to perform a deep copy of the desired part and then uses it again to deep copy it back into the original document. This way all the referenced objects contained within the desired workSheetPart are also copied.

This works, in as much as I now have a copy of the workSheet in my original document. However, all the modifications made by SetupPages() prior to calling CopySheet() do not appear in the copied workSheet. So I now have the original Sheet with (in this example) 11 rows and the copied Sheet with only 1 row.

I have tried placing a Document.WorkBookPart.WorkBook.Save() between SetupPages() and CopySheet(), to see if "saving the changes" would make them available to be copied as well, but to no avail.

Is there any OpenXML trick I am unaware of?

EDIT: Using the debugger I have just noticed that part.WorkSheet.ChildElements.Count = 15 (the expected number of rows after SetupPages). However, tempWBP.WorkSheet.ChildElements.Count = 5 (the number of rows in the original sheet prior to the modifications of SetupPages). For whatever reason, the new Rows added by my program are not being deep-copied. Now this I just don't understand. Should it have something to do with some improper binding of the rows on my part or something, here's the definition of SetupPages():

public void SetupPages(int p, int nSections)
{
    Regex rn = new Regex("[0-9]+");
    Regex rs = new Regex("[A-Z]+");
    SheetData sData = Document.XGet<SheetData>(2 * p + 1);
    for (uint i = 1; i < nSections; i++)
    {
        var r = sData.ChildElements.GetItem(4).Clone() as Row;
        r.RowIndex.Value += i;
        foreach (OpenXmlElement _c in r.ChildElements)
        {
            var c = _c as Cell;
            Match mn = rn.Match(c.CellReference.Value);
            Match ms = rs.Match(c.CellReference.Value);
            string str = (int.Parse(mn.Value) + i).ToString();
            c.CellReference.Value = ms.Value + str;
        }
        (r.FirstChild as Cell).CellValue = new CellValue((i + 1).ToString());
        sData.Append(r);
    }
}

EDIT 2: I've been able to make the copy work fully, but it's very unelegant and doesn't fix what is clearly an error in the code, but merely an emergency measure to pretend that there isn't a problem. I basically get a clone of the original SheetData (which contains the modifications done in SetupPages) and set it as the SheetData of the copied Sheet. I'm just posting this here for information purposes, but if someone could still please point out what is wrong in the code that I'm not seeing, I'll be quite thankful. Here's the "hacked" version of CopySheet(), if anyone is interested.

private void CopySheet(int sNum)
{
    var tempSheet = SpreadsheetDocument.Create(new MemoryStream(), SpreadsheetDocumentType.Workbook);
    WorkbookPart tempWBP = tempSheet.AddWorkbookPart();
    var part = Document.XGetWorkSheetPart(sNum);
    var b = part.Worksheet.ChildElements[5].Clone() as SheetData;
    WorksheetPart tempWSP = tempWBP.AddPart<WorksheetPart>(part);

    var copy = Document.WorkbookPart.AddPart<WorksheetPart>(tempWSP);
    copy.Worksheet.RemoveChild<SheetData>(copy.Worksheet.ChildElements[5] as SheetData);
    copy.Worksheet.InsertAt<SheetData>(b, 5);

    var sheets = Document.WorkbookPart.Workbook.GetFirstChild<Sheets>();
    var sheet = new Sheet();
    sheet.Id = Document.WorkbookPart.GetIdOfPart(copy);
    sheet.Name = "AAA";
    sheet.SheetId = (uint)sheets.ChildElements.Count + 1;
    sheets.Append(sheet);
}
Was it helpful?

Solution

I've solved this as best I can (and from what I've gathered from other forums I've asked, as best "they" can) by closing and opening the file prior to creating the copies. This makes the changes permanent and then, when copied, the changes are copied, too. With this, the "hack" described above becomes unnecessary. The final version of the code therefore became (with a change to avoid SheetID and Sheet.Name conflicts):

private void CopySheet(int sNum, int pNum, string type)
{
    var tempSheet = SpreadsheetDocument.Create(new MemoryStream(), SpreadsheetDocumentType.Workbook);
    WorkbookPart tempWBP = tempSheet.AddWorkbookPart();
    var part = Document.XGetWorkSheetPart(sNum);
    WorksheetPart tempWSP = tempWBP.AddPart<WorksheetPart>(part);
    var copy = Document.WorkbookPart.AddPart<WorksheetPart>(tempWSP);
    var sheets = Document.WorkbookPart.Workbook.GetFirstChild<Sheets>();
    var sheet = new Sheet();
    sheet.Id = Document.WorkbookPart.GetIdOfPart(copy);
    sheet.Name = "Phase " + pNum + " " + type;
    uint id = 1;
    bool valid = false;
    while (!valid)
    {
        uint temp = id;
        foreach (OpenXmlElement e in sheets.ChildElements)
        {
            var s = e as Sheet;
            if (id == s.SheetId.Value)
            {
                id++;
                break;
            }
        }
        if (temp == id)
            valid = true;
    }
    sheet.SheetId = id;
    sheets.Append(sheet);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top