简体   繁体   中英

How can I deep clone row in .xlsx file using openXML?

I have template.xlsx file which I must modify by adding a few copies of one row in specific index. When I try use clone method for this operation I add a row but each row modify each other. I need to create deep clone of opemxml row object but when I try this I have an error that openxml row object are not serialized. How can I deep clone row in .xlsx file using openXML with serialization or if there is another way to deep clone openxml row object?

You can deep clone using .CloneNode(true) on an OpenXmlElement

So if you want to duplicate a row inside a table it will look like

// suppose table an OpenXml Table and row the row you want to clone
table.Append(row.CloneNode(true));

edit : to insert it before a specific row

// suppose r is the row you want to insert it before
r.InsertBeforeSelf(row.CloneNode(true));

In my case, I needed to copy several rows at the end of the sheet. I literally needed to clone a specific range of rows, ie values, styles and formulas, merged cells. After spending many hours on the problem of copy and paste several rows, I finally found a solution. I was able to copy rows 18 to 26 and paste them from row 27.

Example below:

在此处输入图片说明

Below is the code:

public static void CopyRowRange(SpreadsheetDocument document, string sheetName,
       int srcRowFrom, int srcRowTo, int destRowFrom)
    {
        WorkbookPart workbookPart = document.WorkbookPart;
        if (srcRowTo < srcRowFrom || destRowFrom < srcRowFrom) return;
        int destRowFromBase = destRowFrom;

        WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
        SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

        IList<Cell> cells = sheetData.Descendants<Cell>().Where(c =>
            GetRowIndex(c.CellReference) >= srcRowFrom &&
            GetRowIndex(c.CellReference) <= srcRowTo).ToList<Cell>();

        if (cells.Count() == 0) return;

        int copiedRowCount = srcRowTo - srcRowFrom + 1;

        MoveRowIndex(document, sheetName, destRowFrom - 1, srcRowTo, srcRowFrom);

        IDictionary<int, IList<Cell>> clonedCells = null;

        IList<Cell> formulaCells = new List<Cell>();

        IList<Row> cloneRelatedRows = new List<Row>();

        destRowFrom = destRowFromBase;
        int changedRowsCount = destRowFrom - srcRowFrom;

        formulaCells.Clear();

        clonedCells = new Dictionary<int, IList<Cell>>();

        foreach (Cell cell in cells)
        {
            Cell newCell = (Cell)cell.CloneNode(true);
            int index = Convert.ToInt32(GetRowIndex(cell.CellReference));

            int rowIndex = index - changedRowsCount;
            newCell.CellReference = GetColumnName(cell.CellReference) + rowIndex.ToString();

            IList<Cell> rowCells = null;
            if (clonedCells.ContainsKey(rowIndex))
                rowCells = clonedCells[rowIndex];
            else
            {
                rowCells = new List<Cell>();
                clonedCells.Add(rowIndex, rowCells);
            }
            rowCells.Add(newCell);

            if (newCell.CellFormula != null && newCell.CellFormula.Text.Length > 0)
            {
                formulaCells.Add(newCell);
            }
        }

        foreach (int rowIndex in clonedCells.Keys)
        {
            Row row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).FirstOrDefault();
            if (row == null)
            {
                row = new Row() { RowIndex = (uint)rowIndex };

                Row refRow = sheetData.Elements<Row>().Where(r => r.RowIndex > rowIndex).OrderBy(r => r.RowIndex).FirstOrDefault();
                if (refRow == null)
                    sheetData.AppendChild<Row>(row);
                else
                    sheetData.InsertBefore<Row>(row, refRow);
            }
            row.Append(clonedCells[rowIndex].ToArray());

            cloneRelatedRows.Add(row);
        }

        ChangeFormulaRowNumber(worksheetPart.Worksheet, formulaCells, changedRowsCount);

        foreach (Row row in cloneRelatedRows)
        {
            IList<Cell> cs = row.Elements<Cell>().OrderBy(c => c.CellReference.Value).ToList<Cell>();
            row.RemoveAllChildren();
            row.Append(cs.ToArray());
        }

        MergeCells mcells = worksheetPart.Worksheet.GetFirstChild<MergeCells>();
        if (mcells != null)
        {
            IList<MergeCell> newMergeCells = new List<MergeCell>();
            IEnumerable<MergeCell> clonedMergeCells = mcells.Elements<MergeCell>().
                Where(m => MergeCellInRange(m, srcRowFrom, srcRowTo)).ToList<MergeCell>();
            foreach (MergeCell cmCell in clonedMergeCells)
            {
                MergeCell newMergeCell = CreateChangedRowMergeCell(worksheetPart.Worksheet, cmCell, changedRowsCount);
                newMergeCells.Add(newMergeCell);
            }
            uint count = mcells.Count.Value;
            mcells.Count = new UInt32Value(count + (uint)newMergeCells.Count);
            mcells.Append(newMergeCells.ToArray());
        }
    }

    private static WorksheetPart
       GetWorksheetPartByName(SpreadsheetDocument document,
       string sheetName)
    {
        IEnumerable<Sheet> sheets =
            document.WorkbookPart.Workbook.GetFirstChild<Sheets>().
            Elements<Sheet>().Where(s => s.Name == sheetName);

        if (sheets.Count() == 0)
        {
            return null;
        }

        string relationshipId = sheets.First().Id.Value;
        WorksheetPart worksheetPart = (WorksheetPart)
             document.WorkbookPart.GetPartById(relationshipId);
        return worksheetPart;

    }
    private static void MoveRowIndex(SpreadsheetDocument document, string sheetName, int destRowFrom, int srcRowTo, int srcRowFrom)
    {
        WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
        SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

        uint newRowIndex;

        IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(r => r.RowIndex.Value >= srcRowFrom && r.RowIndex.Value <= srcRowTo);
        foreach (Row row in rows)
        {
            newRowIndex = Convert.ToUInt32(destRowFrom + 1);

            foreach (Cell cell in row.Elements<Cell>())
            {
                string cellReference = cell.CellReference.Value;
                cell.CellReference = new StringValue(cellReference.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
            }
            row.RowIndex = new UInt32Value(newRowIndex);

            destRowFrom++;
        }

    }

    private static void ChangeFormulaRowNumber(Worksheet worksheet, IList<Cell> formulaCells, int changedRowsCount)
    {
        foreach (Cell formulaCell in formulaCells)
        {
            Regex regex = new Regex(@"\d+");
            var rowIndex = Convert.ToInt32(regex.Match(formulaCell.CellReference).Value);

            Regex regex2 = new Regex("[A-Za-z]+");
            var columnIndex = regex2.Match(formulaCell.CellReference).Value;

            int newRowIndex = rowIndex + changedRowsCount;
            Cell cell = GetCell(worksheet, columnIndex, newRowIndex);
            cell.CellFormula = new CellFormula(cell.CellFormula.Text.Replace($"{rowIndex}",$"{newRowIndex}"));
        }
    }

    private static MergeCell CreateChangedRowMergeCell(Worksheet worksheet, MergeCell cmCell, int changedRows)
    {
        string[] cells = cmCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);

        Regex regex = new Regex(@"\d+");
        var rowIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
        var rowIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);

        Regex regex2 = new Regex("[A-Za-z]+");
        var columnIndex1 = regex2.Match(cells[0]).Value;
        var columnIndex2 = regex2.Match(cells[1]).Value;

        var cell1Name = $"{columnIndex1}{rowIndex1 + changedRows}";
        var cell2Name = $"{columnIndex2}{rowIndex2 + changedRows}";

        CreateSpreadsheetCellIfNotExist(worksheet, cell1Name);
        CreateSpreadsheetCellIfNotExist(worksheet, cell2Name);

        return new MergeCell() { Reference = new StringValue(cell1Name + ":" + cell2Name) };
    }

    private static bool MergeCellInRange(MergeCell mergeCell, int srcRowFrom, int srcRowTo)
    {
        string[] cells = mergeCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);

        Regex regex = new Regex(@"\d+");
        var cellIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
        var cellIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);

        if (srcRowFrom <= cellIndex1 && cellIndex1 <= srcRowTo &&
            srcRowFrom <= cellIndex2 && cellIndex2 <= srcRowTo)
            return true;
        else
            return false;
    }

    private static uint GetRowIndex(string cellName)
    {
        Regex regex = new Regex(@"\d+");
        Match match = regex.Match(cellName);

        return uint.Parse(match.Value);
    }

    private static string GetColumnName(string cellName)
    {
        Regex regex = new Regex("[A-Za-z]+");
        Match match = regex.Match(cellName);

        return match.Value;
    }

    private static void CreateSpreadsheetCellIfNotExist(Worksheet worksheet, string cellName)
    {
        string columnName = GetColumnName(cellName);
        uint rowIndex = GetRowIndex(cellName);

        IEnumerable<Row> rows = worksheet.Descendants<Row>().Where(r => r.RowIndex.Value == rowIndex);

        if (rows.Count() == 0)
        {
            Row row = new Row() { RowIndex = new UInt32Value(rowIndex) };
            Cell cell = new Cell() { CellReference = new StringValue(cellName) };
            row.Append(cell);
            worksheet.Descendants<SheetData>().First().Append(row);
            worksheet.Save();
        }
        else
        {
            Row row = rows.First();

            IEnumerable<Cell> cells = row.Elements<Cell>().Where(c => c.CellReference.Value == cellName);

            if (cells.Count() == 0)
            {
                Cell cell = new Cell() { CellReference = new StringValue(cellName) };
                row.Append(cell);
                worksheet.Save();
            }
        }
    }

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