65.9K
CodeProject 正在变化。 阅读更多。
Home

一个非常易于使用的 Excel XML 导入-导出库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.18/5 (86投票s)

2008年1月15日

CPOL

13分钟阅读

viewsIcon

693380

downloadIcon

19697

Excel XML 格式的导入导出库,可将程序员的工作量降至最低。

最新版本 [外部链接]

引言

就像其他任何 Excel XML 导出文章/库的作者一样,当我需要将一个简单的报表导出到 Excel 时,我的探索就开始了。我知道编写一个 Excel XML 库非常容易,并且我确信能在互联网上找到许多这样的库。我确实找到了很多,但没有一个像我想要的那么简单、直观、强大,并且实现了我需要的一切。编写一个导出库这样的后台组件可能需要很长时间,但当使用它时,我希望它能快速、无忧地完成我的工作。该库还应与 .NET 2.0/3.0/3.5 兼容。

因此,我推出了这个库,它易于使用且快速,同时功能强大,堪称目前最强大的 XML 导出库之一。好吧,我承认这是无耻的自我推销。

附注:XML 文件格式仅被 Excel XP、2003 和 2007 版本支持。早期版本,即 Excel 97 和 Excel 2000 不支持此功能。

特点

该库具有多种功能,它们是...

  • 完全导入和导出 Excel XML 文件
  • 访问任何位置的单元格,包括 ExcelXmlWorkbookWorksheetRowCell
  • 完全支持公式和范围
  • 导入文件的公式和范围将被解析为 FormulaRange 实例
  • ExcelXmlWorkbookWorksheetRowCell 提供大量的插入、删除和添加函数
  • 通过任何插入、删除和添加函数进行的任何更改,都能自动管理工作簿中的单元格引用
  • 完全支持 WorksheetRowCell 甚至 Range 中的样式
  • 无需实例声明;所有繁琐的工作都由库完成
  • 单元格 ContentType,可用于了解单元格实际包含的内容
  • DataSetExcelXmlWorkbook 的转换功能
  • 绝对和非绝对范围以及命名范围
  • 自动筛选
  • 非常快速,因为所有操作都通过 XmlWriter 完成
  • 能够冻结行或列,或两者都冻结
  • 几乎所有的打印选项
  • 几乎所有的样式选项
  • 多种格式选项
  • 隐藏列和行

使用库

使用代码非常简单。这是我在构建此库时最关心的问题。主或顶层类是 ExcelXmlWorkbook,它包含多个 Worksheet。该库位于 Yogesh.Extensions.ExcelXml 中。以下示例展示了在创建实例时向工作簿添加单元格的各种方法。

// Create the instance
ExcelXmlWorkbook book = new ExcelXmlWorkbook();

// Many such properties exist. Details can be found in the documentation
book.Properties.Author = "Yogesh Jagota"; // The author of the document

// This returns the first worksheet.
// Note that we have not declared a instance of a new worksheet
// All the dirty work is done by the library.
Worksheet sheet = book[0];

// Name is the name of the sheet. If not set, the default name
// style is "sheet" + sheet number, like sheet1, sheet2
sheet.Name = "AgewiseOutstanding";

// More on this in documentation
sheet.FreezeTopRows = 3;

// and this too...
sheet.PrintOptions.Orientation = PageOrientation.Landscape;
sheet.PrintOptions.SetMargins(0.5, 0.4, 0.5, 0.4);

// This is the actual code which sets out the cell values
// Note again, that we don't declare any instance at all.
// All the work is done by the library.
// Index operator takes first value as column and second as row.
sheet[0, 0].Value = "Outstanding as on " + DateTime.Now;

sheet[0, 1].Value = "Name of Party";
sheet[1, 1].Value = "RSM";
sheet[2, 1].Value = "ASM";
sheet[3, 1].Value = "0-30";
sheet[4, 1].Value = "31-60";
sheet[5, 1].Value = "61-90";
sheet[6, 1].Value = "91+";

sheet[0, 2].Value = "M/s Stupid Paymaster";
sheet[1, 2].Value = "Mr. Nonsense";
sheet[2, 2].Value = "Mr. More Nonsense";
sheet[3, 2].Value = 0;
sheet[4, 2].Value = 5000;
sheet[5, 2].Value = 45000;
sheet[6, 2].Value = 0;

sheet[0, 3].Value = "M/s Good Paymaster";
sheet[1, 3].Value = "Mr. Good RSM";
sheet[2, 3].Value = "Mr. Good ASM";
sheet[3, 3].Value = 32000;
sheet[4, 3].Value = 0;
sheet[5, 3].Value = 0;
sheet[6, 3].Value = 0;
sheet[7, 3].Value = sheet[6, 3];

string outputFile = "Outstanding File.xml";
// no extension is added if not present

book.Export(outputFile);

导入文件

要导入文件,您可以将文件名或 Stream 对象传递给静态的 ExcelXmlWorkbook.Import 方法,该方法将返回一个加载了文件的 ExcelXmlWorkbook 实例。如果发生任何错误,该函数将简单地返回 null,并且无法得知发生了什么错误。因此,首选的导入方式是通过 Stream,因为它能更好地控制错误管理。

导出文件

所有代码仅在调用 Export 函数时才写入磁盘。Export 必须提供一个 Stream 或文件名。如果发生任何错误,该函数将简单地返回 false,并且无法得知发生了什么错误。因此,首选的导出方式是通过 Stream,因为它能更好地控制错误管理。

为单元格赋值

请注意上一个示例中的最后一次赋值。

sheet[7, 3].Value = sheet[6, 2];

在这里,我们实际上是在将一个单元格分配给另一个单元格。您可能会想,这个单元格的值是什么?该单元格根本没有值。它将拥有对所分配单元格的引用,当您在 Excel 中打开文件时,会显示类似这样的内容:=G3。它不是绝对引用,稍后会详细介绍。我们可以为单元格分配这些值:

  1. 字符串
  2. bool
  3. 所有整数类型,即 bytesbyteintuintlongulongfloatdoubledecimal
  4. 日期时间
  5. Cell
  6. Formula [稍后详细介绍]

了解单元格包含的内容类型

每个 Cell 都包含一个只读的 ContentType 字段,可用于检查单元格包含的值类型。可用的值有 StringNumberBooleanDateTimeFormulaUnresolvedValue

从单元格检索值

只读属性 GetValue<T> 返回转换为所提供类型的单元格值。您可以将 ContentTypeGetValue<T> 结合使用来检索单元格的确切值。GetValue<T> 在检索单元格值时启用严格的类型检查。此外,如果与 GetValue<T> 提供的类型与单元格类型不匹配,则会返回 default(T)。例如,如果单元格的 ContentType == ContentType.Numeric,检索该单元格值的唯一方法是提供 bytesbyteintuintlongulongfloatdouble。如果单元格的 ContentType == ContentType.String,检索该单元格值的唯一方法是提供 string 等。

访问单元格的各种方法

没有硬编码的方法来访问特定单元格。有多种方法可以做到这一点。例如,最后一个示例中第二行的第四列可以通过以下方式设置为值 1:

  1. 直接使用 ExcelXmlWorkbook 类。
  2. book[0][3, 1].Value = 1
  3. 使用 Worksheet 类。
  4. Worksheet sheet = book[0];
    sheet[3, 1].Value = 1
  5. 使用 Row 类。
  6. Worksheet sheet = book[0];
    Row row = sheet[1];
    row[3].Value = 1
  7. 使用 Cell 类。
  8. Worksheet sheet = book[0];
    Row row = sheet[1];
    Cell cell = row[3];
    cell.Value = 1

请注意,我们不需要声明一个新的工作表、行或单元格实例。所有繁琐的工作都由库完成。这种编码风格为访问单元格和行提供了许多方法。

样式

所有单元格、行和工作表都有可以单独设置的样式。它们是 FontAlignmentInteriorBorderDisplayFormat。有关样式类成员的更多信息,请参阅文档。更改工作表样式设置会影响工作表中的所有单元格。行设置会影响行中的所有子单元格,而单个单元格设置则影响该单元格本身。示例

sheet[1, 3].Font.Bold = true;

样式的全部功能都实现在 XmlStyle 类中。您可以在代码中创建一个 XmlStyle 实例,并将其分配给所有单元格、行和工作表中存在的 Style 属性。示例

XmlStyle style = new XmlStyle();
style.Font.Bold = true;
sheet[1, 3].Style = style;

范围

我编写自己实现的主要原因是范围,我发现它缺失了,或者没有范围应有的强大功能。在这个库中,范围非常强大且可扩展。范围具有单元格、行和工作表中找到的所有样式元素。示例

// This sets the text of cells 1-8 of row 3 to bold
Range range = new Range(sheet[0, 2], sheet[7, 2]);
range.Font.Bold = true;

即使是这样的代码也是有效的,尽管许多人可能会推荐使用第一种方式...

new Range(sheet[0, 2], sheet[7, 2]).Font.Bold = true;

请注意,范围不能分配给单元格值。分配它将生成一个空单元格。一个范围可以包含单个单元格或一系列单元格。在上面的示例中,我们向构造函数提供了第一个单元格和最后一个单元格。范围始终包含矩形范围,就像在 Excel 中一样。

将自动筛选应用于范围

要将自动筛选应用于范围,只需调用范围的 AutoFilter 方法即可完成。示例

new Range(sheet[0, 1], sheet[6, 3]).AutoFilter();

绝对和非绝对范围

默认情况下,所有范围都输出非绝对引用。要设置绝对引用,只需将范围的 Absolute 属性设置为 true

Range range = new Range(sheet[0 ,2], sheet[7, 2]);
range.Font.Bold = true;
range.Absolute = true;

函数

现在,我们来谈谈范围及其 Absolute 属性的实际用途:添加函数。我认为可以使用本文第一个示例的这个例子轻松理解我库中的一个函数。

sheet[7, 3].Value = FormulaHelper.Formula("sum", new Range(sheet[3, 3], sheet[6, 3]));

sheet[7, 3].Value = new Formula().Add("sum").StartGroup().Add(
            new Range(sheet[3, 3], sheet[6, 3])).EndGroup();

当您在 Excel 中打开此工作簿时,该单元格的值将是 =SUM(D4:G4)

函数参数

在这里,我们在公式构造函数中添加了一个参数。您可以使用 Formula 类的 Add 函数添加任意数量的参数。但是只允许两种类型的参数:stringRangestring 参数类型可用于添加任何值以及命名范围(有关命名范围的更多信息,请参阅文档)。示例

Formula formula = new Formula().Add("sum").StartGroup();

formula.Add("D4").Operator(',');

// Here I am using the object initializers just to fit the code in one line
// The library is compatible with both VS2005 and VS2008
formula.Add(new Range(sheet[4, 3]) { Absolute = true } ).Operator(',');

formula.Add(new Range(sheet[5, 3], Range(sheet[6, 3])).EndGroup();

sheet[7, 3].Value = formula;

当您在 Excel 中打开此工作簿时,该单元格的值将是 =SUM(D4, $E$4, F4:G4)

通过检查单元格值或样式来过滤作为参数的单元格

您可以将一个参数(即委托,它接受 Cell 作为其值并返回 bool)传递给 Formula 构造函数或 Add 方法,从而筛选所有单元格并将它们自动添加到公式的参数列表中。可以检查所有值访问器(即 ValueIntValue 等)和单元格样式。示例

  • 示例 1
  • // Lets assume column 1,2,3,6 and 7 are bold...
    XmlStyle style = new XmlStyle();
    style.Font.Bold = true;
    
    // VS2008 style
    sheet[7, 3].Value = FormulaHelper.Formula("sum", 
                        new Range(sheet[0, 3], sheet[6, 3]), 
                        cell => cell.Style == style);
    
    // or VS2005 style
    sheet[7, 3].Value = FormulaHelper.Formula("sum", 
                        new Range(sheet[0, 3], sheet[6, 3]), 
                        delegate (Cell cell) { return cell.Style == style; } );
  • 示例 2
  • sheet[7, 3].Value = FormulaHelper.Formula("sum", 
                        new Range(sheet[0, 3], sheet[6, 3]), 
                        cell => cell.GetValue<int>() > 10000 && 
                        cell.GetValue<int>() <= 50000);

在第一个样式示例中,单元格的值将是 =SUM(A4:C4, F4:G4)。连续范围匹配为 true 将合并为一个参数,即 A4:C4,而不是三个参数,即 A4,B4,C4

修改导入的 Excel XML 文件

导入的 Excel XML 文件可以直接通过直接赋值进行修改,就像新文件一样。此外,还有许多函数允许插入、删除和添加...

  • 工作簿中的一个或多个工作表,例如 InsertSheetBeforeInsertSheetAfter
  • 工作表中的一个或多个行和列,例如 InsertColumnAfterInsertColumnsAfterInsertRowBeforeInsertRowsBefore
  • 行中的一个或多个单元格,例如 InsertCellBeforeInsertCellsBefore

有关这些函数的更多信息,请参阅文档。

将 DataSet 导出到 ExcelXmlWorksheet

ExcelXmlWorksheet 中的一个静态成员 DataSetToWorkbook 用于转换 DataSet 并返回一个 Worksheet 引用。所有表都将转换为工作簿的不同工作表。

用法

ExcelXmlWorksheet sheet = ExcelXmlWorksheet.DataSetToWorkbook(sourceDataSet)

单元格集合

单元格集合是一个强类型 List 集合,完全支持 LINQ。您可以使用 Add 方法添加 WorksheetRangeRowCell。您可以添加所有单元格,也可以使用谓词筛选单元格。

内存

查看所有这些代码可能会让您认为所有单元格、行、工作表、范围都会占用太多内存。它们也一定有自己的样式副本,这会造成额外的开销。答案是否定的。

我已经优化了库,使其尽可能少地占用内存。至于样式,如果您有一个通过编程方式编写的包含 100,000 个单元格的工作簿,其中只有 10 个单独的样式,那么内存中的样式数量将只有 11 个,即 10 个单独的样式 + 1 个默认样式。虽然样式是在工作簿级别添加的,但如果您有 10 个工作簿,并且它们都包含相同的 10 个样式,那么程序中活动的样式实例数量将是 110 个。

结论

我很乐意听取您的评论和建议。任何错误都可以在此处报告。

更新

v3.29 中的代码重大更改

已删除 PrintOptions 中的 SetHeaderFooterMargin 方法。请改用 HeaderMarginFooterMargin 属性。

v3.06 中的代码重大更改

公式系统的工作方式与以前不同,因此之前的代码可能会中断。为了向后兼容,我包含了一个静态类 FormulaHelper,可用于旧代码。只需按以下方式替换旧代码:

cell.Value = new Formula("Sum", new Range(sheet[3, 3], sheet[6, 3]));

cell.Value = FormulaHelper.Formula("Sum", new Range(sheet[3, 3], sheet[6, 3]));

历史

  • [2008年11月26日] Revision 3.35
    • 修复了 GUID 类型存储中的一个错误。
    • 修复了 ContentType.Time 格式生成中的一个错误。
  • [2008年11月19日] Revision 3.33
    • 添加了 GUID 单元格内容类型。
    • 现在 GetValue<string> 将几乎所有内容转换为字符串类型,包括新添加的 GUID 单元格内容类型。
    • 修复了 SetHeaderSetFooter 中空字符串导致的问题。
    • 修复了新的 IsRawContent 属性的错误。
  • [2008年11月12日] Revision 3.29
    • 增加了对制表符和逗号分隔导出的支持。
    • 修复了 WorkbookAdd(string sheetName) 方法中的一个错误,该方法添加了两个工作表而不是一个。
    • 修复了 WorksheetImportTable 方法中的一个错误,该方法中的列索引使用了基于一的索引。
    • 修复了合并单元格跨越多行导致 Excel 中表格出错的错误。
    • 修复了 DateTime 值以 12 小时格式保存,但未考虑 AM/PM 并且始终保存为 AM 的错误。
    • HeaderMarginFooterMargin 字段转换为属性,并删除了 SetHeaderFooterMargin 方法。
    • 添加了 HeaderFooter 字符串属性。
    • 添加了多部分标题和页脚字符串助手,即 SetHeaderSetFooter
    • 增加了打印网格线支持(EnableGridlines 属性)。
    • 将许多内部值重置为 private,并清理了项目。
    • 添加了货币显示格式。
    • 修复了 GetValue 未检索到十进制值的错误。
    • 将日期的默认显示格式设置为 GeneralDate
  • [2008年7月23日] Revision 3.06
    • 实现了新的公式系统。
    • 为单元格添加了 RowSpanColumnSpan 属性。
    • 修复了命名范围重命名中的一个小错误。
    • 如果设置了冻结行,则冻结列不起作用。已修复。
    • 样式未在列导出中保存。已修复。
    • 程序集文件包含有关此库的错误信息。已修复。
    • 创建新的 XmlWriter 时,工作簿导出可能会引发 NullReferenceException。已修复。
    • 记录了 IStyle 接口。
  • [2008年6月10日] Revision 2.89
    • 增加了打印区域支持。
    • 向 Worksheet 添加了 TabColor 属性。
    • 向单元格添加了引用 (HRef) 支持。
    • 增加了对自定义显示格式的支持。
    • 增加了对单元格图案的支持。
    • 修复了当 "Long Date"、"Short Date"、"Time" 和 "@" 作为 DisplayFormat 时引发异常的错误。
    • 修复了数字格式问题,其中一些国际格式未正确保存。
  • [2008年4月3日] Revision 2.82
    • 修复了单个单元格合并错误。
  • [2008年3月19日] Revision 2.81
    • 修复了多工作表导入错误。
  • [2008年3月10日] Revision 2.80
    • 增加了十进制支持。
    • 修复了将 0 分配给单元格时仍然会引发异常的错误。
  • [2008年3月6日] Revision 2.79
    • 增加了单元格合并/取消合并支持。
    • 为工作表、行和范围添加了 GetEnumerator 支持。
    • 添加了 CellCollection 类。
    • 修复了单元格的数字输出包含全局数字格式而不是仅限美国格式的错误。感谢 Reinhard。
    • 添加了六种新的显示格式类型,并删除了 Custom 格式类型。
    • 向单元格添加了 Index 属性,它还有一个 ExcelColumnIndex 属性,返回 Excel 格式的列,例如 A、AA、AC、FA。
  • [2008年2月28日] Revision 2.45
    • 修复了 GetValue<T> 不接受 stringDateTime 类型的错误。感谢 Karl 指出这一点。
  • [2008年2月21日] Revision 2.44
    • 修复了将 0 分配给单元格时引发异常的错误。感谢 Ralf 指出这一点。
  • [2008年2月20日] Revision 2.43
  • [2008年1月19日] Revision 1.30
    • 添加了完整的文档。
    • 添加了 DataSet 导出功能。
    • 添加了范围自动筛选。
    • 添加了打印行和列标题
    • 更改了一些名称以移除 FxCop 报告的警告(ExcelXmlWorkBook 改为 ExcelXmlWorkbookWorkSheet 改为 WorksheetCellCompareDelegate 改为 CellCompare)。
    • CellCompare 现在位于 Yogesh.Extensions.ExcelXml 中,而不是 Yogesh.Extensions.ExcelXml.Formula
    • 添加了单元格注释。
  • [2008年1月16日] Revision 1.0
    • 在 The Code Project 上首次发布。
© . All rights reserved.