简体   繁体   English

C#Open XML SDK 2.0 Excel电子表格-从字符串数组加载单元格范围

[英]C# Open XML SDK 2.0 Excel spreadsheet - load range of cells from string array

In a .Net windows desktop application, I am able to import a string array of data into a range of cells located in Excel spreadsheet. 在.Net Windows桌面应用程序中,我能够将字符串数据数组导入Excel电子表格中的一系列单元格中。 The C# code is as follows: C#代码如下:

using Excel = Microsoft.Office.Interop.Excel;

// Create Application, Workbook, and Worksheet
xlApp = new Microsoft.Office.Interop.Excel.Application();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWs = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

// Move data from temp array into Excel spreadsheet.
Excel.Range c1 = (Excel.Range)xlWs.Cells[startRowNum, 1];
Excel.Range c2 = (Excel.Range)xlWs.Cells[startRowNum + myTable.Rows.Count - 1,   Columns.Count];
Excel.Range range = xlWs.get_Range(c1, c2);
range.Value = tempArray;

I am trying to duplicate this process in an ASP.Net webpage. 我正在尝试在ASP.Net网页中重复此过程。 Using Visual Studio 2010 and C#. 使用Visual Studio 2010和C#。 How can I do this same import of a string array into an Open XML SDK 2.0 Excel spreadsheet range of cells? 如何将同样的字符串数组导入到Open XML SDK 2.0 Excel电子表格范围的单元格中?

As answered, a library might be easier to use. 答案是,库可能更易于使用。 An alternative library choice is SpreadsheetLight . 另一种库选择是SpreadsheetLight Here's how the code might look like: 代码如下所示:

SLDocument sl = new SLDocument("YourFile.xlsx");
// row 1 column 1
sl.SetCellValue(1, 1, "String1");
// row 1 column 2
sl.SetCellValue(1, 2, "String2");
sl.SaveAs("AnotherFile.xlsx");

You don't have to worry about which order you set the cell values. 您不必担心设置单元格值的顺序。 Internally, SpreadsheetLight runs on Open XML SDK. 在内部,SpreadsheetLight在Open XML SDK上运行。 Disclaimer: I wrote SpreadsheetLight. 免责声明:我写了SpreadsheetLight。

Using the OpenXML SDK directly instead of going through Excel's automation model is much more complex and error-prone. 直接使用OpenXML SDK而不是通过Excel的自动化模型,要复杂得多且容易出错。 Therefore I recommend using a library for this; 因此,我建议为此使用一个库。 espcially if your task gets more complex (eg http://excelpackage.codeplex.com/ ). 特别是如果您的任务变得更加复杂(例如, http : //excelpackage.codeplex.com/ )。 Edit : An example on doing something similar using ExcelPackage can be found here: http://excelpackage.codeplex.com/wikipage?title=Using%20a%20template%20to%20create%20an%20Excel%20spreadsheet Although I have no idea on what performance to expect compared to using the raw SDK, I would guess that ExcelPackage is using the SDK internally anyway and therefore it might produce some overhead. 编辑 :可以在此处找到有关使用ExcelPackage进行类似操作的示例: http ://excelpackage.codeplex.com/wikipage?title=使用%20a%20template%20to%20create%20an%20Excel%20spreadsheet,尽管我对此一无所知与使用原始SDK相比预期的性能如何,我想ExcelPackage无论如何都在内部使用SDK,因此可能会产生一些开销。 Probably only a measurement for you concrete scenario can provide a definite answer here. 在这里,可能只有针对您的具体方案的度量才能提供明确的答案。

If you want to stick to the SDK though here is an example for inserting strings into an Excel workbook: 如果您想坚持使用SDK,请参考以下示例,将字符串插入Excel工作簿:

string filePath = "workbook.xlsx";
string sheetName = "Sheet1";
uint startRow = 9;
string columnName = "C";
string[] data = new string[] { "A", "B", "C" };

using (var spreadsheetDocument = SpreadsheetDocument.Open(filePath, true))
{
    // Find the Id of the worksheet in question
    var sheet = spreadsheetDocument.WorkbookPart.Workbook
        .Sheets.Elements<Sheet>()
        .Where(s => s.Name == sheetName).First();
    var sheetReferenceId = sheet.Id;

    // Map the Id to the worksheet part
    WorksheetPart worksheetPart = (WorksheetPart)spreadsheetDocument.WorkbookPart.GetPartById(sheetReferenceId);
    var sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

    // Inset the data at the given location
    for (uint i = 0; i < data.Length; i++)
    {
        uint rowNumber = startRow + i;

        // Find the XML entry for row i
        var row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowNumber).FirstOrDefault();
        if (row == null)
        {
            // Row does not exist yet, create it
            row = new Row();
            row.RowIndex = rowNumber;

            // Insert the row at its correct sequential position
            Row rowAfter = null;
            foreach (Row otherRow in sheetData.Elements<Row>())
            {
                if (otherRow.RowIndex > row.RowIndex)
                {
                    rowAfter = otherRow;
                    break;
                }
            }

            if (rowAfter == null)
                // New row is the last row in the sheet
                sheetData.Append(row);
            else
                sheetData.InsertBefore(row, rowAfter);
        }

        // CellReferences in OpenXML are "normal" Excel cell references, e.g. D15
        string cellReference = columnName + rowNumber.ToString();

        // Find cell in row
        var cell = row.Elements<Cell>()
            .Where(c => c.CellReference == cellReference)
            .FirstOrDefault();
        if (cell == null)
        {
            // Cell does not exist yet, create it
            cell = new Cell();
            cell.CellReference = new StringValue(cellReference);

            // The cell must be in the correct position (e.g. column B after A)
            // Note: AA must be after Z, so a normal string compare is not sufficient
            Cell cellAfter = null;
            foreach (Cell otherCell in row.Elements<Cell>())
            {
                // This is ugly, but somehow the row number must be stripped from the
                // cell reference for comparison
                string otherCellColumn = otherCell.CellReference.Value;
                otherCellColumn = otherCellColumn.Remove(otherCellColumn.Length - rowNumber.ToString().Length);

                // Now compare first to length and then alphabetically
                if (otherCellColumn.Length > columnName.Length ||
                    string.Compare(otherCellColumn, columnName, true) > 0)
                {
                    cellAfter = otherCell;
                    break;
                }
            }

            if (cellAfter == null)
                // New cell is last cell in row
                row.Append(cell);
            else
                row.InsertBefore(cell, cellAfter);
        }

        // Note: This is the most simple approach.
        //       Normally Excel itself will store the string as a SharedString,
        //       which is more difficult to implement. The only drawback of using
        //       this approach though, is that the cell might have been the only
        //       reference to its shared string value, which is not deleted from the
        //       list here.
        cell.DataType = CellValues.String;
        cell.CellValue = new CellValue(data[i]);
    }
}

Please note that this example is not perfect as it does not consider complex scenarios at all (eg styling, print margin, merged cells...). 请注意,此示例并不完美,因为它根本没有考虑复杂的情况(例如,样式,打印页边距,合并的单元格...)。 For production use you might want to extract certain functionality into a method (for example inserting a row or cell at the correct position) or even into a class all by itself (eg the part about comparing cell references in correct order). 对于生产用途,您可能希望将某些功能提取到方法中(例如,将行或单元格插入正确的位置),或者甚至全部提取到类中(例如,按正确顺序比较单元格引用的部分)。

Edit : The performance of using the SDK instead of going through the Automation Model is MUCH better (this is probably the second huge advantage of the SDK, the first being that you do not neeed to have Excel installed). 编辑 :使用SDK而不是通过自动化模型的性能要好得多(这可能是SDK的第二个巨大优势,第一个是您不需要安装Excel。) If you still see performance bottlenecks here are some ideas for improvement: 如果您仍然看到性能瓶颈,请参考以下改进建议:

  1. Make sure that the cells are already created in the template document (eg insert a 0 into each of the using Excel). 确保在模板文档中已经创建了单元格(例如,在每个使用的Excel中插入0)。 This saves you the effort to create new cells. 这省去了创建新单元的工作。
  2. Make sure that none of the cells already exists in the template and then create all of the cells in sequence (saving you the effort to find the correct insert position at least for the row). 确保模板中不存在任何单元格,然后按顺序创建所有单元格(省去了至少找到该行的正确插入位置的工作)。
  3. Manipulate the XML directly, which is more difficult and requires even more knowledge of the inner workings of OpenXML. 直接操作XML,这比较困难,并且需要更多有关OpenXML内部工作的知识。 This is probably acceptable for rather uniform documents. 对于相当统一的文档,这可能是可以接受的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM