2009-02-09 27 views
30

Quiero actualizar una celda en una hoja de cálculo que utiliza un gráfico, utilizando Open XML SDK 2.0 (CTP). Todas las muestras de código que he encontrado insertan nuevas celdas. Estoy luchando con recuperar la hoja de trabajo correcta.Open XML SDK 2.0: ¿cómo actualizar una celda en una hoja de cálculo?

public static void InsertText(string docName, string text, uint rowIndex, 
    string columnName) 
{ 
    // Open the document for editing. 
    using (SpreadsheetDocument spreadSheet = 
    SpreadsheetDocument.Open(docName, true)) 
    { 
    Workbook workBook = spreadSheet.WorkbookPart.Workbook; 

    WorksheetPart worksheetPart = workBook.WorkbookPart. 
     WorksheetParts.First(); 

    SheetData sheetData = worksheetPart.Worksheet. 
     GetFirstChild<SheetData>(); 

    // If the worksheet does not contain a row with the specified 
    // row index, insert one. 
    Row row; 

    if (sheetData.Elements<Row>().Where(
     r => r.RowIndex == rowIndex).Count() != 0) 
     // At this point I am expecting a match for a row that exists 
     // in sheet1 but I am not getting one 

Cuando navego por el árbol en Visual Studio, veo tres hojas, pero ninguna de ellas tiene hijos. ¿Qué me estoy perdiendo?

+1

Estoy progresando. Una cosa que me dejó perplejo fue que todos los ejemplos suponen que WorksheetParts.First() obtiene la hoja de trabajo "Hoja 1". Este no es el caso, más bien devuelve lo que sea que sea el primer elemento en workbook.xml. Publicaré el código cuando lo tenga funcionando. – cdonner

+0

intente WorksheetParts .Primer(). Obtendrá el primer elemento que es tipo de hoja. –

Respuesta

60

Aquí está el código de trabajo. Este es un prototipo. Para un número mayor de cambios, uno puede abrir el documento solo una vez. Además, hay algunos elementos codificados como el nombre de la hoja y el tipo de celda que deberían parametrizarse antes de que esto pueda denominarse listo para producción. http://openxmldeveloper.org/forums/4005/ShowThread.aspx fue muy útil.

using System; 
using System.Collections.Generic; 
using System.Data; 
using System.Linq; 
using System.Text; 
using DocumentFormat.OpenXml; 
using DocumentFormat.OpenXml.Packaging; 
using DocumentFormat.OpenXml.Spreadsheet; 
using System.Xml; 
using System.IO; 
using System.Diagnostics; 

namespace OpenXMLWindowsApp 
{ 
    public class OpenXMLWindowsApp 
    { 
     public void UpdateSheet() 
     { 
      UpdateCell("Chart.xlsx", "20", 2, "B"); 
      UpdateCell("Chart.xlsx", "80", 3, "B"); 
      UpdateCell("Chart.xlsx", "80", 2, "C"); 
      UpdateCell("Chart.xlsx", "20", 3, "C"); 

      ProcessStartInfo startInfo = new ProcessStartInfo("Chart.xlsx"); 
      startInfo.WindowStyle = ProcessWindowStyle.Normal; 
      Process.Start(startInfo); 
     } 

     public static void UpdateCell(string docName, string text, 
      uint rowIndex, string columnName) 
     { 
      // Open the document for editing. 
      using (SpreadsheetDocument spreadSheet = 
        SpreadsheetDocument.Open(docName, true)) 
      { 
       WorksheetPart worksheetPart = 
         GetWorksheetPartByName(spreadSheet, "Sheet1"); 

       if (worksheetPart != null) 
       { 
        Cell cell = GetCell(worksheetPart.Worksheet, 
              columnName, rowIndex); 

        cell.CellValue = new CellValue(text); 
        cell.DataType = 
         new EnumValue<CellValues>(CellValues.Number); 

        // Save the worksheet. 
        worksheetPart.Worksheet.Save(); 
       } 
      } 

     } 

     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) 
      { 
       // The specified worksheet does not exist. 

       return null; 
      } 

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

     } 

     // Given a worksheet, a column name, and a row index, 
     // gets the cell at the specified column and 
     private static Cell GetCell(Worksheet worksheet, 
        string columnName, uint rowIndex) 
     { 
      Row row = GetRow(worksheet, rowIndex); 

      if (row == null) 
       return null; 

      return row.Elements<Cell>().Where(c => string.Compare 
        (c.CellReference.Value, columnName + 
        rowIndex, true) == 0).First(); 
     } 


     // Given a worksheet and a row index, return the row. 
     private static Row GetRow(Worksheet worksheet, uint rowIndex) 
     { 
      return worksheet.GetFirstChild<SheetData>(). 
       Elements<Row>().Where(r => r.RowIndex == rowIndex).First(); 
     } 
    } 
} 
+1

Gracias por el código de trabajo ... Pude adaptarlo fácilmente a mi situación. Tiene razón, la mayoría de los ejemplos crean nuevos libros de trabajo/hojas de trabajo y celdas. Solo quiero actualizar algunas celdas existentes. –

+2

Cuando uso su (excelente) código de muestra obtengo el resultado deseado, pero cuando abro el archivo XLSX en Excel 2010 (no lo he probado con 2007) recibo una advertencia de que algo no está bien (Excel encontró contenido indecible) y ofrece arreglarlo. Me pregunto si eso está relacionado con que este código no inserte primero el texto en la tabla de cadenas. ¿Cómo modificaría este código para eliminar la advertencia? –

+0

No puedo ofrecer ninguna ayuda con Office 2010, Philipp. No he visto esto en años. – cdonner

6

He estado trabajando con Excel y encontraron esta biblioteca de ayuda a ser de gran ayuda (he creado mis propios ayudantes por palabra, habría ahorrado al menos 2 semanas si yo era consciente de esto): http://simpleooxml.codeplex.com/

Esto es lo que se necesita para actualizar celular (writer.PasteText (...)):

MemoryStream stream = SpreadsheetReader.Create(); 
SpreadsheetDocument doc = SpreadsheetDocument.Open(stream, true); 
WorksheetPart worksheetPart = SpreadsheetReader.GetWorksheetPartByName(doc, "Sheet1"); 
WorksheetWriter writer = new WorksheetWriter(doc, worksheetPart); 

writer.PasteText("B2", "Hello World"); 

//Save to the memory stream 
SpreadsheetWriter.Save(doc); 

byte[] result = stream.ToArray(); 
FileStream file = new FileStream(@"D:\x1.xlsx", FileMode.Create); 
file.Write(result, 0, result.Length); 
file.Close(); 
+0

Microsoft hace todo este trabajo para crear métodos realmente útiles como 'PasteText' y luego no crea ni siquiera un artículo simple sobre su uso. Al menos tenemos MSDN pero aún estamos en ooxml 2.5 ¡debería haber más en estas gemas! =) – Coops

+0

¡Gracias, esto hace que mi vida sea más fácil! – GeorgDangl

3

el código publicado por @CDonner lanza algunas excepciones, he añadido una parte del código que se encargará del código, que arroja una excepción, aquí yo t es

using System; 
using System.Collections.Generic; 
using System.Data; 
using System.Linq; 
using System.Text; 
using DocumentFormat.OpenXml; 
using DocumentFormat.OpenXml.Packaging; 
using DocumentFormat.OpenXml.Spreadsheet; 
using System.Xml; 
using System.IO; 
using System.Diagnostics; 

namespace Application.Model{ 
public class TempCode 
{ 
    public TempCode() 
    { 
     UpdateCell("E:/Visual Studio Code/Book1.xlsx", "120", 1, "A"); 
     UpdateCell("E:/Visual Studio Code/Book1.xlsx", "220", 2, "B"); 
     UpdateCell("E:/Visual Studio Code/Book1.xlsx", "320", 3, "C"); 
     UpdateCell("E:/Visual Studio Code/Book1.xlsx", "420", 4, "D"); 
     UpdateCell("E:/Visual Studio Code/Book1.xlsx", "520", 5, "E"); 

     ProcessStartInfo startInfo = new ProcessStartInfo("E:/Visual Studio Code/Book1.xlsx"); 
     startInfo.WindowStyle = ProcessWindowStyle.Normal; 
     Process.Start(startInfo); 



    } 

    public static void UpdateCell(string docName, string text,uint rowIndex, string columnName){ 
     // Open the document for editing. 
     using (SpreadsheetDocument spreadSheet = SpreadsheetDocument.Open(docName, true)) 
     { 
      WorksheetPart worksheetPart = GetWorksheetPartByName(spreadSheet, "Sheet2"); 
      if (worksheetPart != null) 
      { 
       Cell cell = GetCell(worksheetPart.Worksheet, columnName, rowIndex); 
       cell.CellValue = new CellValue(text); 
       cell.DataType = new EnumValue<CellValues>(CellValues.Number); 
       // Save the worksheet. 
       worksheetPart.Worksheet.Save(); 
      } 
     } 

    } 

    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 Cell GetCell(Worksheet worksheet, string columnName, uint rowIndex) 
    { 
     Row row; 
     string cellReference = columnName + rowIndex; 
     if (worksheet.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0) 
      row = worksheet.GetFirstChild<SheetData>().Elements<Row>().Where(r => r.RowIndex == rowIndex).FirstOrDefault(); 
     else{ 
      row = new Row() { RowIndex = rowIndex }; 
      worksheet.Append(row); 
     } 

     if (row == null) 
      return null; 

     if (row.Elements<Cell>().Where(c => c.CellReference.Value == cellReference).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, 
       StyleIndex = (UInt32Value)1U 

      }; 
      row.InsertBefore(newCell, refCell); 
      worksheet.Save(); 
      return newCell; 
     } 
    } 
} 

}

+0

check string.Compare (cell.CellReference.Value, cellReference, true)> 0) ya que da "AA9" <"B9", lo que es incorrecto en términos de excel column precedence – avestnik

1

Ésta es SDK 2.5, aunque, sin embargo, era un código muy útil encontrar aquí: http://fczaja.blogspot.dk/2013/05/how-to-read-and-write-excel-cells-with.html

tenía que hacer una ligera modificación de los valores de texto para agregarlos a la SharedStringTablePart.

// Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text 
// and inserts it into the SharedStringTablePart. If the item already exists, returns its index. 
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart) 
{ 
    // If the part does not contain a SharedStringTable, create one. 
    if (shareStringPart.SharedStringTable == null) 
    { 
     shareStringPart.SharedStringTable = new SharedStringTable(); 
    } 

    int i = 0; 

    // Iterate through all the items in the SharedStringTable. If the text already exists, return its index. 
    foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>()) 
    { 
     if (item.InnerText == text) 
     { 
      return i; 
     } 

     i++; 
    } 

    // The text does not exist in the part. Create the SharedStringItem and return its index. 
    shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(text))); 
    shareStringPart.SharedStringTable.Save(); 

    return i; 
} 

Y utilizar de esta manera:

SharedStringTablePart shareStringPart = GetSharedStringTablePart(excelDoc); 

// Insert the text into the SharedStringTablePart. 
int index = InsertSharedStringItem(cellValue, shareStringPart); 

// Set the value of cell A1. 
cell.CellValue = new CellValue(index.ToString()); 
cell.DataType = new EnumValue<CellValues>(CellValues.SharedString); 
0

he hecho algunos cambios en el código @AZ.

En primer lugar, en la función GetCell hay un problema al seleccionar la fila actual. Sólo cambia:

if (worksheet.GetFirstChild<SheetData>().Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0) 

en lugar de:

if (worksheet.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0) 

Y en la sección:

if (string.Compare(cell.CellReference.Value, cellReference, true) > 0) 

Si está utilizando columnas por encima de Z columna (como la columna AA, por ejemplo) no funcionar correctamente. Para algunos esto, estoy usando los números de columna para determinar dónde insertar la Celda.

Para ello, he creado un ColumnIndex función, con convert las letras de las columnas de números:

private static int ColumnIndex(string reference) 
    { 
     int ci = 0; 
     reference = reference.ToUpper(); 
     for (int ix = 0; ix < reference.Length && reference[ix] >= 'A'; ix++) 
      ci = (ci * 26) + ((int)reference[ix] - 64); 
     return ci; 
    } 

Así que cambió la cadena de función de comparación para esto:

string columnNew = new String(cellReference.Where(c => c != '-' && (c < '0' || c > '9')).ToArray()); 
      foreach (Cell cell in row.Elements<Cell>()) 
      { 
       string columnBase = new String(cell.CellReference.Value.Where(c => c != '-' && (c < '0' || c > '9')).ToArray()); 

       if (ColumnIndex(columnBase) > ColumnIndex(columnNew)) 
       { 
        refCell = cell; 
        break; 
       } 
      } 

Saludos.

Cuestiones relacionadas