Excel单元格中的OpenXml和Date格式

我正在尝试使用OpenXML以xlsx格式创建Excel文件,因为我需要在Web服务器上使用它。

填写表格中的值没有任何问题; 但是我很难在单元格中设置经典的日期格式。

下面是使用DocumentFormat.OpenXml和WindowsBase引用的快速测试。

 class Program { static void Main(string[] args) { BuildExel(@"C:\test.xlsx"); } public static void BuildExel(string fileName) { using (SpreadsheetDocument myWorkbook = SpreadsheetDocument.Create(fileName, SpreadsheetDocumentType.Workbook)) { // Workbook Part WorkbookPart workbookPart = myWorkbook.AddWorkbookPart(); var worksheetPart = workbookPart.AddNewPart(); string relId = workbookPart.GetIdOfPart(worksheetPart); // File Version var fileVersion = new FileVersion { ApplicationName = "Microsoft Office Excel" }; // Style Part WorkbookStylesPart wbsp = workbookPart.AddNewPart(); wbsp.Stylesheet = CreateStylesheet(); wbsp.Stylesheet.Save(); // Sheets var sheets = new Sheets(); var sheet = new Sheet { Name = "sheetName", SheetId = 1, Id = relId }; sheets.Append(sheet); // Data SheetData sheetData = new SheetData(CreateSheetData1()); // Add the parts to the workbook and save var workbook = new Workbook(); workbook.Append(fileVersion); workbook.Append(sheets); var worksheet = new Worksheet(); worksheet.Append(sheetData); worksheetPart.Worksheet = worksheet; worksheetPart.Worksheet.Save(); myWorkbook.WorkbookPart.Workbook = workbook; myWorkbook.WorkbookPart.Workbook.Save(); myWorkbook.Close(); } } private static Stylesheet CreateStylesheet() { Stylesheet ss = new Stylesheet(); var nfs = new NumberingFormats(); var nformatDateTime = new NumberingFormat { NumberFormatId = UInt32Value.FromUInt32(1), FormatCode = StringValue.FromString("dd/mm/yyyy") }; nfs.Append(nformatDateTime); ss.Append(nfs); return ss; } private static List CreateSheetData1() { List elements = new List(); var row = new Row(); // Line 1 Cell[] cells = new Cell[2]; Cell cell1 = new Cell(); cell1.DataType = CellValues.InlineString; cell1.InlineString = new InlineString { Text = new Text { Text = "Daniel" } }; cells[0] = cell1; Cell cell2 = new Cell(); cell2.DataType = CellValues.Number; cell2.CellValue = new CellValue((50.5).ToString()); cells[1] = cell2; row.Append(cells); elements.Add(row); // Line 2 row = new Row(); cells = new Cell[1]; Cell cell3 = new Cell(); cell3.DataType = CellValues.Date; cell3.CellValue = new CellValue(DateTime.Now.ToOADate().ToString()); cell3.StyleIndex = 1; // <= here I try to apply the style... cells[0] = cell3; row.Append(cells); elements.Add(row); return elements; } 

执行的代码创建Excel文档。 但是,当我尝试打开文档时,收到此消息: “Excel在’test.xlsx’中找到了不可读的内容。 您想恢复此工作簿的内容吗? 如果您信任此工作簿的来源,请单击“是”。

如果我删除该行:

 cell3.StyleIndex = 1; 

我可以打开文档但是日期如果没有格式化,只会显示日期的编号。

感谢您帮助格式化日期。

这个博客帮助了我: http : //polymathprogrammer.com/2009/11/09/how-to-create-stylesheet-in-excel-open-xml/

我的问题是我想将NumberingFormats添加到样式表而不是添加一个新的样式表。 如果你想这样做,请使用

 Stylesheet.InsertAt(new NumberingFormats(), 0); 

而不是

 Stylesheet.AppendChild(new NumberingFormats(), 0); 

惊喜,订单数量..

另一个BIG BIG投票: https : //github.com/closedxml/closedxml

在尝试从网络上的点点滴滴(包括StackOverFlow)构建我自己的类之后,我找到了上面提到的库,并在一段时间内得到了一个function齐全的Excel文件。

我已经粘贴了下面的尝试,以启发任何感觉完成它的冲动的人。 它部分完成,并且日期和字符串单元格创建存在问题。

在尝试使用此类之前,首先下载closedXML并首先尝试。

考虑自己警告。

  ///  /// This class allows for the easy creation of a simple Excel document who's sole purpose is to contain some export data. /// The document is created using OpenXML. ///  internal class SimpleExcelDocument : IDisposable { SheetData sheetData; ///  /// Constructor is nothing special because the work is done at export. ///  internal SimpleExcelDocument() { sheetData = new SheetData(); } #region Get Cell Reference public Cell GetCell(string fullAddress) { return sheetData.Descendants().Where(c => c.CellReference == fullAddress).FirstOrDefault(); } public Cell GetCell(uint rowId, uint columnId, bool autoCreate) { return GetCell(getColumnName(columnId), rowId, autoCreate); } public Cell GetCell(string columnName, uint rowId, bool autoCreate) { return getCell(sheetData, columnName, rowId, autoCreate); } #endregion #region Get Cell Contents // See: http://msdn.microsoft.com/en-us/library/ff921204.aspx // #endregion #region Set Cell Contents public void SetValue(uint rowId, uint columnId, bool value) { Cell cell = GetCell(rowId, columnId, true); cell.DataType = CellValues.Boolean; cell.CellValue = new CellValue(BooleanValue.FromBoolean(value)); } public void SetValue(uint rowId, uint columnId, double value) { Cell cell = GetCell(rowId, columnId, true); cell.DataType = CellValues.Number; cell.CellValue = new CellValue(DoubleValue.FromDouble(value)); } public void SetValue(uint rowId, uint columnId, Int64 value) { Cell cell = GetCell(rowId, columnId, true); cell.DataType = CellValues.Number; cell.CellValue = new CellValue(IntegerValue.FromInt64(value)); } public void SetValue(uint rowId, uint columnId, DateTime value) { Cell cell = GetCell(rowId, columnId, true); //cell.DataType = CellValues.Date; cell.CellValue = new CellValue(value.ToOADate().ToString()); cell.StyleIndex = 1; } public void SetValue(uint rowId, uint columnId, string value) { Cell cell = GetCell(rowId, columnId, true); cell.InlineString = new InlineString(value.ToString()); cell.DataType = CellValues.InlineString; } public void SetValue(uint rowId, uint columnId, object value) { bool boolResult; Int64 intResult; DateTime dateResult; Double doubleResult; string stringResult = value.ToString(); if (bool.TryParse(stringResult, out boolResult)) { SetValue(rowId, columnId, boolResult); } else if (DateTime.TryParse(stringResult, out dateResult)) { SetValue(rowId, columnId,dateResult); } else if (Int64.TryParse(stringResult, out intResult)) { SetValue(rowId, columnId, intResult); } else if (Double.TryParse(stringResult, out doubleResult)) { SetValue(rowId, columnId, doubleResult); } else { // Just assume that it is a plain string. SetValue(rowId, columnId, stringResult); } } #endregion public SheetData ExportAsSheetData() { return sheetData; } public void ExportAsXLSXStream(Stream outputStream) { // See: http://blogs.msdn.com/b/chrisquon/archive/2009/07/22/creating-an-excel-spreadsheet-from-scratch-using-openxml.aspx for some ideas... // See: http://stackoverflow.com/questions/1271520/opening-xlsx-in-office-2003 using (SpreadsheetDocument package = SpreadsheetDocument.Create(outputStream, SpreadsheetDocumentType.Workbook)) { // Setup the basics of a spreadsheet document. package.AddWorkbookPart(); package.WorkbookPart.Workbook = new Workbook(); WorksheetPart workSheetPart = package.WorkbookPart.AddNewPart(); workSheetPart.Worksheet = new Worksheet(sheetData); workSheetPart.Worksheet.Save(); // create the worksheet to workbook relation package.WorkbookPart.Workbook.AppendChild(new Sheets()); Sheet sheet = new Sheet { Id = package.WorkbookPart.GetIdOfPart(workSheetPart), SheetId = 1, Name = "Sheet 1" }; package.WorkbookPart.Workbook.GetFirstChild().AppendChild(sheet); package.WorkbookPart.Workbook.Save(); package.Close(); } } #region Internal Methods private static string getColumnName(uint columnId) { if (columnId < 1) { throw new Exception("The column # can't be less then 1."); } columnId--; if (columnId >= 0 && columnId < 26) return ((char)('A' + columnId)).ToString(); else if (columnId > 25) return getColumnName(columnId / 26) + getColumnName(columnId % 26 + 1); else throw new Exception("Invalid Column #" + (columnId + 1).ToString()); } // Given a worksheet, a column name, and a row index, // gets the cell at the specified column private static Cell getCell(SheetData worksheet, string columnName, uint rowIndex, bool autoCreate) { Row row = getRow(worksheet, rowIndex, autoCreate); if (row == null) return null; Cell foundCell = row.Elements().Where(c => string.Compare (c.CellReference.Value, columnName + rowIndex, true) == 0).FirstOrDefault(); if (foundCell == null && autoCreate) { foundCell = new Cell(); foundCell.CellReference = columnName; row.AppendChild(foundCell); } return foundCell; } // Given a worksheet and a row index, return the row. // See: http://msdn.microsoft.com/en-us/library/bb508943(v=office.12).aspx#Y2142 private static Row getRow(SheetData worksheet, uint rowIndex, bool autoCreate) { if (rowIndex < 1) { throw new Exception("The row # can't be less then 1."); } Row foundRow = worksheet.Elements().Where(r => r.RowIndex == rowIndex).FirstOrDefault(); if (foundRow == null && autoCreate) { foundRow = new Row(); foundRow.RowIndex = rowIndex; worksheet.AppendChild(foundRow); } return foundRow; } #endregion #region IDisposable Stuff private bool _disposed; //private bool _transactionComplete; ///  /// This will dispose of any open resources. ///  public void Dispose() { Dispose(true); // Use SupressFinalize in case a subclass // of this type implements a finalizer. GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // If you need thread safety, use a lock around these // operations, as well as in your methods that use the resource. if (!_disposed) { if (disposing) { //if (!_transactionComplete) // Commit(); } // Indicate that the instance has been disposed. //_transaction = null; _disposed = true; } } #endregion } 

以下是如何在单元格上应用自定义日期格式。 首先,我们必须在工作簿的样式表中查找或创建格式:

 // get the stylesheet from the current sheet var stylesheet = spreadsheetDoc.WorkbookPart.WorkbookStylesPart.Stylesheet; // cell formats are stored in the stylesheet's NumberingFormats var numberingFormats = stylesheet.NumberingFormats; // cell format string const string dateFormatCode = "dd/mm/yyyy"; // first check if we find an existing NumberingFormat with the desired formatcode var dateFormat = numberingFormats.OfType().FirstOrDefault(format => format.FormatCode == dateFormatCode); // if not: create it if (dateFormat == null) { dateFormat = new NumberingFormat { NumberFormatId = UInt32Value.FromUInt32(164), // Built-in number formats are numbered 0 - 163. Custom formats must start at 164. FormatCode = StringValue.FromString(dateFormatCode) }; numberingFormats.AppendChild(dateFormat); // we have to increase the count attribute manually ?!? numberingFormats.Count = Convert.ToUInt32(numberingFormats.Count()); // save the new NumberFormat in the stylesheet stylesheet.Save(); } // get the (1-based) index of the dateformat var dateStyleIndex = numberingFormats.ToList().IndexOf(dateFormat) + 1; 

然后,我们可以使用已解析的styleindex将我们的格式应用于单元格:

 cell.StyleIndex = Convert.ToUInt32(dateStyleIndex); 

我相信你的问题出在NumberFormatId 。 内置数字格式编号为0 – 163.自定义格式必须从164开始。

您的答案可以在什么表明Office Open XML Cell包含日期/时间值?

诀窍在于,单元格的StyleIndex(s-attribute)实际上是电子表格样式部分中单元格样式(XF元素)列表的索引。 其中每一个都将指向Samuel提到的预定义数字格式ID。 如果我没记错的话,您要查找的数字格式ID是14或15。

我有同样的问题,最后我自己导出到Excel编写器。 代码就在那里解决了这个问题,但你真的会更好地使用整个导出器。 它很快并且允许细胞的大量格式化。 你可以在那里查看

https://openxmlexporttoexcel.codeplex.com/

我希望它有所帮助。

要了解为什么CellValues.Date DataType不起作用(至少在所有Excel版本中都没有),请参阅:

使用OpenXML在Excel单元格中添加日期

有关完整,有效且解释良好的解决方案,请参阅:

OpenXML – 在Excel电子表格中写入日期会导致内容不可读

在尝试了很多post之后,我发现.ToOADate()和CellValues.Number以及cell.StyleIndex = 4都是需要的…加上! 所有模板日期列必须格式化为默认日期样式,以便日期为FILTERABLE作为日期。 如果没有这些,打开Excel文件时会出现错误,或者值显示为数字。

 using DocumentFormat.OpenXml.Packaging; using DocumentFormat.OpenXml.Spreadsheet; // IMPORTANT! All template date columns MUST be formatted to the default date style for the dates to be filterable as dates Cell cell = new Cell(); dataMember = dataMember.ToOADate().ToString(); //OA Date needed to export number as Date cell.DataType = CellValues.Number; cell.CellValue = new CellValue(dataMember); cell.StyleIndex = 4; // Date format: M/d/yyyy 

我希望以下链接对未来的访问者有所帮助。

首先, 获取标准文档 。

ECMA-376第4版第1部分是最有用的文件。 本文档中与此问题相关的部分包括:

30年8月18日

18.8.31(这个糟糕的狗屎的语义)

18.8.45(excel理解的样式的定义)

L.2.7.3.6(如何引用样式)

我在保存文档后遇到了与格式化日期字段相同的问题。 解决方案是添加数字格式如下:

 new NumberingFormat() { NumberFormatId = 164, FormatCode = StringValue.FromString($"[$-409]d\\-mmm\\-yyyy;@") } 

并添加像这样的单元格:

 cell.CellValue = new CellValue(date.ToOADate().ToString()); cell.StyleIndex = 1; // your style index using numbering format above cell.DataType = CellValues.Number;