2012-06-20 17 views
33

Ho generato un foglio di calcolo .xlsx in C# utilizzando l'SDK OpenXML, ma non riesco a capire come far funzionare gli stili di cella. Ho studiato i file prodotti da Excel e non riesco a capire come funziona.Stili di cella nel foglio di calcolo OpenXML (SpreadsheetML)

In questo momento, sto creando un riempimento, la creazione di un CellStyleFormat che punta al riempimento, la creazione di un CellFormat che punta l'indice della CellStyleFormat, quindi la creazione di un CellStyle che punta al CellFormat.

Ecco il codice che sto usando per generare il 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."); 
} 

Ecco il XML generato:

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> 

fogli di lavoro/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> 

L'ultima cella dell'ultima riga è dove sto cercando di aggiungere lo stile.

Tutto questo convalida correttamente quando l'eseguo attraverso la produttività strumento OpenXML SDK, ma, quando tento di aprire il file in Excel, ottengo il seguente errore:

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

Il foglio poi spettacoli, ma il riempimento non è applicato.

Qualche idea su come risolvere il problema?

+1

@Am_I_Helpful An .xslx è solo un file zip. I contenuti XML sono all'interno. – Polynomial

+0

Non ne ero a conoscenza. Grazie, ha aiutato! E, +1. –

risposta

73

Giusto, sono riuscito a capirlo, dopo un sacco di sperimentazione.

Si scopre che excel riserva gli stili 0 e 1 per le celle normali e il riempimento del motivo "Gray125" rispettivamente. La maggior parte del codice sopra può essere rimosso, in quanto abbiamo solo bisogno di un CellFormat.

codice di lavoro:

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."); 
} 

Alcuni consigli:

Usa ClosedXML se si vuole evitare questa follia.

Non posso raccomandare abbastanza il ClosedXML se stai facendo questo tipo di lavoro. L'API e il formato OpenXML sono terribilmente noiosi da utilizzare da soli, con tutti i tipi di casi non documentati. ClosedXML fa così tanto del lavoro alle gambe per te. Sono anche molto bravi a sistemare rapidamente gli errori.

+23

A proposito, se qualcuno ritorna a questo in un secondo momento, consiglio vivamente di controllare la libreria ClosedXML. È molto più facile lavorare con OpenXML direttamente. – Polynomial

+7

Ti darei più di un semplice +1 se potessi puntare a ClosedXML. Questa è una libreria seriamente facile da usare ... Grazie! – pennyrave

+0

Come posso modificare questo codice in modo tale che faccia una modifica di un foglio di calcolo esistente? –

2

Una risposta più generica, tutto ciò che ho trovato dopo il test, quindi nessuna documentazione a cui puntare.

Dopo aver impostato una raccolta CellFormats nel foglio di stile, Excel esegue una convalida più approfondita su di esso.

CellFormats non può essere vuoto, deve avere almeno uno CellFormat lì.

Una volta aggiunto uno CellFormat, Excel si lamenterà se Fills, Fonts o Borders le raccolte sono vuote.

Il primo Font viene utilizzato come predefinito per l'intera cartella di lavoro e anche intestazioni Colonna/Riga in Excel.

Excel ignorerà il primo CellFormat, quindi aggiungi solo uno vuoto.

Se avete bisogno di un Border o Fill nel formato, Excel sarà anche ignorare prima Border e Fill, così anche aggiungere quelle vuote come primo figlio in Borders e Fills.

Infine, a partire dal secondo CellFormat (s = "1") sei a posto.

Testato in Excel 2010.

Problemi correlati