簡體   English   中英

OpenXML - 將日期寫入 Excel 電子表格會導致內容不可讀

[英]OpenXML - Writing a date into Excel spreadsheet results in unreadable content

我正在使用以下代碼將DateTime添加到電子表格的列中:

var dt = DateTime.Now;
r.AppendChild<Cell>(new Cell()
    { 
        CellValue = new CellValue(dt.ToOADate().ToString()),
        DataType = new EnumValue<CellValues>(CellValues.Date), 
        StyleIndex = 1,
        CellReference = header[6] + index
    });

當我嘗試在 Excel 2010 中打開文件時,出現錯誤

Excel 在 file.xlsx 中發現不可讀的內容

如果我注釋掉這條線,一切都很好。

我在 StackOverflow 上提到過類似的問題,但它們基本上和我有相同的代碼。

像往常一樣遲到了,但我必須發布一個答案,因為以前的所有答案都是完全錯誤的,除了奧萊被否決的答案,遺憾的是它不完整。

由於問題與 Excel 有關,最簡單的方法是使用您想要的數據和樣式創建 Excel 電子表格,然后將其作為零件打開並查看原始 Z3501BB093D363810B671059B9CFED3F8

將日期 01/01/2015 添加到單元格 A1 中會產生以下結果:

<row r="1">
  <c r="A1" s="0">
    <v>42005</v>
  </c>
</row>

請注意,type 屬性存在。 但是一個樣式屬性引用了以下樣式:

<xf numFmtId="14" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />

這是您必須添加的最基本樣式。

所以生成上面的代碼:

  1. 您需要按如下方式創建樣式:
var CellFormats = new CellFormats();
CellFormats.Append(new CellFormat()
{
    BorderId = 0,
    FillId = 0,
    FontId = 0,
    NumberFormatId = 14,
    FormatId = 0,
    ApplyNumberFormat = true
});
CellFormats.Count = (uint)CellFormats.ChildElements.Count;
var StyleSheet = new Stylesheet();
StyleSheet.Append(CellFormats);

NumberFormatId = 14指的是內置格式mm-dd-yy ,這里是一些其他格式的列表

不幸的是,添加上述樣式似乎還不夠,如果這樣做實際上會導致 Excel 崩潰。 請注意, BorderIdFillIdFontId需要與樣式表中的項目相對應,這意味着您需要提供它們。 完整代碼清單中的GetStyleSheet()方法提供了 Excel 正常工作所需的最低默認樣式表。

  1. 並添加一個單元格,如下所示:
SheetData.AppendChild(new Row(
    new Cell() 
    { 
        // CellValue is set to OADate because that's what Excel expects.
        CellValue = new CellValue(date.ToOADate().ToString(CultureInfo.InvariantCulture)), 
        // Style index set to style (0 based).
        StyleIndex = 0
    }));

注意:Office 2010 和 2013可以不同地處理日期,但默認情況下它們似乎沒有。

它們支持 ISO 8601 格式的日期,即yyyy-MM-ddTHH:mm:ss恰好這也是可排序的標准格式(“s”),因此您可以執行以下操作:

SheetData.AppendChild(new Row(
    new Cell() 
    { 
        CellValue = new CellValue(date.ToString("s")), 
        // This time we do add the DataType attribute but ONLY for Office 2010+.
        DataType = CellValues.Date
        StyleIndex = 1
    }));

結果:

<row>
  <c s="0" t="d">
    <v>2015-08-05T11:13:57</v>
  </c>
</row>

完整的代碼清單

以下是添加具有日期格式的單元格所需的最少代碼示例。

private static void TestExcel()
{
    using (var Spreadsheet = SpreadsheetDocument.Create("C:\\Example.xlsx", SpreadsheetDocumentType.Workbook))
    {
        // Create workbook.
        var WorkbookPart = Spreadsheet.AddWorkbookPart();
        var Workbook = WorkbookPart.Workbook = new Workbook();

        // Add Stylesheet.
        var WorkbookStylesPart = WorkbookPart.AddNewPart<WorkbookStylesPart>();
        WorkbookStylesPart.Stylesheet = GetStylesheet();
        WorkbookStylesPart.Stylesheet.Save();

        // Create worksheet.
        var WorksheetPart = Spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
        var Worksheet = WorksheetPart.Worksheet = new Worksheet();

        // Add data to worksheet.
        var SheetData = Worksheet.AppendChild(new SheetData());
        SheetData.AppendChild(new Row(
            new Cell() { CellValue = new CellValue(DateTime.Today.ToOADate().ToString(CultureInfo.InvariantCulture)), StyleIndex = 1 },
            // Only works for Office 2010+.
            new Cell() { CellValue = new CellValue(DateTime.Today.ToString("s")), DataType = CellValues.Date, StyleIndex = 1 }));

        // Link worksheet to workbook.
        var Sheets = Workbook.AppendChild(new Sheets());
        Sheets.AppendChild(new Sheet()
        {
            Id = WorkbookPart.GetIdOfPart(WorksheetPart),
            SheetId = (uint)(Sheets.Count() + 1),
            Name = "Example"
        });

        Workbook.Save();
    }
}

private static Stylesheet GetStylesheet()
{
    var StyleSheet = new Stylesheet();

     // Create "fonts" node.
    var Fonts = new Fonts();
    Fonts.Append(new Font()
    {
        FontName = new FontName() { Val = "Calibri" },
        FontSize = new FontSize() { Val = 11 },
        FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
    });

    Fonts.Count = (uint)Fonts.ChildElements.Count;

    // Create "fills" node.
    var Fills = new Fills();
    Fills.Append(new Fill()
    {
        PatternFill = new PatternFill() { PatternType = PatternValues.None }
        });
        Fills.Append(new Fill()
        {
            PatternFill = new PatternFill() { PatternType = PatternValues.Gray125 }
        });

    Fills.Count = (uint)Fills.ChildElements.Count;

    // Create "borders" node.
    var Borders = new Borders();
    Borders.Append(new Border()
    {
        LeftBorder = new LeftBorder(),
        RightBorder = new RightBorder(),
        TopBorder = new TopBorder(),
        BottomBorder = new BottomBorder(),
        DiagonalBorder = new DiagonalBorder()
    });

    Borders.Count = (uint)Borders.ChildElements.Count;

    // Create "cellStyleXfs" node.
    var CellStyleFormats = new CellStyleFormats();
    CellStyleFormats.Append(new CellFormat()
    {
        NumberFormatId = 0,
        FontId = 0,
        FillId = 0,
        BorderId = 0
    });

    CellStyleFormats.Count = (uint)CellStyleFormats.ChildElements.Count;

    // Create "cellXfs" node.
    var CellFormats = new CellFormats();

    // A default style that works for everything but DateTime
    CellFormats.Append(new CellFormat()
    {
        BorderId = 0,
        FillId = 0,
        FontId = 0,
        NumberFormatId = 0,
        FormatId = 0,
        ApplyNumberFormat = true
    });

   // A style that works for DateTime (just the date)
   CellFormats.Append(new CellFormat()
    {
        BorderId = 0,
        FillId = 0,
        FontId = 0,
        NumberFormatId = 14, // or 22 to include the time
        FormatId = 0,
        ApplyNumberFormat = true
    });

    CellFormats.Count = (uint)CellFormats.ChildElements.Count;

    // Create "cellStyles" node.
    var CellStyles = new CellStyles();
    CellStyles.Append(new CellStyle()
    {
        Name = "Normal",
        FormatId = 0,
        BuiltinId = 0
    });
    CellStyles.Count = (uint)CellStyles.ChildElements.Count;

    // Append all nodes in order.
    StyleSheet.Append(Fonts);
    StyleSheet.Append(Fills);
    StyleSheet.Append(Borders);
    StyleSheet.Append(CellStyleFormats);
    StyleSheet.Append(CellFormats);
    StyleSheet.Append(CellStyles);

    return StyleSheet;
}

嘗試指出它是CellValues.String類型,而不是CellValues.Date類型。

利用

DataType = new EnumValue<CellValues>(CellValues.String)   // good

代替

DataType = new EnumValue<CellValues>(CellValues.Date)     // bad

現在,將其添加為date而不進行ToString()轉換並使用CellValues.Date DataType 是有意義的——但CellValue() 僅將字符串作為參數。

[為什么,OpenXmlSDK,為什么??? 你是一個包裝器。 把東西包好。 讓它們隱形,讓我的生活更輕松。 :::嘆:::]

此外,如果目標單元格希望格式化一個日期,我們應該指出它是一個日期。

但我發現,雖然CellValues.StringCellValues.Date都按預期格式化(相同),但只有CellValues.Date會在加載時拋出“不可讀的內容”。

我對dt.ToOADate().ToString(new CultureInfo("en-US"));的任何變化完全沒有運氣。 方法——我最終得到一個五位數字,它在電子表格中顯示為五位數字,而它應該是一個格式化的日期。

添加字符串值時,我收到了相同的錯誤消息,但使用的是CellValues.Number數據類型。

嘗試dt.ToOADate().ToString().Replace (",", ".")而不是dt.ToOADate().ToString()

對於一些工作代碼示例,請參閱http://www.codeproject.com/KB/office/ExcelOpenXMLSDK.aspx

編輯:

請將您的代碼更改為:

dt.ToOADate().ToString(new CultureInfo("en-US"));

以下代碼可用於在電子表格中設置 DateTime 值:

Cell cell = GetRequiredCell(); // It returns the required Cell

DateTime dtValue = new DateTime(2012, 12, 8);

string strValue = dtValue.ToOADate().ToString().Replace(",", ".");
// decimal separator change it to "."

cell.DataType = new EnumValue<CellValues>(CellValues.Number);
cell.CellValue = new CellValue(strValue);
cell.StyleIndex = 1; 
private Cell CreateCellWithValue(DateTime columnValue, uint? styleIndex, string cellReference)
{
    Cell c = new Cell();
    c.DataType = CellValues.Number;
    c.CellValue = new CellValue(columnValue.ToOADate().ToString(new CultureInfo("en-US")));
    c.CellReference = cellReference;
    c.StyleIndex = styleIndex;

    return c;
}

例如,您可以使用日期列創建自己的 excel 文件。 然后,如果您使用生產力工具從 Open XML SDK 打開它,您會發現沒有為具有日期值的單元格指定數據DataType 這意味着您應該在創建日期單元格時省略DataType 在這種情況下,還需要將dt.ToOADate().ToString()作為單元格值傳遞。

以下對我們有用:

c.CellValue = new CellValue(datetimeValue).ToOADate().ToString());
c.DataType = CellValues.Number;
c.StyleIndex = StyleDate;

DataType設置為CellValues.Number ,然后確保使用CellFormats中的適當樣式索引格式化單元格。 在我們的例子中,我們在工作表中構建了一個樣式表, StyleDate是樣式表中CellFormats的索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM