OpenXML добавить ячейку в рабочий лист

В настоящее время я работаю над OpenXML 2.5 Framework на сайте MSDN здесь, https://msdn.microsoft.com/en-us/library/office/cc861607.aspx

Все методы, которые я пытался добавить ячейку в существующий рабочий лист, повреждают книгу, поскольку сайт MSDN описывает только создание рабочего листа, а не его изменение.

Каждый раз, когда я добавляю ячейку, система требует совершенно новый рабочий лист и не разрешает добавлять ячейку к существующему рабочему листу. Я часами бился головой о MSDN и безуспешно гуглил.

Проблема в том, что мне нужен класс, который может получать строки и обновлять файл excel. Кто-нибудь смог добавить ячейку на существующий лист? Моя проблема, кажется, связана с решением строки за строкой.

Рабочий ввод (PowerShell) работает только в том случае, если для ячейки создан новый рабочий лист,

[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)

Файл, на который это указывает, имеет следующее:

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();
        }
    }
}

Поэтому, несмотря на эту работу, я не могу получить ячейку на существующем листе, может ли кто-нибудь помочь, поскольку я схожу с ума :(

Спасибо всем


person jaradsc    schedule 05.07.2016    source источник
comment
OpenXML прекрасен в том смысле, что он доступен, но плох тем, что с ним крайне сложно работать, как будто Microsoft на самом деле этого не хочет. Хорошая новость заключается в том, что для Excel вы можете использовать EPPlus, с которым гораздо проще работать. Я бы пошел прямо к его использованию.   -  person Scott Hannen    schedule 05.07.2016
comment
Существует также библиотека с именемclosedXML, которую я использую, когда мне нужно создать Excel в С#, ее очень просто использовать, например, создавать файлы, рабочие листы и ячейки closedxml.codeplex.com   -  person Brandon Tomblinson    schedule 05.07.2016
comment
Спасибо, Скотт, по ссылке я вижу, что это относится к Office 2007/Office 2010. Вы знаете, работает ли это с Office 2013/2016? У меня есть 2 полностью работоспособных решения, одно с использованием PowerShell Export-XLSX, но это Office 2007 и не работает для Office 2013/16, другое — открывать Excel и вставлять данные (через код), но тогда вы не можете лицензировать Excel на сервере . Будет полезно узнать о совместимости, если это известно, спасибо   -  person jaradsc    schedule 05.07.2016
comment
Спасибо, ребята, работает хорошо, но не с Office 2016, поэтому я вернулся к OpenXML 2.5, чтобы файлы можно было открывать в Office Excel 2016. Я опубликую здесь, как только взломаю это   -  person jaradsc    schedule 05.07.2016


Ответы (1)


Проблема у вас в вызове InsertWorksheet в AddSheetData. Вы вызываете метод InsertWorksheet независимо от того, существует ли уже рабочий лист. Вместо этого вы можете сначала найти рабочий лист, а затем, если он существует, вы можете использовать его, а если нет, вы можете создать новый.

Во-первых, вы можете искать WorksheetPart по его имени, используя метод, подобный этому (взято из моего ответа здесь):

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;
}

Если этот метод найдет WorksheetPart, он вернет его, если нет, он вернет null.

После того, как вы это сделаете, вам просто нужно немного изменить AddSheetData для вызова GetWorksheetPartBySheetName, а затем вызывать InsertWorksheet только в том случае, если этот метод возвращает null. Для этого вы можете заменить эту строку

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

с этим

WorksheetPart wsPart = GetWorksheetPartBySheetName(sSheet.WorkbookPart, wsName);
if (wsPart == null)
    wsPart = InsertWorksheet(wsName, sSheet.WorkbookPart);
person petelids    schedule 05.07.2016