制作OpenXML工作表的多个副本时出错

时间:2014-02-15 16:05:07

标签: c# excel openxml

我有一个修改现有电子表格的程序。部分程序会创建某些工作表的副本。要创建一个副本,程序运行完美,生成的文件也很好,在Excel中打开时不会出现任何错误。但是,在创建同一工作表的两个副本时,程序仍然可以正常运行,但是在Excel中打开时,会出现以下关于不可读内容的错误:

Repaired Records: Worksheet properties from /xl/workbook.xml part (Workbook)

以下是用于执行复制的代码:

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);
    var sheetData = part.Worksheet.ChildElements[5].Clone() as SheetData;
    var merge = part.Worksheet.ChildElements[6].Clone() as MergeCells;
    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>(sheetData, 5);
    //copy.Worksheet.RemoveChild<MergeCells>(copy.Worksheet.ChildElements[6] as MergeCells);
    //copy.Worksheet.InsertAt<MergeCells>(merge, 6);
    //copy.Worksheet.SheetProperties.CodeName.Value = "Phase" + pNum + type;

    var sheets = Document.WorkbookPart.Workbook.Sheets;
    var sheet = new Sheet();
    sheet.Id = Document.WorkbookPart.GetIdOfPart(copy);
    sheet.Name = "Phase " + pNum + " " + type;
    sheet.SheetId = (uint)sheets.ChildElements.Count;
    sheets.Append(sheet);
}

此方法利用.AddPart<>()执行任何Part(及其引用的任何部分)的深层副本的事实,该部分尚未属于文档,在临时工作表的帮助下创建工作表的所有引用部分的深层副本。

如上所述,如果函数仅针对给定工作表调用一次,则此方法非常有效。但是,如果多次调用它,则在Excel中打开文件时会出现无法读取的内容错误。话虽这么说,文件本身看起来很好,没有丢失数据或任何东西(这将有助于弄清楚到底出了什么问题),只是错误说有错误。

注释掉的行是&#34; hack&#34;我必须处理有关.AddPart<>()的问题,但我不会在这里详细介绍它们,因为我已经发布了关于here的信息(但我仍然没有#&}# 39;得到答复,所以请务必回答这个问题!)。话虽这么说,这些线似乎与当前的问题无关,因为错误出现时有或没有这些代码行。

2 个答案:

答案 0 :(得分:1)

我注意到源文件的表单由于某种原因没有正确编号,因此它们的SheetID不是顺序的(6,1,3)。因此,我的程序是在使用代码表创建文件的两个副本时.SheetID = sheets.ChildElements.Count + 1,将值设置为4,5,6和7,这会导致与现有工作表冲突。因此,我修改了该部分,以便程序始终获得有效的ID:

// ...
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;
//...

这样我的sheetID变为(6,1,3,2,4,5,7),因此没有冲突,也没有错误!

答案 1 :(得分:0)

    static WorksheetPart GetWorkSheetPart(WorkbookPart workbookPart, string sheetName)
    {
        //Get the relationship id of the sheetname
        string relId = workbookPart.Workbook.Descendants<Sheet>()
        .Where(s => s.Name.Value.Equals(sheetName))
        .First()
        .Id;
        return (WorksheetPart)workbookPart.GetPartById(relId);
    }

    static void FixupTableParts(WorksheetPart worksheetPart, int numTableDefParts)
    {
        //Every table needs a unique id and name
        foreach (TableDefinitionPart tableDefPart in worksheetPart.TableDefinitionParts)
        {
            tableId++;
            tableDefPart.Table.Id = (uint)tableId;
            tableDefPart.Table.DisplayName = "CopiedTable" + tableId;
            tableDefPart.Table.Name = "CopiedTable" + tableId;
            tableDefPart.Table.Save();
        }
    }

    private void CopySheet(SpreadsheetDocument mySpreadsheet, string sheetName, string clonedSheetName)
    {
        WorkbookPart workbookPart = mySpreadsheet.WorkbookPart;
        IEnumerable<Sheet> source = workbookPart.Workbook.Descendants<Sheet>();
        Sheet sheet = Enumerable.First<Sheet>(source, (Func<Sheet, bool>)(s => (string)s.Name == sheetName));
        string sheetWorkbookPartId = (string)sheet.Id;
        WorksheetPart sourceSheetPart = (WorksheetPart)workbookPart.GetPartById(sheetWorkbookPartId);
        SpreadsheetDocument tempSheet = SpreadsheetDocument.Create(new MemoryStream(), mySpreadsheet.DocumentType);
        WorkbookPart tempWorkbookPart = tempSheet.AddWorkbookPart();
        WorksheetPart tempWorksheetPart = tempWorkbookPart.AddPart<WorksheetPart>(sourceSheetPart);
        //Add cloned sheet and all associated parts to workbook
        WorksheetPart clonedSheet = workbookPart.AddPart<WorksheetPart>(tempWorksheetPart);

        int numTableDefParts = sourceSheetPart.GetPartsCountOfType<TableDefinitionPart>();
        tableId = numTableDefParts;
        if (numTableDefParts != 0)
            FixupTableParts(clonedSheet, numTableDefParts);
        CleanView(clonedSheet);

        //Add new sheet to main workbook part
        Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
        Sheet copiedSheet = new Sheet();
        copiedSheet.Name = clonedSheetName;
        copiedSheet.Id = workbookPart.GetIdOfPart(clonedSheet);
        copiedSheet.SheetId = (uint)sheets.ChildElements.Count + 1;
        sheets.Append(copiedSheet);
        workbookPart.Workbook.Save();
    }

    static void CleanView(WorksheetPart worksheetPart)
    {
        SheetViews views = worksheetPart.Worksheet.GetFirstChild<SheetViews>();
        if (views != null)
        {
            views.Remove();
            worksheetPart.Worksheet.Save();
        }
    }