2011-05-17 103 views
12

我正在嘗試使用OpenXML以xlsx格式創建Excel文件,因爲我需要在Web服務器上使用它。Excel中的OpenXml和日期格式單元格

我沒有任何問題來填充表中的值;但我很努力在單元格中設置經典日期格式。

下面的快速測試使用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'中發現了不可讀的內容。你想恢復這個工作簿的內容嗎?如果您信任該工作簿的來源,如果我刪除行單擊Yes「

cell3.StyleIndex = 1; 

我可以打開該文檔,但如果沒有格式化的日期,只顯示最新的數。

感謝您幫助格式化日期。

回答

1

我相信你的問題在NumberFormatId。內置數字格式編號0 - 163自定義格式,必須在啓動164

+0

如果更改NumberFormatId和StyleIndex(CELL3)爲164的值,還是有一錯誤。 StyleIndex是否必須與NumberingFormatId具有相同的值? – Dan 2011-05-17 17:05:49

+0

@ daner06,no,StyleIndex引用單元樣式索引。 – 2011-05-18 00:24:58

+1

某處是否有這些NumberFormatId值的列表? – 2014-05-23 14:49:38

1

你的答案可以在What indicates an Office Open XML Cell contains a Date/Time value?

訣竅發現是,電池的StyleIndex(S-屬性)是名副其實的索引到電子表格樣式部分中的單元格樣式(XF元素)列表中。其中每一個都會指向Samuel提到的預定義數字格式ID。如果我沒記錯的話,你要找的數字格式編號是14或15.

+0

除非有14或15​​的內部,其中? > 14或15,您將收到此消息。如果第14或第15個單元格樣式與您正在尋找的格式不匹配,則會得到意想不到的結果。 applyNumberFormat =「1」,numFmtId =「X」,和,其中X是> 163的數字應該有幫助。如果沒有明確將其添加到xlsx文件,我從來沒有自定義格式化工作。 – TamusJRoyce 2011-06-23 19:43:00

6

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

試圖從四處撒網,包括計算器蔓延的點點滴滴建立自己下課後,我發現上面提到的圖書館,並在幾分鐘有一個功能齊全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 
    } 
1

我有同樣的問題,並最終寫我自己的導出到Excel的作家。代碼就是爲了解決這個問題,但是如果使用整個導出器,你真的會更好。它速度很快,並允許大量格式的單元格。您可以在

https://openxmlexporttoexcel.codeplex.com/

審查,我希望它能幫助。

0

我希望以下鏈接對未來的訪問者有所幫助。第一,Get the standards documentation

ECMA-376第4版第1部分是最有用的文檔。在本文檔中,涉及到這個問題部分是:

30年8月18日

31年8月18日(這低劣屎語義學對)

45年8月18日(一式的定義中,Excel作爲理解)

L.2.7.3.6(樣式如何引用)

2

這裏是如何在一個單元格應用自定義日期格式。 首先,我們要查找或創建工作簿的樣式表的格式:

// 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); 
+0

這會在打開電子表格時導致「修復記錄」警告 – 2018-01-11 12:09:30

0

我也遇到過保存文檔後,同樣的問題涉及格式化日期字段。並將該溶液是添加數字格式如下:

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; 
相關問題