使用 Open XML 创建基本的 Excel 工作簿
本文介绍了如何使用 Open XML 创建一个基本的 Excel 工作簿。
引言
本文的目的是描述如何仅仅使用 DocumentFormat.OpenXml.dll(命名空间为 DocumentFormat.OpenXml
)来创建一个 Excel 工作簿。
为了测试示例,您必须从下载中心下载并安装 Open XML SDK 2.0。
此演示适用于 C# 和 Visual Basic。
使用 Open XML SDK 创建 Excel 文件时,无需安装 Microsoft Office,因此此库无需安装 Excel 即可使用。但是,演示项目在创建 xlsx 文档后会启动它,因此为了查看创建的文件,应安装一个可以打开 xlsx 文件的程序来查看该文件。
关于 Open XML 的一点解释
Open XML 是一种用于 Office 文件的开放式标准化格式。使用的标准是
这些标准定义了 Office 文件的结构和元素。Office 文件(如 Excel 的 xlsx)本身是包含特定目录和文件结构的压缩文件。包含电子表格内容的文件的 xml 文件,与其他 xml 文件一样。
就 Excel 文件而言,一个基本的 xlsx 文件例如包含以下文件
/[Content_Types].xml
:定义电子表格的部件和扩展名/xl/workbook.xml
:例如工作簿中包含的工作表/xl/styles.xml
:工作表中使用的样式/xl/sharedStrings.xml
:单元格之间共享的字符串/xl/worksheets/sheet1.xml...
:实际的工作表
实际的包包含更多文件,但在此文章的范围内,这些是最有趣的。所包含的演示项目展示了为生成和修改这些文件而进行的一些操作。
关于项目
项目本身非常简单。它由两个类组成:MainWindow
类和一个静态 Excel
类。Excel 类负责对 Excel 电子表格进行的所有操作。它有点像一个工具类,但请注意它远未完成。它旨在用作学习工具或实际实现的种子。
在编写此演示时,我发现 Excel 对 XML 文件非常挑剔。一个令人惊讶的是,XML 文件中元素的顺序非常重要。例如,样式表中的元素,如字体、填充、边框、CellStyleXfs、CellXfs 等必须按特定顺序排列。否则,文档将被解释为已损坏。
另一个观察是,元素索引经常被使用(例如共享字符串的索引)。但是,库中没有提供获取索引的支持,因此必须循环集合以计算所需元素的索引。
因此,在构建此工具时,最好的工具之一是从 xlsx (=zip) 文件中提取数据以查看实际内容的实用程序。
如果您下载该项目,您会注意到使用了完全限定的类名。在本文中,为了提高可读性,我删除了类前面的命名空间。因此,如果您从代码片段中复制代码,请不要忘记为这两个命名空间定义 using
(在 C# 中)或 Imports
(在 VB 中)
- DocumentFormat.OpenXml.Packaging
- DocumentFormat.OpenXml.Spreadsheet
创建电子表格
现在来看代码。第一件事是创建电子表格。这实际上是 xlsx 文件。电子表格在 C# 中创建如下:
/// <summary>
/// Creates the workbook
/// </summary>
/// <returns>Spreadsheet created</returns>
public static SpreadsheetDocument CreateWorkbook(string fileName) {
SpreadsheetDocument spreadSheet = null;
SharedStringTablePart sharedStringTablePart;
WorkbookStylesPart workbookStylesPart;
try {
// Create the Excel workbook
spreadSheet = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook, false);
// Create the parts and the corresponding objects
// Workbook
spreadSheet.AddWorkbookPart();
spreadSheet.WorkbookPart.Workbook = new Workbook();
spreadSheet.WorkbookPart.Workbook.Save();
// Shared string table
sharedStringTablePart = spreadSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
sharedStringTablePart.SharedStringTable = new SharedStringTable();
sharedStringTablePart.SharedStringTable.Save();
// Sheets collection
spreadSheet.WorkbookPart.Workbook.Sheets = new Sheets();
spreadSheet.WorkbookPart.Workbook.Save();
// Stylesheet
workbookStylesPart = spreadSheet.WorkbookPart.AddNewPart<WorkbookStylesPart>();
workbookStylesPart.Stylesheet = new Stylesheet();
workbookStylesPart.Stylesheet.Save();
} catch (System.Exception exception) {
System.Windows.MessageBox.Show(exception.Message, "Excel OpenXML basics", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
}
return spreadSheet;
}
而在 Visual Basic 中:
''' <summary>
''' Creates the workbook
''' </summary>
''' <returns>Spreadsheet created</returns>
Public Shared Function CreateWorkbook(fileName As String) As SpreadsheetDocument
Dim spreadSheet As SpreadsheetDocument = Nothing
Dim sharedStringTablePart As SharedStringTablePart
Dim workbookStylesPart As WorkbookStylesPart
Try
' Create the Excel workbook
spreadSheet = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook, False)
' Create the parts and the corresponding objects
' Workbook
spreadSheet.AddWorkbookPart()
spreadSheet.WorkbookPart.Workbook = New Workbook()
spreadSheet.WorkbookPart.Workbook.Save()
' Shared string table
sharedStringTablePart = spreadSheet.WorkbookPart.AddNewPart(Of SharedStringTablePart)()
sharedStringTablePart.SharedStringTable = New SharedStringTable()
sharedStringTablePart.SharedStringTable.Save()
' Sheets collection
spreadSheet.WorkbookPart.Workbook.Sheets = New DocumentFormat.OpenXml.Spreadsheet.Sheets()
spreadSheet.WorkbookPart.Workbook.Save()
' Stylesheet
workbookStylesPart = spreadSheet.WorkbookPart.AddNewPart(Of WorkbookStylesPart)()
workbookStylesPart.Stylesheet = New Stylesheet()
workbookStylesPart.Stylesheet.Save()
Catch exception As System.Exception
System.Windows.MessageBox.Show(exception.Message, "Excel OpenXML basics", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand)
End Try
Return spreadSheet
End Function
创建电子表格文档后,添加所有工作表共有的四个基本部件:
- 工作簿:包含工作簿本身的定义
- 共享字符串表:单元格之间共享的字符串表
- 工作表集合:工作簿中的工作表集合
- 样式表:单元格的样式定义。例如边框、字体等。
DocumentFormat.OpenXml.Packaging
命名空间中找到,而 Excel 对象在 DocumentFormat.OpenXml.Spreadsheet
命名空间中。因此,部件描述“存储”,而 Spreadsheet 命名空间中的对象描述 xml 文件中的内容元素。
创建样式
使用代码创建
创建样式定义的一种方法是从代码中创建它们。在演示中,创建了以下基本样式:
- 数字格式(在
x:numFmts
中):用于正确格式化货币值 - 字体(在
x:fonts
中):使用 Calibri,字号为 11 - 填充(在
x:fills
中):定义一个无图案的填充 - 边框(在
x:borders
中):定义一个无边框的边框定义 - 单元格样式格式(在
x:CellStyleXfs
中):使用通用的单元格样式格式定义 - 单元格格式(在
x:CellXfs
中) - 通用文本
- 日期:NumberFormatId 22
- 货币:NumberFormatId 164,引用数字格式
- 百分比:NumberFormatId 10
另一件事是,如前所述,样式表元素的顺序至关重要。如果顺序不正确,Excel 会很容易将文档解释为损坏。
样式创建的一个片段是创建 CellStyleFormats
元素和格式内的 CellFormat
元素
Stylesheet stylesheet = spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet;
// Cell formats (x:CellXfs)
stylesheet.InsertAt<CellFormats>(new CellFormats(), 5);
// General text
stylesheet.GetFirstChild<CellFormats>().InsertAt<CellFormat>(
new CellFormat() {
FormatId = 0,
NumberFormatId = 0
}, 0);
Dim stylesheet As Stylesheet = spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet
' Cell formats (x:CellXfs)
stylesheet.InsertAt(Of CellFormats)(New CellFormats(), 5)
' General text
stylesheet.GetFirstChild(Of CellFormats)().InsertAt(Of CellFormat)(
New CellFormat() With {
.FormatId = 0,
.NumberFormatId = 0
}, 0)
使用预定义的样式表
定义样式的另一种方法是使用包含样式定义的现有 XML 文件。在项目中,有一个 PredefinedStyles.xml 文件包含在构建输出中。此文件的内容被读取到字符串中,然后添加到之前创建的空样式表中。
样式定义如下(它实际上与使用代码创建的相同):
<x:numFmts>
<x:numFmt numFmtId="164" formatCode="#,##0.00\ "€"" />
</x:numFmts>
<x:fonts>
<x:font>
<x:sz val="11" />
<x:name val="Calibri" />
</x:font>
</x:fonts>
<x:fills>
<x:fill>
<x:patternFill patternType="none" />
</x:fill>
</x:fills>
<x:borders>
<x:border>
<x:left />
<x:right />
<x:top />
<x:bottom />
<x:diagonal />
</x:border>
</x:borders>
<x:cellStyleXfs>
<x:xf numFmtId="0" fontId="0" fillId="0" borderId="0" />
</x:cellStyleXfs>
<x:cellXfs>
<x:xf numFmtId="0" xfId="0" />
<x:xf numFmtId="22" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />
<x:xf numFmtId="164" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />
<x:xf numFmtId="10" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />
</x:cellXfs>
它在 C# 中使用以下方法添加:
/// <summary>
/// Adds a predefined style from the given xml
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="xml">Style definition as xml</param>
/// <returns>True if succesful</returns>
public static bool AddPredefinedStyles(SpreadsheetDocument spreadsheet, string xml) {
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.InnerXml = xml;
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.Save();
return true;
}
以及 Visual Basic 中的函数
''' <summary>
''' Adds a predefined style from the given xml
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="xml">Style definition as xml</param>
''' <returns>True if succesful</returns>
Public Shared Function AddPredefinedStyles(spreadsheet As SpreadsheetDocument, xml As String) As Boolean
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.InnerXml = xml
spreadsheet.WorkbookPart.WorkbookStylesPart.Stylesheet.Save()
Return True
End Function
事实上,基本上任何元素都可以通过简单地添加 XML 来填充。例如,工作表可以通过添加适当的 XML 作为工作表的内部文本来填充。
添加工作表
下一步是添加工作表。添加工作表很简单。但是,需要记住的一个陷阱是定义与工作簿的关系。否则,当工作簿打开时,工作表将不包含在内。
工作簿的创建在 C# 中如下所示:
/// <summary>
/// Adds a new worksheet to the workbook
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="name">Name of the worksheet</param>
/// <returns>True if succesful</returns>
public static bool AddWorksheet(SpreadsheetDocument spreadsheet, string name) {
Sheets sheets = spreadsheet.WorkbookPart.Workbook.GetFirstChild<Sheets>();
Sheet sheet;
WorksheetPart worksheetPart;
// Add the worksheetpart
worksheetPart = spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet(new SheetData());
worksheetPart.Worksheet.Save();
// Add the sheet and make relation to workbook
sheet = new DocumentFormat.OpenXml.Spreadsheet.Sheet() {
Id = spreadsheet.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = (uint)(spreadsheet.WorkbookPart.Workbook.Sheets.Count() + 1),
Name = name
};
sheets.Append(sheet);
spreadsheet.WorkbookPart.Workbook.Save();
return true;
}
以及 Visual Basic 中的等效函数
''' <summary>
''' Adds a new worksheet to the workbook
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="name">Name of the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function AddWorksheet(spreadsheet As SpreadsheetDocument, name As String) As Boolean
Dim sheets As Sheets = spreadsheet.WorkbookPart.Workbook.GetFirstChild(Of Sheets)()
Dim sheet As Sheet
Dim worksheetPart As WorksheetPart
' Add the worksheetpart
worksheetPart = spreadsheet.WorkbookPart.AddNewPart(Of WorksheetPart)()
worksheetPart.Worksheet = New Worksheet(New SheetData())
worksheetPart.Worksheet.Save()
' Add the sheet and make relation to workbook
sheet = New Sheet With {
.Id = spreadsheet.WorkbookPart.GetIdOfPart(worksheetPart),
.SheetId = (spreadsheet.WorkbookPart.Workbook.Sheets.Count() + 1),
.Name = name}
sheets.Append(sheet)
spreadsheet.WorkbookPart.Workbook.Save()
Return True
End Function
另一件事是记住正确地给工作表编号。因此,在定义 SheetId
时会查询工作表的数量。
另一个常见的事物:共享字符串
工作簿包含一个共享字符串表。其思想是工作表可以引用共享字符串,而无需在工作表中实际存储字符串值。
尽管这听起来是个好主意,并且是建议的方法,但对文件大小的影响并不是很大。当我用单个(短)字符串重复 10,000 次进行测试时,使用共享字符串与在工作表中重复字符串之间的差异只有几百字节。这很大程度上是因为即使字符串重复,压缩算法也能很好地压缩字符串。当然,工作簿的结构和字符串的位置对此影响很大。
为了将共享字符串添加到表中,我在 C# 中使用以下方法:
/// <summary>
/// Add a single string to shared strings table.
/// Shared string table is created if it doesn't exist.
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="stringItem">string to add</param>
/// <param name="save">Save the shared string table</param>
/// <returns></returns>
public static bool AddSharedString(SpreadsheetDocument spreadsheet, string stringItem, bool save = true) {
SharedStringTable sharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable;
if (0 == sharedStringTable.Where(item => item.InnerText == stringItem).Count()) {
sharedStringTable.AppendChild(
new DocumentFormat.OpenXml.Spreadsheet.SharedStringItem(
new DocumentFormat.OpenXml.Spreadsheet.Text(stringItem)));
// Save the changes
if (save) {
sharedStringTable.Save();
}
}
return true;
}
在 Visual Basic 中
''' <summary>
''' Add a single string to shared strings table.
''' Shared string table is created if it doesn't exist.
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="stringItem">string to add</param>
''' <param name="save">Save the shared string table</param>
''' <returns></returns>
Public Shared Function AddSharedString(spreadsheet As SpreadsheetDocument, stringItem As String, Optional save As Boolean = True) As Boolean
Dim sharedStringTable As SharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable
Dim stringQuery = (From item In sharedStringTable
Where item.InnerText = stringItem
Select item).Count()
If 0 = stringQuery Then
sharedStringTable.AppendChild(
New DocumentFormat.OpenXml.Spreadsheet.SharedStringItem(
New DocumentFormat.OpenXml.Spreadsheet.Text(stringItem)))
' Save the changes
If save Then
sharedStringTable.Save()
End If
End If
Return True
End Function
首先,检查字符串是否存在,如果不存在,则将其添加到表中。
在许多方法中,我有一个可选的 save
参数。这是因为保存更改相当慢,所以当数据例如在循环中添加时,保存会延迟。
现在谈谈数据
此演示将以下数据添加到工作表:
- 一个共享字符串,3 次
- 一个小数
- 一个整数
- 一个货币值
- 一个日期
- 一个百分比
- 以及一个布尔值
所有数据都通过特定方法添加,但它们都使用一个通用方法来实际将值存储在工作表中。
我不会列出添加单元格值的过程,因为它是一个稍大的方法,但它包含以下操作:
- 检查已定义行是否存在。如果未找到,则创建一行
- 检查已定义单元格(基于列索引)是否存在。如果未找到,则创建一个单元格
- 检查列是否在列集合中定义。如有必要,则添加。实际上这并非强制性,但通过定义列可以设置其宽度。
- 最后将值添加到单元格中
- 再次注意顺序。行和单元格在 XML 中必须按正确的顺序排列
- 不使用日期数据类型。这是因为 Office 2007 不支持日期类型。
- 单元格中的引用定义为 A1、B3 等。从代码角度来看,这是一种相当繁琐的方式。这就是我同时使用列和行的索引的原因。
列字符串的构造如下
/// <summary>
/// Converts a column number to column name (i.e. A, B, C..., AA, AB...)
/// </summary>
/// <param name="columnIndex">Index of the column</param>
/// <returns>Column name</returns>
public static string ColumnNameFromIndex(uint columnIndex) {
uint remainder;
string columnName = "";
while (columnIndex > 0) {
remainder = (columnIndex - 1) % 26;
columnName = System.Convert.ToChar(65 + remainder).ToString() + columnName;
columnIndex = (uint)((columnIndex - remainder) / 26);
}
return columnName;
}
以及在 VB.Net 中
''' <summary>
''' Converts a column number to column name (i.e. A, B, C..., AA, AB...)
''' </summary>
''' <param name="columnIndex">Index of the column</param>
''' <returns>Column name</returns>
Public Shared Function ColumnNameFromIndex(columnIndex As UInt32) As String
Dim remainder As UInt32
Dim columnName As String = ""
While (columnIndex > 0)
remainder = (columnIndex - 1) Mod 26
columnName = System.Convert.ToChar(65 + remainder).ToString() + columnName
columnIndex = ((columnIndex - remainder) / 26)
End While
Return columnName
End Function
添加字符串或共享字符串
如前所述,共享字符串在工作表中仅使用一个索引来指向共享字符串表中的字符串。令人惊讶的是,我没有找到直接从表中获取字符串索引的机制。相反,我必须构建一个循环来计算索引。
/// <summary>
/// Returns the index of a shared string.
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="stringItem">String to search for</param>
/// <returns>Index of a shared string. -1 if not found</returns>
public static int IndexOfSharedString(SpreadsheetDocument spreadsheet, string stringItem) {
SharedStringTable sharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable;
bool found = false;
int index = 0;
foreach (SharedStringItem sharedString in sharedStringTable.Elements<SharedStringItem>()) {
if (sharedString.InnerText == stringItem) {
found = true;
break; ;
}
index++;
}
return found ? index : -1;
}
以及在 Visual Basic 中
''' <summary>
''' Returns the index of a shared string.
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="stringItem">String to search for</param>
''' <returns>Index of a shared string. -1 if not found</returns>
Public Shared Function IndexOfSharedString(spreadsheet As SpreadsheetDocument, stringItem As String) As Int32
Dim sharedStringTable As SharedStringTable = spreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable
Dim found As Boolean = False
Dim index As Int32 = 0
For Each sharedString As SharedStringItem In sharedStringTable.Elements(Of SharedStringItem)()
If sharedString.InnerText = stringItem Then
found = True
Exit For
End If
index = index + 1
Next
If found Then
Return index
Else
Return -1
End If
End Function
添加字符串的方法非常简单。它提供了添加共享字符串或普通字符串的选项。
/// <summary>
/// Sets a string value to a cell
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="worksheet">Worksheet to use</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="rowIndex">Index of the row</param>
/// <param name="stringValue">String value to set</param>
/// <param name="useSharedString">Use shared strings? If true and the string isn't found in shared strings, it will be added</param>
/// <param name="save">Save the worksheet</param>
/// <returns>True if succesful</returns>
public static bool SetCellValue(SpreadsheetDocument spreadsheet, Worksheet worksheet, uint columnIndex, uint rowIndex, string stringValue, bool useSharedString, bool save = true) {
string columnValue = stringValue;
CellValues cellValueType;
// Add the shared string if necessary
if (useSharedString) {
if (Excel.IndexOfSharedString(spreadsheet, stringValue) == -1) {
Excel.AddSharedString(spreadsheet, stringValue, true);
}
columnValue = Excel.IndexOfSharedString(spreadsheet, stringValue).ToString();
cellValueType = CellValues.SharedString;
} else {
cellValueType = CellValues.String;
}
return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, cellValueType, columnValue, null, save);
}
以及在 VB.Net 中
''' <summary>
''' Sets a string value to a cell
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="worksheet">Worksheet to use</param>
''' <param name="columnIndex">Index of the column</param>
''' <param name="rowIndex">Index of the row</param>
''' <param name="stringValue">String value to set</param>
''' <param name="useSharedString">Use shared strings? If true and the string isn't found in shared strings, it will be added</param>
''' <param name="save">Save the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function SetStringCellValue(spreadsheet As SpreadsheetDocument, worksheet As Worksheet, columnIndex As UInt32, rowIndex As UInt32, stringValue As String, useSharedString As Boolean, Optional save As Boolean = True) As Boolean
Dim columnValue As String = stringValue
Dim cellValueType As CellValues
' Add the shared string if necessary
If (useSharedString) Then
If (Excel.IndexOfSharedString(spreadsheet, stringValue) = -1) Then
Excel.AddSharedString(spreadsheet, stringValue, True)
End If
columnValue = Excel.IndexOfSharedString(spreadsheet, stringValue).ToString()
cellValueType = CellValues.SharedString
Else
cellValueType = CellValues.String
End If
Return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, cellValueType, columnValue, Nothing, save)
End Function
添加数值
添加数值与添加非共享字符串值非常相似。唯一需要注意的是在 xml 内部使用正确的十进制分隔符(.
),因此可能需要替换十进制分隔符。另一种方法是将当前线程的区域性设置为 en-US。如果您使用 EN_US_CULTURE
编译符号(项目中默认)编译项目,则线程的 UI
区域性将设置为本地区域性,但线程的 CurrentCulture
将替换为 en-US。这消除了重新格式化小数的需要。但是,请记住,如果您要获取例如 CurrencySymbol
等区域设置,则必须从 CurrentUICulture
中获取。
代码如下所示:
/// <summary>
/// Sets a cell value with double number
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="worksheet">Worksheet to use</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="rowIndex">Index of the row</param>
/// <param name="doubleValue">Double value</param>
/// <param name="styleIndex">Style to use</param>
/// <param name="save">Save the worksheet</param>
/// <returns>True if succesful</returns>
public static bool SetCellValue(SpreadsheetDocument spreadsheet, Worksheet worksheet, uint columnIndex, uint rowIndex, double doubleValue, uint? styleIndex, bool save = true) {
#if EN_US_CULTURE
string columnValue = doubleValue.ToString();
#else
string columnValue = doubleValue.ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".");
#endif
return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Number, columnValue, styleIndex, save);
}
Visual Basic 版本是:
''' <summary>
''' Sets a cell value with double number
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="worksheet">Worksheet to use</param>
''' <param name="columnIndex">Index of the column</param>
''' <param name="rowIndex">Index of the row</param>
''' <param name="doubleValue">Double value</param>
''' <param name="styleIndex">Style to use</param>
''' <param name="save">Save the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function SetDoubleCellValue(spreadsheet As SpreadsheetDocument, worksheet As Worksheet, columnIndex As UInt32, rowIndex As UInt32, doubleValue As Double, styleIndex As UInt32?, Optional save As Boolean = True) As Boolean
#If EN_US_CULTURE Then
Dim columnValue As String = doubleValue.ToString()
#Else
Dim columnValue As String = doubleValue.ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".")
#End If
Return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Number, columnValue, styleIndex, save)
End Function
添加日期值
日期值有点棘手。关于日期需要记住的事项:
- 需要指定样式。我使用了预定义的格式 ID 22 来正确格式化日期
- 在样式定义中,ApplyNumberFormat 必须为 true。
- 日期表示为双精度值
- Excel 从 1900 年 1 月 1 日开始计算日期。这与 .Net 不同,因此必须使用 OLE 自动化日期
- 可能需要更正小数分隔符(取决于区域设置)。例如,我的小数分隔符是逗号,所以必须用点号替换。或者如前面数字所述,可以使用 en-US 区域性。
/// <summary>
/// Sets a cell value with a date
/// </summary>
/// <param name="spreadsheet">Spreadsheet to use</param>
/// <param name="worksheet">Worksheet to use</param>
/// <param name="columnIndex">Index of the column</param>
/// <param name="rowIndex">Index of the row</param>
/// <param name="datetimeValue">DateTime value</param>
/// <param name="styleIndex">Style to use</param>
/// <param name="save">Save the worksheet</param>
/// <returns>True if succesful</returns>
public static bool SetCellValue(SpreadsheetDocument spreadsheet, Worksheet worksheet, uint columnIndex, uint rowIndex, System.DateTime datetimeValue, uint? styleIndex, bool save = true) {
#if EN_US_CULTURE
string columnValue = datetimeValue.ToOADate().ToString();
#else
string columnValue = datetimeValue.ToOADate().ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".");
#endif
return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Date, columnValue, styleIndex, save);
}
以及 Visual Basic 的等效代码
''' <summary>
''' Sets a cell value with a date
''' </summary>
''' <param name="spreadsheet">Spreadsheet to use</param>
''' <param name="worksheet">Worksheet to use</param>
''' <param name="columnIndex">Index of the column</param>
''' <param name="rowIndex">Index of the row</param>
''' <param name="datetimeValue">DateTime value</param>
''' <param name="styleIndex">Style to use</param>
''' <param name="save">Save the worksheet</param>
''' <returns>True if succesful</returns>
Public Shared Function SetDateCellValue(spreadsheet As SpreadsheetDocument, worksheet As Worksheet, columnIndex As UInt32, rowIndex As UInt32, datetimeValue As System.DateTime, styleIndex As UInt32?, Optional save As Boolean = True) As Boolean
#If EN_US_CULTURE Then
Dim columnValue As String = datetimeValue.ToOADate().ToString()
#Else
Dim columnValue As String = datetimeValue.ToOADate().ToString().Replace(System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator, ".")
#End If
Return SetCellValue(spreadsheet, worksheet, columnIndex, rowIndex, CellValues.Date, columnValue, styleIndex, save)
End Function
货币、百分比和布尔值
接下来添加不同值类型的方法与前面非常相似,因此我不会将它们包含在文章中。但是,有几点需要记住:
- 百分比类似于小数,但格式不同。我使用了格式 ID 10。
- 货币与百分比非常相似,但这次也需要定义
numFmt
。 - Excel 使用 0 和 1 表示布尔值。如果使用
True
或False
,它们将被解释为文本。在 Visual Basic 中,true 的值 (-1) 需要更改为 1。
最后的话
希望本文能帮助您使用 Open XML Excel 文件。了解代码的最佳方法是调试它并进行小的更改以查看会发生什么 :) 玩得开心!
历史
- 2012 年 4 月 22 日:创建
- 2012 年 4 月 25 日:可读性修改,添加了将数字写入 xlsx 时的另一种格式化方式