我已使用 OpenXML SDK 在 C# 中生成了一个 .xlsx 电子表格,但无法弄清楚如何使单元格样式正常工作。我一直在研究 Excel 生成的文件,但不太明白它是如何完成的。
现在,我正在创建一个填充,创建一个指向填充的
CellStyleFormat
,创建一个指向 CellFormat
索引的 CellStyleFormat
,然后创建一个指向 CellStyle
的
CellFormat
.
这是我用来生成文档的代码:
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.");
}
这是生成的 XML:
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>
工作表/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>
最后一行的最后一个单元格是我尝试添加样式的位置。
当我通过 OpenXML SDK Productivity Tool 运行它时,这一切都得到了正确验证,但是,当我尝试在 Excel 中打开该文件时,出现以下错误:
修复的记录:/xl/styles.xml 部分的格式(样式)
随后会显示电子表格,但未应用填充。
知道如何解决这个问题吗?
是的,经过大量实验,我设法弄清楚了这一点。
原来Excel为普通单元格和“Gray125”图案填充分别保留了样式0和1。上面的大部分代码都可以删除,因为我们实际上只需要一个
CellFormat
。
工作代码:
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.");
}
一些建议:
如果您正在进行此类工作,我强烈推荐ClosedXML。OpenXML API 和格式本身使用起来非常乏味,并且存在各种未记录的情况。 ClosedXML 为您完成了很多琐碎的工作。他们也非常擅长快速修复错误。
一个更通用的答案,所有这些都是我在测试后发现的,所以没有文档可以指出。
在样式表中设置
CellFormats
集合后,Excel 会对其运行更深入的验证。
CellFormats
不能为空,必须至少有一个CellFormat
。
添加
CellFormat
后,如果 Fills
、Fonts
或 Borders
集合为空,Excel 将发出警告。
First
Font
用作整个工作簿以及 Excel 中的列/行标题的默认值。
Excel会忽略第一个
CellFormat
,所以只需添加一个空的即可。
如果您的格式中需要
Border
或 Fill
,Excel 也会忽略第一个 Border
和 Fill
,因此还要添加空的作为 Borders
和 Fills
中的第一个子元素。
最后,从第二个开始
CellFormat
(s = "1"
)你就可以开始了。
在 Excel 2010 中测试。
ClosedXML 确实使这变得更加容易。这是一些 ClosedXML 样式代码:
//workbook styling
workbook.Style.Border.BottomBorder = XLBorderStyleValues.Thick;
//worksheet styling
worksheet.Style.Font.Strikethrough = true;
//range styling
ws.Range("A1:C3").Style.Font.Bold = true;
//cell styling
worksheet.Cell("A1").Style.Fill.BackgroundColor = XLColor.Gray;
SlapKit 是 ClosedXML 的升级版,支持开箱即用的更高级的单元格样式场景。由于它基于 ClosedXML,因此在通用功能上具有完全相同的 API。不过,它可以做一些很酷的额外事情:
//------You can use predefined styles (not exists in ClosedXML)--------
//workbook styling
workbook.Style = workbook.PredefinedStyles[XLBuiltInStyleType.Accent1];
//worksheet styling
worksheet.Style = workbook.PredefinedStyles[XLBuiltInStyleType.Accent2];
//range styling
ws.Range("A1:C3").Style = workbook.PredefinedStyles[XLBuiltInStyleType.Good];
//cell styling
worksheet.Cell("B1").Style = workbook.PredefinedStyles[XLBuiltInStyleType.Bad];
//You can customize the custom styles according to your preferences.
workbook.PredefinedStyles[XLBuiltInStyleType.Bad]
.Fill.SetBackgroundColor(XLColor.Red)
.Font.SetFontColor(XLColor.White)
.Font.SetBold();
//You can create your custom styles.
IXLCustomPredefinedStyle styleGood = workbook.PredefinedStyles["my style"];
styleGood
.Fill.SetBackgroundColor(XLColor.Green)
.Font.SetFontColor(XLColor.White)
.Font.SetBold();
//use the custom-named style
worksheet.Cell("B1").Style = styleGood;
他们的文档中有更多内容(顺便说一句,基于 ClosedXML,他们也记录了所有 ClosedXML 功能,这似乎很好地增强了 ClosedXML 文档)