经常会有项目需要把表格导出为 Excel 文件,或者是导入一份 Excel 来操作,那么如何在 C# 中操作 Excel 文件成了一个最基本的问题。

做开发这几年来,陆陆续续也接触过这样的需求,但因为不频繁,所以经常是遇到问题再去网上搜。最近的一个项目,要导出的这个 Excel 涉及了很多比较偏僻的操作,所以决定在这里开一篇文章,专门用来收集和整理使用到的代码,以及一些技巧。如果各位看官有一些自己的心得,或者有更好的方案,也欢迎交流。我会时不时更新一下。

 

0. 使用之前

在写代码之前,我们需要先添加引用,在 程序集 – 扩展 里面:Microsoft.Office.Interop.Excel。
还有要注意的是,引用之后,需要将属性中的「嵌入互操作类型」设置为 Flase,不然编译时可能会出错。
然后就是记得 using 啦:

using Microsoft.Office.Interop.Excel;
using System.Reflection;

▲ 这里的第二个 using 是因为在 Excel 操作中会经常用到一个 Missing.Value 的默认值,所以需要先引用 System.Reflection。

 

1. 开始使用

一般在使用中,我们只是操作一份 Excel 中的第一个工作表(sheet),下面来最简单的创建和读取一份 Excel 中的第一个 sheet。

// 定义一个 Missing 的值,方便后面使用
Missing miss = Missing.Value; // 创建 Excel,并制定是不可见的
ApplicationClass excel = new ApplicationClass();
excel.Visible = false; // 新建一份电子表格,或者打开现有的文件
Workbook wb = excel.Workbooks.Add();
Workbook wb = excel.Workbooks.Open("demo.xls"); // 取得到第一个工作表,或者取得当前默认的工作表
Worksheet ws = wb.Sheets[1] as Worksheet;
Worksheet ws = wb.ActiveSheet as Worksheet;

▲ 注意,在 Excel 的操作中,许多时候,索引是从 1 开始的,而不是 0,这和大多数程序语法有区别。

 

2. 使用之后

既然已经把 Excel 都创建出来了,我们就先来说说使用之后的结束,以及保存文件,需要注意的问题。这就好比在写代码,两个花括号都是同时敲的,过后再来写里面的代码。

许多资料中,Excel 使用之后都直接就 excel = null; 来结束代码,这些朋友难道没发现,这样会在系统中留下许多 EXCEL.EXE 的进程吗?如下图:

如果这是在用户的客户端,可能会因为关机,而把这个问题忽略。但如果是在服务器上生成 Excel 文件,一个用户生成一次,就产生一个进程,那么后果可想而知。所以我们要来先说说使用之后怎么结束 Excel 的进程。

结束 Excel 不能单单把 EXCEL.EXE 结束就好,这样的话,如果用户正好打开了一个 Excel 也会被结束掉。

下面是正确结束 Excel 的代码:

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID); // 结束 Excel 进程
public static void KillExcel(Application excel)
{
IntPtr t = new IntPtr(excel.Hwnd);
int k = 0;
GetWindowThreadProcessId(t, out k);
System.Diagnostics.Process p = System.Diagnostics.Process.GetProcessById(k);
p.Kill();
}

▲ 注意 DllImport 需要 using System.Runtime.InteropServices 。

接下来就是比较保险的关闭,以及调用上面的代码:

// 关闭电子表格,释放资源
wb.Close();
wb = null; // 退出 Excel,释放资源
excel.Quit();
KillExcel(excel);
excel = null;

 

3. 保存时的格式问题

「文件格式和扩展名不匹配。文件可能已损坏或不安全。」看起来似乎很严重,尤其对一些电脑小白来说,不安全这个词很耀眼。这个问题,可能很多朋友在做 Excel 导出的时候,都会遇到,包括我自己使用一些软件也遇过,绝大多数都没有对这个问题进行处理,觉得让用户点一下「是」就好了。但对于我这种相对注重用户体验的开发者来说,这样当然是不行的。那么到底是什么造成的?

其实解决这个问题非常简单,之所以会出现这个问题,是和 Office Excel 的版本有关系。我们都知道 Excel 有一个 97-2003 的格式,就是最常见的 .xls 文件,除了这个还有一种 .xlsx 文件,这种是自 Office Excel 2007 之后有的新格式,除了这些,Excel 还支持把表格保存为 .xml 甚至是纯文本的格式。而我们用程序在生成 Excel 的时候,考虑到国内还有一大批 Office 2003 的使用者,所以我们都会保存为 .xls 以便更好的兼容他们。大部分都是像下面的代码这个保存的:

// 保存
wb.SaveAs("demo.xls");

虽然你的保存路径中有包含 .xls 后缀名,但其实这个时候,Excel 并不知道你是要以什么格式去保存,所以它可能是无格式的,或者是当前系统中 Office 版本的默认格式。

现在我们来看看 SaveAs 的参数中,会发现还有第二个参数 FileFormat,顾名思义,就是文件格式,正好是我们要的参数,所以我们只要告诉 Excel 要保存的格式,问题迎面而解:

// 保存,格式编码为56(xls)(.xlsx 的编码为51)
wb.SaveAs("demo.xls", 56);

现在再打开生成的 demo.xls 文件,会发现直接就打开了,不会再出现上面的问题了。

 

4. 常用的格式设置

在操作 Excel 的时候,除了上面这些基础问题,还有就是一些常见的格式设置。包括字体字号、粗体、合并单元格、垂直居中、横向居中、行高、列宽、单元格格式,边框样式,等等。下面的代码就包含了这些常用的设置:

// 选择一块区域(一个或多个单元格之间)
Range range = ws.get_Range(ws.Cells[1, 1], ws.Cells[2, 10]); // 设定单元格格式,@是指文本格式(导出一串长数字时,例如手机号码,会被处理为数字,所以我们要强制为文本)
range.NumberFormat = "@"; // 合并单元格
range.MergeCells = true; // 设置行高、列宽
range.RowHeight = 35;
range.ColumnWidth = 100; // 设置字体字号、粗体、字体(还有大多数字体相关的都在 Font 属性中)
range.Font.Size = 12;
range.Font.Bold = true;
range.Font.Name = "楷体"; // 横向居中、垂直居中
range.HorizontalAlignment = XlHAlign.xlHAlignCenter;
range.VerticalAlignment = XlHAlign.xlHAlignCenter; // 设置边框
range.Borders.LineStyle = 1;
range.Borders.LineStyle = XlLineStyle.xlContinuous; // 给单元格设置值(内容)
range.set_Value(miss, "abel.cnblogs.com");

比较麻烦的是,每操作一个区域,我们就要重新设定一次 Range,这相当于在 Excel 实际做了一次选择某些单元格的操作,所以数据量大的话,生成 Excel 的速度会有点慢。

 

5. 常用的打印设置

这部分可能在网上比较少见,但一些项目中也会有相关的需求,比如要默认横向纸张啦,打印标题行啦(就是不管打印到第几页,都会出现这一行,一般是表格第一行)等等,我们来看看代码吧:

// 设置横向纸张
ws.PageSetup.Orientation = XlPageOrientation.xlLandscape; // 设置打印标题的范围
ws.PageSetup.PrintTitleRows = "$3:$3";

 

6. 其它设置

还有一些其它的设置和操作,暂时先都整理在这吧。

// 设置电子表格的名称
ws.Name = "Hello C#";

 

7. 关于生成速度慢的解决方案…

上面我们有提到,使用程序来生成 Excel,遇到数据量大的话(十万级的数据就足够了),会比较慢的问题,这个怎么破?

其实我们可以不使用 Excel 操作类来生成,而是直接用 IO 来生成 xml 格式的 Excel 表格,最后保存为 .xls 文件即可,速度可以提高N倍。当然,会出现上面第三点说的问题。

那有没有速度又快,又不会出现那个安全提示的方法呢?也是有的,来看看代码:

// 打开 XML 格式的 Excel 文件
Workbook wb = excel.Workbooks.OpenXML("temp.xml"); // 再保存为真正 xls 格式的 Excel 文件
wb.SaveAs("demo.xls", 56);

是的,方法就是先打开一份用 IO 生成好的 xml 格式的 Excel,再另存为 Office 97-2003 格式的 xls 文件。

注意,这个方法只适合格式简单的 Excel,不然在保存的时候,会提示兼容问题!

 

最后

准备写这篇文章,已经计划了好几天,一直到昨晚才动笔,花了两个晚上,大部分代码在写文章的时候,还重新在 Visual Studio 中实践了一次,花了两个晚上写完。除了方便自己以后用,也希望能帮到有需求的朋友。

如果你也有这方面的经验,或者技巧,或者觉得文章中有什么问题,非常欢迎一起交流!

C# 操作 Excel 常见问题收集和整理(定期更新,欢迎交流)的更多相关文章

  1. C# 操作 Excel 常见问题收集和整理

    C# 操作 Excel 常见问题收集和整理(定期更新,欢迎交流) 经常会有项目需要把表格导出为 Excel 文件,或者是导入一份 Excel 来操作,那么如何在 C# 中操作 Excel 文件成了一个 ...

  2. 转-C# 操作 Excel 常见问题收集和整理

    经常会有项目需要把表格导出为 Excel 文件,或者是导入一份 Excel 来操作,那么如何在 C# 中操作 Excel 文件成了一个最基本的问题. 做开发这几年来,陆陆续续也接触过这样的需求,但因为 ...

  3. C#操作Excel开发报表系列整理(转)

    C#操作Excel进行报表开发系列共写了七篇,也已经有很久没有新东西了,现在整理一下,方便以后查阅,如果有写新的,会同时更新.需要注意的是因为Office的版本不同,实际的代码可能会有所不同,但是都是 ...

  4. C#操作Excel开发报表系列整理

    C#操作Excel进行报表开发系列共写了八篇,也已经有很久没有新东西了,现在整理一下,方便以后查阅,如果有写新的,会同时更新.需要注意的是因为Office的版本不同,实际的代码可能会有所不同,但是都是 ...

  5. POI 操作 excel表格 (简单整理)

    简单的整理: import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import ...

  6. C++ builder 操作Excel方法(据网上资料整理)

    c++ builder 操作Excel方法,下面是从网上找到的一些不错的方法,学习一下: 用OLE操作Excel(目前最全的资料)(04.2.19更新) 本文档部分资料来自互联网,大部分是ccrun( ...

  7. python操作excel表格(xlrd/xlwt)

    最近遇到一个情景,就是定期生成并发送服务器使用情况报表,按照不同维度统计,涉及python对excel的操作,上网搜罗了一番,大多大同小异,而且不太能满足需求,不过经过一番对源码的"研究&q ...

  8. 【转】经典!python中使用xlrd、xlwt操作excel表格详解

    最近遇到一个情景,就是定期生成并发送服务器使用情况报表,按照不同维度统计,涉及python对excel的操作,上网搜罗了一番,大多大同小异,而且不太能满足需求,不过经过一番对源码的"研究&q ...

  9. 【转】python操作excel表格(xlrd/xlwt)

    [转]python操作excel表格(xlrd/xlwt) 最近遇到一个情景,就是定期生成并发送服务器使用情况报表,按照不同维度统计,涉及python对excel的操作,上网搜罗了一番,大多大同小异, ...

随机推荐

  1. C# 二进制字节流查找函数IndexOf

    C# 二进制字节流查找函数IndexOf /// <summary> /// 报告指定的 System.Byte[] 在此实例中的第一个匹配项的索引. /// </summary&g ...

  2. C#缩放和裁剪图片

    在GDI+中,缩放和剪裁可以看作同一个操作,无非就是原始区域的选择不同罢了.空口无凭,先看具体算法可能更好理解. using System; using System.Collections.Gene ...

  3. Microsoft.AlphaImageLoader滤镜解说

    Microsoft.AlphaImageLoader是IE滤镜的一种,其主要作用就是对图片进行透明处理.尽管FireFox和IE7以上的IE浏览器已经支持透明的PNG图片,可是就IE5-IE6而言还是 ...

  4. [Angular2 Router] Use Params from Angular 2 Routes Inside of Components

    Angular 2’s ActivatedRoute allows you to get the details of the current route into your components. ...

  5. 异常处理之namespace找不到

    最近在做控件的时候遇到了这么一个异常 就是说类库中明明就有命名空间,但是在winForm中引用的时候也能正常显示, 可是编译的时候却报 找不到指定的命名空间 这样的异常,头痛了好久,结果看到了winF ...

  6. js的2种继承方式详解

    js中继承可以分为两种:对象冒充和原型链方式 一.对象冒充包括三种:临时属性方式.call()及apply()方式1.临时属性方式 复制代码代码如下: function Person(name){   ...

  7. 1.4.2 solr字段类型--(1.4.2.5)使用枚举字段

    1.4.2 solr字段类型 (1.4.2.1) 字段类型定义和字段类型属性. (1.4.2.2) solr附带的字段类型 (1.4.2.3) 使用货币和汇率 (1.4.2.4) 使用Dates(日期 ...

  8. Tested work with China Digiprog 3 4.94 mileage programmer

    I was thinking about buying a Digiprog3 clone from China I know that YANHUA Digiprog 3 is the best a ...

  9. 补充:tableView优化总结

    tableView优化方式总结 (说明:一下只是我自己的小总结,会有不足,望大神指正,多谢大家了) 1 缓存 cell 的行高.目的方便第二遍访问 cell. 2 减少图片的blend 操作.包括:设 ...

  10. 重构9-Extract Interface(提取接口)

    我们来介绍一个常常被忽视的重构:提取接口.如果你发现多于一个类使用另外一个类的某些方法,引入接口解除这种依赖往往十分有用.该重构实现起来非常简单,并且能够享受到松耦合带来的好处. public cla ...