2012-06-20 29 views
33

He generado una hoja de cálculo .xlsx en C# utilizando OpenXML SDK, pero no puedo encontrar la manera de que funcionen los estilos de celda. He estado estudiando archivos producidos por Excel, y no puedo entender cómo se hace.Estilos de celda en hoja de cálculo OpenXML (SpreadsheetML)

En este momento, estoy creando un relleno, creando un CellStyleFormat que apunta en el relleno, creando un CellFormat que apunta a que el índice de la CellStyleFormat, a continuación, crear un CellStyle que apunta a la CellFormat.

Aquí está el código que estoy usando para generar el documento:

Console.WriteLine("Creating document"); 
using (var spreadsheet = SpreadsheetDocument.Create("output.xlsx", SpreadsheetDocumentType.Workbook)) 
{ 
    Console.WriteLine("Creating workbook"); 
    spreadsheet.AddWorkbookPart(); 
    spreadsheet.WorkbookPart.Workbook = new Workbook(); 
    Console.WriteLine("Creating worksheet"); 
    var wsPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>(); 
    wsPart.Worksheet = new Worksheet(); 

    var stylesPart = spreadsheet.WorkbookPart.AddNewPart<WorkbookStylesPart>(); 
    stylesPart.Stylesheet = new Stylesheet(); 
    stylesPart.Stylesheet.Fills = new Fills(); 

    // create a solid red fill 
    var solidRed = new PatternFill() { PatternType = PatternValues.Solid }; 
    solidRed.AppendChild(new BackgroundColor { Rgb = HexBinaryValue.FromString("FF00FF00") }); 

    stylesPart.Stylesheet.Fills.AppendChild(new Fill { PatternFill = new PatternFill() { PatternType = PatternValues.None } }); 
    stylesPart.Stylesheet.Fills.AppendChild(new Fill { PatternFill = solidRed }); 
    stylesPart.Stylesheet.CellStyleFormats = new CellStyleFormats(); 
    stylesPart.Stylesheet.CellStyleFormats.AppendChild(new CellFormat { FillId = 0, ApplyFill = false }); 
    stylesPart.Stylesheet.CellStyleFormats.AppendChild(new CellFormat { FillId = 1, ApplyFill = true }); 
    stylesPart.Stylesheet.CellFormats = new CellFormats(); 
    stylesPart.Stylesheet.CellFormats.AppendChild(new CellFormat { FormatId = 0 }); 
    stylesPart.Stylesheet.CellFormats.AppendChild(new CellFormat { FormatId = 1 }); 
    stylesPart.Stylesheet.CellStyles = new CellStyles(); 
    stylesPart.Stylesheet.CellStyles.AppendChild(new CellStyle { Name = "None", FormatId = 0 }); 
    stylesPart.Stylesheet.CellStyles.AppendChild(new CellStyle { Name = "Solid Red", FormatId = 1 }); 

    stylesPart.Stylesheet.Save(); 

    Console.WriteLine("Creating sheet data"); 
    var sheetData = wsPart.Worksheet.AppendChild(new SheetData()); 

    Console.WriteLine("Adding rows/cells..."); 

    var row = sheetData.AppendChild(new Row()); 
    row.AppendChild(new Cell() { CellValue = new CellValue("This"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("is"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("a"),  DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("test."), DataType = CellValues.String }); 

    sheetData.AppendChild(new Row()); 

    row = sheetData.AppendChild(new Row()); 
    row.AppendChild(new Cell() { CellValue = new CellValue("Value:"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("123"),  DataType = CellValues.Number }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("Formula:"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellFormula = new CellFormula("B3"), StyleIndex = 1 }); // 

    Console.WriteLine("Saving worksheet"); 
    wsPart.Worksheet.Save(); 

    Console.WriteLine("Creating sheet list"); 
    var sheets = spreadsheet.WorkbookPart.Workbook.AppendChild(new Sheets()); 
    sheets.AppendChild(new Sheet() { Id = spreadsheet.WorkbookPart.GetIdOfPart(wsPart), SheetId = 1, Name = "Test" }); 

    Console.WriteLine("Saving workbook"); 
    spreadsheet.WorkbookPart.Workbook.Save(); 

    Console.WriteLine("Done."); 
} 

aquí está el XML generado:

workbook.xml

<?xml version="1.0" encoding="utf-8"?> 
<x:workbook xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> 
    <x:sheets> 
    <x:sheet name="Test" sheetId="1" r:id="Rbad86b8c80844a16" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" /> 
    </x:sheets> 
</x:workbook> 

styles.xml

<?xml version="1.0" encoding="utf-8"?> 
<x:styleSheet xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> 
    <x:fills> 
    <x:fill> 
     <x:patternFill patternType="none" /> 
    </x:fill> 
    <x:fill> 
     <x:patternFill patternType="solid"> 
     <x:bgColor rgb="FF00FF00" /> 
     </x:patternFill> 
    </x:fill> 
    </x:fills> 
    <x:cellStyleXfs> 
    <x:xf fillId="0" applyFill="0" /> 
    <x:xf fillId="1" applyFill="1" /> 
    </x:cellStyleXfs> 
    <x:cellXfs> 
    <x:xf xfId="0" /> 
    <x:xf xfId="1" /> 
    </x:cellXfs> 
    <x:cellStyles> 
    <x:cellStyle name="None" xfId="0" /> 
    <x:cellStyle name="Solid Red" xfId="1" /> 
    </x:cellStyles> 
</x:styleSheet> 

hojas de trabajo/sheet.xml

<?xml version="1.0" encoding="utf-8"?> 
<x:worksheet xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> 
    <x:sheetData> 
    <x:row> 
     <x:c t="str"><x:v>This</x:v></x:c> 
     <x:c t="str"><x:v>is</x:v></x:c> 
     <x:c t="str"><x:v>a</x:v></x:c> 
     <x:c t="str"><x:v>test.</x:v></x:c> 
    </x:row> 
    <x:row /> 
    <x:row> 
     <x:c t="str"><x:v>Value:</x:v></x:c> 
     <x:c t="n"><x:v>123</x:v></x:c> 
     <x:c t="str"><x:v>Formula:</x:v></x:c> 
     <x:c s="1"><x:f>B3</x:f></x:c> 
    </x:row> 
    </x:sheetData> 
</x:worksheet> 

La última celda de la última fila es donde yo estoy tratando de añadir el estilo.

Todo esto valida correctamente cuando lo ejecuto a través de la herramienta de productividad OpenXML SDK, pero, cuando intento abrir el archivo en Excel, me sale el siguiente error:

Repaired Records: Format from /xl/styles.xml part (Styles)

La hoja de cálculo a continuación, espectáculos, pero el relleno no se aplica.

¿Alguna idea de cómo solucionar esto?

+1

@Am_I_Helpful An .xslx es solo un archivo zip. Los contenidos XML están adentro. – Polynomial

+0

No estaba enterado de eso. Gracias, ¡ayudo! Y, +1. –

Respuesta

73

Correcto, logré resolver esto, después de mucha experimentación.

Resulta que Excel reserva los estilos 0 y 1 para las celdas normales y el relleno de patrón "Gray125", respectivamente. La mayoría del código anterior se puede eliminar, ya que solo necesitamos un CellFormat realmente.

código de trabajo:

Console.WriteLine("Creating document"); 
using (var spreadsheet = SpreadsheetDocument.Create("output.xlsx", SpreadsheetDocumentType.Workbook)) 
{ 
    Console.WriteLine("Creating workbook"); 
    spreadsheet.AddWorkbookPart(); 
    spreadsheet.WorkbookPart.Workbook = new Workbook(); 
    Console.WriteLine("Creating worksheet"); 
    var wsPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>(); 
    wsPart.Worksheet = new Worksheet(); 

    var stylesPart = spreadsheet.WorkbookPart.AddNewPart<WorkbookStylesPart>(); 
    stylesPart.Stylesheet = new Stylesheet(); 

    Console.WriteLine("Creating styles"); 

    // blank font list 
    stylesPart.Stylesheet.Fonts = new Fonts(); 
    stylesPart.Stylesheet.Fonts.Count = 1; 
    stylesPart.Stylesheet.Fonts.AppendChild(new Font()); 

    // create fills 
    stylesPart.Stylesheet.Fills = new Fills(); 

    // create a solid red fill 
    var solidRed = new PatternFill() { PatternType = PatternValues.Solid }; 
    solidRed.ForegroundColor = new ForegroundColor { Rgb = HexBinaryValue.FromString("FFFF0000") }; // red fill 
    solidRed.BackgroundColor = new BackgroundColor { Indexed = 64 }; 

    stylesPart.Stylesheet.Fills.AppendChild(new Fill { PatternFill = new PatternFill { PatternType = PatternValues.None } }); // required, reserved by Excel 
    stylesPart.Stylesheet.Fills.AppendChild(new Fill { PatternFill = new PatternFill { PatternType = PatternValues.Gray125 } }); // required, reserved by Excel 
    stylesPart.Stylesheet.Fills.AppendChild(new Fill { PatternFill = solidRed }); 
    stylesPart.Stylesheet.Fills.Count = 3; 

    // blank border list 
    stylesPart.Stylesheet.Borders = new Borders(); 
    stylesPart.Stylesheet.Borders.Count = 1; 
    stylesPart.Stylesheet.Borders.AppendChild(new Border()); 

    // blank cell format list 
    stylesPart.Stylesheet.CellStyleFormats = new CellStyleFormats(); 
    stylesPart.Stylesheet.CellStyleFormats.Count = 1; 
    stylesPart.Stylesheet.CellStyleFormats.AppendChild(new CellFormat()); 

    // cell format list 
    stylesPart.Stylesheet.CellFormats = new CellFormats(); 
    // empty one for index 0, seems to be required 
    stylesPart.Stylesheet.CellFormats.AppendChild(new CellFormat()); 
    // cell format references style format 0, font 0, border 0, fill 2 and applies the fill 
    stylesPart.Stylesheet.CellFormats.AppendChild(new CellFormat { FormatId = 0, FontId = 0, BorderId = 0, FillId = 2, ApplyFill = true }).AppendChild(new Alignment { Horizontal = HorizontalAlignmentValues.Center }); 
    stylesPart.Stylesheet.CellFormats.Count = 2; 

    stylesPart.Stylesheet.Save(); 

    Console.WriteLine("Creating sheet data"); 
    var sheetData = wsPart.Worksheet.AppendChild(new SheetData()); 

    Console.WriteLine("Adding rows/cells..."); 

    var row = sheetData.AppendChild(new Row()); 
    row.AppendChild(new Cell() { CellValue = new CellValue("This"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("is"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("a"),  DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("test."), DataType = CellValues.String }); 

    sheetData.AppendChild(new Row()); 

    row = sheetData.AppendChild(new Row()); 
    row.AppendChild(new Cell() { CellValue = new CellValue("Value:"), DataType = CellValues.String }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("123"),  DataType = CellValues.Number }); 
    row.AppendChild(new Cell() { CellValue = new CellValue("Formula:"), DataType = CellValues.String }); 
    // style index = 1, i.e. point at our fill format 
    row.AppendChild(new Cell() { CellFormula = new CellFormula("B3"), DataType = CellValues.Number, StyleIndex = 1 }); 

    Console.WriteLine("Saving worksheet"); 
    wsPart.Worksheet.Save(); 

    Console.WriteLine("Creating sheet list"); 
    var sheets = spreadsheet.WorkbookPart.Workbook.AppendChild(new Sheets()); 
    sheets.AppendChild(new Sheet() { Id = spreadsheet.WorkbookPart.GetIdOfPart(wsPart), SheetId = 1, Name = "Test" }); 

    Console.WriteLine("Saving workbook"); 
    spreadsheet.WorkbookPart.Workbook.Save(); 

    Console.WriteLine("Done."); 
} 

Algunos consejos:

Uso ClosedXML si se quiere evitar esta locura.

No puedo recomendar ClosedXML lo suficiente como para hacer este tipo de trabajo. La API y el formato de OpenXML es terriblemente tedioso para trabajar por sí solo, con todo tipo de casos no documentados. ClosedXML hace mucho del trabajo de la pierna para ti. También son geniales para solucionar los errores rápidamente.

+23

Por cierto, si alguien vuelve a esto en una fecha posterior, le recomiendo que revise la lib de ClosedXML. Es mucho más fácil trabajar con OpenXML directamente. – Polynomial

+7

Te daría más que solo +1 si pudiera para apuntar hacia ClosedXML. Esa es una biblioteca seriamente fácil de usar ... ¡Gracias! – pennyrave

+0

¿Cómo modifico este código para que haga una modificación de una hoja de cálculo existente? –

2

Una respuesta más genérica, todo esto encontré después de la prueba, por lo que no hay documentación que señalar.

Una vez que establece una colección CellFormats en la hoja de estilos, Excel ejecuta una validación más profunda en ella.

CellFormats no puede estar vacío, debe tener al menos un CellFormat allí.

Una vez que se agrega una CellFormat, Excel se quejará si Fills, Fonts o Borders colecciones están vacíos.

Primero se usa Font como valor predeterminado para todo el libro y también para los encabezados de columna/fila en Excel.

Excel ignorará primero CellFormat, así que simplemente agregue uno vacío.

Si necesita un Border o Fill en su formato, Excel también ignorará primera Border y Fill, por lo que también se suman las vacías como primer hijo en Borders y Fills.

Finalmente, comenzando en el segundo CellFormat (s = "1") está listo para continuar.

Probado en Excel 2010.

Cuestiones relacionadas