【问题标题】:Write IEnumerable<object> to Excel, File isn't editable in Excel web将 IEnumerable<object> 写入 Excel,文件在 Excel Web 中不可编辑
【发布时间】:2021-04-13 15:23:26
【问题描述】:

我已经编写了这个类来将任何类型的集合写入一个 excel 文件:

public static class ExcelWriter
{
    public static void WriteToExcelFile(IEnumerable<object> collection, string filePath)
    {
        if (collection?.Any() != true || String.IsNullOrWhiteSpace(filePath))
            return;

        if (File.Exists(filePath))
            File.Delete(filePath);

        using (var document = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
        {
            WorkbookPart workbookPart = document.AddWorkbookPart();
            workbookPart.Workbook = new Workbook();

            WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
            worksheetPart.Worksheet = new Worksheet();

            Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());

            Sheet sheet = new Sheet()
            {
                Id = workbookPart.GetIdOfPart(worksheetPart),
                SheetId = 1,
                Name = "Sheet1"
            };

            sheets.Append(sheet);

            workbookPart.Workbook.Save();

            SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());

            sheetData.AppendChild(generateHeaderRow(collection.First().GetType()));

            foreach (var item in collection)
            {
                sheetData.AppendChild(generateValuesRow(item));
            }

            worksheetPart.Worksheet.Save();
        }
    }

    private static Row generateHeaderRow(Type dataType)
    {
        var propertyNames = dataType.GetProperties().Select(p => p.Name).ToList();

        var headerCells = new Cell[propertyNames.Count];
        for (int i = 0; i < propertyNames.Count; i++)
        {
            headerCells[i] = createCell(propertyNames[i], CellValues.String);
        }

        return new Row(headerCells);
    }

    private static Row generateValuesRow(object rowData)
    {
        var cells = new List<Cell>();

        foreach (var property in rowData.GetType().GetProperties())
        {
            var propertyValue = property.GetValue(rowData);
            cells.Add(createCell(propertyValue.ToString(), getCellValueByType(propertyValue)));
        }

        return new Row(cells);
    }

    private static CellValues getCellValueByType(object propertyValue)
    {
        string propertyValueString = propertyValue.ToString();
        if (Double.TryParse(propertyValueString, out _))
        {
            return CellValues.Number;
        }
        if (DateTime.TryParse(propertyValueString, out _))
        {
            return CellValues.Date;
        }
        if (Boolean.TryParse(propertyValueString, out _))
        {
            return CellValues.Boolean;
        }

        return CellValues.String;
    }

    private static Cell createCell(string value, CellValues dataType)
    {
        return new Cell()
        {
            CellValue = new CellValue(value),
            DataType = new EnumValue<CellValues>(dataType)
        };
    }
}

这实际上生成了 excel 文件,但是当我在 Excel web 中打开该文件时,它会以查看模式打开并说您需要修复它才能进行编辑(虽然 Excel 无法修复它):

我更改了我的代码以使用特定类型并且它没有这个问题。该代码如下所示:

public class TypeDependentWriter
{
    public static void WriteToExcelFile(IEnumerable<TestDataType> collection, string filePath)
    {
        if (collection?.Any() != true || String.IsNullOrWhiteSpace(filePath))
            return;

        if (File.Exists(filePath))
            File.Delete(filePath);

        using (var document = SpreadsheetDocument.Create(filePath, SpreadsheetDocumentType.Workbook))
        {
            WorkbookPart workbookPart = document.AddWorkbookPart();
            workbookPart.Workbook = new Workbook();

            WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
            worksheetPart.Worksheet = new Worksheet();

            Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());

            Sheet sheet = new Sheet()
            {
                Id = workbookPart.GetIdOfPart(worksheetPart),
                SheetId = 1,
                Name = "Sheet1"
            };

            sheets.Append(sheet);

            workbookPart.Workbook.Save();

            SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());

            // Header row.
            Row row = new Row();

            row.Append(
                createCell("Id", CellValues.String),
                createCell("Name", CellValues.String),
                createCell("Birth Date", CellValues.String));

            sheetData.AppendChild(row);

            // Data rows.
            foreach (var item in collection)
            {
                row = new Row();

                row.Append(
                    createCell(item.Id.ToString(), CellValues.Number),
                    createCell(item.Name, CellValues.String),
                    createCell(item.DateOfBirth.ToString("yyyy/MM/dd"), CellValues.String));

                sheetData.AppendChild(row);
            }

            worksheetPart.Worksheet.Save();
        }
    }

    private static Cell createCell(string value, CellValues dataType)
    {
        return new Cell()
        {
            CellValue = new CellValue(value),
            DataType = new EnumValue<CellValues>(dataType)
        };
    }
}

我怎样才能使第一个代码工作?

【问题讨论】:

  • 我建议你使用这两个代码库生成一个基于TestDataType的文件,然后在BeyondCompare(或类似工具)中打开这两个文件,看看它们的不同之处。

标签: c# excel openxml


【解决方案1】:

我的第二个代码没有产生问题的原因是这部分:

row.Append(
    createCell(item.Id.ToString(), CellValues.Number),
    createCell(item.Name, CellValues.String),
    createCell(item.DateOfBirth.ToString("yyyy/MM/dd"), CellValues.String /* I'm using string for DateTime here. */));

我认为我应该:

  • 使用CellValues.Number 作为日期。
  • 并在调用 ToString() 之前对 DateTime 变量调用 .ToOADate() 扩展方法。
  • 我应该定义一个单元格样式并将该样式应用于存储日期的单元格。

所以我现在以这种方式创建日期单元格:

var dateCell = new Cell()
{
    CellValue = new CellValue(((DateTime)propertyValue).ToOADate()
        .ToString(CultureInfo.InvariantCulture)),
    DataType = new EnumValue<CellValues>(CellValues.Number),
    StyleIndex = 1 /* Style index for date cells */
}

日期单元格的样式可以这样定义(我取自Dave Williams post):

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);

但是,如果您没有任何其他定义的填充、字体...这将不起作用,因为我们正在使用我们尚未定义的FillId = 0 等,您可以定义的最小样式为也可以在Dave Williams post 上找到这项工作:

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;
}

【讨论】:

    猜你喜欢
    • 2017-09-19
    • 1970-01-01
    • 2013-10-14
    • 2021-12-22
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 2018-01-17
    • 2012-02-25
    相关资源
    最近更新 更多