[英]OpenXml and Date format in Excel cell
我正在嘗試使用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<WorksheetPart>();
string relId = workbookPart.GetIdOfPart(worksheetPart);
// File Version
var fileVersion = new FileVersion { ApplicationName = "Microsoft Office Excel" };
// Style Part
WorkbookStylesPart wbsp = workbookPart.AddNewPart<WorkbookStylesPart>();
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<OpenXmlElement> CreateSheetData1()
{
List<OpenXmlElement> elements = new List<OpenXmlElement>();
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;
我可以打開文檔但是日期如果沒有格式化,只會顯示日期的編號。
感謝您幫助格式化日期。
另一個BIG BIG投票: https : //github.com/closedxml/closedxml
在嘗試從網絡上的點點滴滴(包括StackOverFlow)構建我自己的類之后,我找到了上面提到的庫,並且片刻之后還有一個功能齊全的Excel文件。
我已將下面的嘗試粘貼到任何感覺完成它的沖動的人的啟發下。 它部分完成,並且日期和字符串單元格創建存在問題。
在嘗試使用此類之前,首先下載closedXML並首先嘗試。
考慮自己警告。
/// <summary>
/// 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.
/// </summary>
internal class SimpleExcelDocument : IDisposable
{
SheetData sheetData;
/// <summary>
/// Constructor is nothing special because the work is done at export.
/// </summary>
internal SimpleExcelDocument()
{
sheetData = new SheetData();
}
#region Get Cell Reference
public Cell GetCell(string fullAddress)
{
return sheetData.Descendants<Cell>().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>();
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<Sheets>().AppendChild<Sheet>(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<Cell>().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<Row>().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;
/// <summary>
/// This will dispose of any open resources.
/// </summary>
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
}
這篇博客幫助了我: http : //polymathprogrammer.com/2009/11/09/how-to-create-stylesheet-in-excel-open-xml/
我的問題是我想將NumberingFormats添加到樣式表而不是添加一個新的樣式表。 如果你想這樣做,請使用
Stylesheet.InsertAt<NumberingFormats>(new NumberingFormats(), 0);
而不是
Stylesheet.AppendChild<NumberingFormats>(new NumberingFormats(), 0);
驚喜,訂單數量..
https://github.com/closedxml/closedxml基本上是我認為的正確答案。
以下是如何在單元格上應用自定義日期格式。 首先,我們必須在工作簿的樣式表中查找或創建格式:
// 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<NumberingFormat>().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開始。
我有同樣的問題,最后我自己導出到Excel編寫器。 代碼就在那里解決了這個問題,但你真的會更好地使用整個導出器。 它很快並且允許細胞的大量格式化。 你可以在那里查看
https://openxmlexporttoexcel.codeplex.com/
我希望它有所幫助。
要了解為什么CellValues.Date DataType不起作用(至少在所有Excel版本中都沒有),請參閱:
有關完整,有效且解釋良好的解決方案,請參閱:
在嘗試了很多帖子之后,我發現.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
您的答案可以在什么表明Office Open XML Cell包含日期/時間值?
訣竅在於,單元格的StyleIndex(s-attribute)實際上是電子表格樣式部分中單元格樣式(XF元素)列表的索引。 其中每一個都將指向Samuel提到的預定義數字格式ID。 如果我沒記錯的話,您要查找的數字格式ID是14或15。
我希望以下鏈接對未來的訪問者有所幫助。
首先, 獲取標准文檔 。
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;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.