最近,因为有需求,找了很多C#操作Excel的资料来学习,完了准备在这里做个记录,以备后日不时之需。
我看到的,操作Excel的方式有四种:
com组件
首先,com 组件的方式指的是利用 office 的 Excel 组件对Excel文件进行操作,这种方式需要电脑上安装有Excel,项目中也要添加相关引用,项目运行时也要启动Excel进程。
据说,这种方式的功能很强大,说是Excel能实现的功能都能够通过这种方法在代码里进行设置,反正我是没有验证过,因为它实在是太慢了。其他方法1s就可以搞定的,用它稳定在8.7s...
OleDb
使用Microsoft Jet 提供程序用于连接到 Excel 工作簿,将Excel文件作为数据源来读写,这种方法读写都相当快,就是不是很灵活。
它需要使用指定的连接字符串初始化 System.Data.OleDb.OleDbConnection 类的新实例,.xls文件 和 .xlsx文件的连接字符串并不一样。
case ".xls":
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathName +
";Extended Properties='Excel 8.0;HDR=Yes;IMEX=2;'";
break;
case ".xlsx":
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + pathName +
";Extended Properties='Excel 12.0;HDR=Yes;IMEX=2;'";
break;
上面的HDR用于判断是否将第一行作为数据读取(个人感觉应该只在读操作有用),IMEX用于判断是读取还是写入,这个需要自己尝试,我只能说,我在写入操作的时候将其设为0,读取的时候设为1,你也可以设为2,错了再调整...
它能返回所有的sheet的名称,以及单个sheet的所有列名,这有时是相当有用的。
#region 获取Excel文件的表名 返回list集合
/// 路径名
///
public static List GetExcelTableName(OleDbConnection conn, string pathName)
{
List tableName = new List();
if (File.Exists(pathName))
{
//conn.Open();
System.Data.DataTable dt = conn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
string strSheetTableName = null;
foreach (System.Data.DataRow row in dt.Rows)
{
strSheetTableName = row["TABLE_NAME"].ToString();
//过滤无效SheetName
if (strSheetTableName.Contains("$") && strSheetTableName.Replace("'", "").EndsWith("$"))
{
strSheetTableName = strSheetTableName.Replace("'", ""); //可能会有 '1X$' 出现
strSheetTableName = strSheetTableName.Substring(0, strSheetTableName.Length - 1);
tableName.Add(strSheetTableName);
}
}
}
return tableName;
}
#endregion
#region 获取EXCEL工作表的列名 返回list集合
/// Excel路径名
///
public static List getExcelFileInfo(string pathName)
{
string strConn;
List list_ColumnName = new List();
FileInfo file = new FileInfo(pathName);
if (!file.Exists) { throw new Exception("文件不存在"); }
string extension = file.Extension;
switch (extension)
{
case ".xls":
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathName + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=2;'";
break;
case ".xlsx":
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + pathName + ";Extended Properties='Excel 12.0;HDR=Yes;IMEX=2;'";
break;
default:
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathName + ";Extended Properties='Excel 8.0;HDR=Yes;IMEX=1;'";
break;
}
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(strConn);
conn.Open();
System.Data.DataTable table = conn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, new object[] { null, null, null, null });
string TableName = null;
string ColumnName = null;
foreach (System.Data.DataRow drow in table.Rows)
{
TableName = drow["Table_Name"].ToString();
if (TableName.Contains("$") && TableName.Replace("'", "").EndsWith("$"))
{
System.Data.DataTable tableColumns = conn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Columns, new object[] { null, null, TableName, null });
foreach (System.Data.DataRow drowColumns in tableColumns.Rows)
{
ColumnName = drowColumns["Column_Name"].ToString();
list_ColumnName.Add(ColumnName);
}
}
}
return list_ColumnName;
}
#endregion
读写的部分准备用copy来进行说明,虽然不会用到。
#region 读取Excel,将第一行作为标题,其他行作为数据,再将其导出到Excel,对空白行进行处理
public static bool copy(string copyPath, string pastePath, string copy_sheetName)
{
DataSet ds = new DataSet();
OleDbConnection copy_objConn = null;//Excel的连接
OleDbConnection paste_objConn = null;//Excel的连接
try
{
string strExtension = System.IO.Path.GetExtension(copyPath);//获取文件扩展名
string strFileName = System.IO.Path.GetFileName(copyPath);//获取文件名
switch (strExtension)
{
case ".xls":
copy_objConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + copyPath
+ ";" + "Extended Properties=\"Excel 8.0;HDR=YES;IMEX=1;\"");
break;
case ".xlsx":
copy_objConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + copyPath
+ ";" + "Extended Properties=\"Excel 12.0;HDR=YES;IMEX=1;\"");
break;
default:
copy_objConn = null;
break;
}
if (copy_objConn == null)
{
return false;
}
copy_objConn.Open();
string strSql = "select * from [" + copy_sheetName + "] where [系统物料号] is not null";
OleDbCommand objCmd = new OleDbCommand(strSql, copy_objConn);//获取Excel指定Sheet表中的信息
OleDbDataAdapter myData = new OleDbDataAdapter(strSql, copy_objConn);
myData.Fill(ds, copy_sheetName);//填充数据
DataTable dt = ds.Tables[0];
strExtension = System.IO.Path.GetExtension(pastePath);//获取文件扩展名
strFileName = System.IO.Path.GetFileName(pastePath);//获取文件名
switch (strExtension)
{
case ".xls":
paste_objConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pastePath
+ ";" + "Extended Properties=\"Excel 8.0;HDR=YES;IMEX=0;\"");
break;
case ".xlsx":
paste_objConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + pastePath
+ ";" + "Extended Properties=\"Excel 12.0;HDR=YES;IMEX=0;\"");
break;
default:
paste_objConn = null;
break;
}
if (paste_objConn == null)
{
return false;
}
paste_objConn.Open();
StringBuilder strSQL = new StringBuilder();
System.Data.OleDb.OleDbCommand cmd;
string tableName = dt.TableName.Substring(0, dt.TableName.Length - 1);
try
{
List list = GetExcelTableName(paste_objConn, pastePath);
if (list.Contains(tableName))
{
//覆盖文件时可能会出现Table 'Sheet1' already exists.所以这里先删除了一下
cmd = new System.Data.OleDb.OleDbCommand(string.Format("drop table {0}", tableName), paste_objConn);
cmd.ExecuteNonQuery();
}
}
catch (Exception e)
{
Console.WriteLine("drop table failed!" + e.Message);
}
//创建表格字段
strSQL.Append("CREATE TABLE ").Append("[" + tableName + "]");
strSQL.Append("(");
for (int i = 0,int dt_column_count = dt.Columns.Count; i < dt_column_count ; i++)
{
strSQL.Append("[" + dt.Columns[i].ColumnName + "] text,");
}
strSQL = strSQL.Remove(strSQL.Length - 1, 1);
strSQL.Append(")");
cmd = new System.Data.OleDb.OleDbCommand(strSQL.ToString(), paste_objConn);
cmd.ExecuteNonQuery();
//添加数据
StringBuilder strvalue = null;
for (int i = 0,int dt_row_count = dt.Rows.Count; i < dt_row_count; i++)
{
strSQL.Clear();
strvalue = new StringBuilder();
for (int j = 0; j < dt.Columns.Count; j++)
{
strvalue.Append("'" + dt.Rows[i][j].ToString() + "'");
if (j != dt.Columns.Count - 1)
{
strvalue.Append(",");
}
}
cmd.CommandText = strSQL.Append(" insert into [" + dt.TableName.Substring(0, dt.TableName.Length - 1) + "] values (").Append(strvalue).Append(")").ToString();
cmd.ExecuteNonQuery();
}
}
catch (Exception e)
{
Console.WriteLine("copy失败!" + e.Message);
return false;
}
finally
{
copy_objConn.Close();
paste_objConn.Close();
}
return true;
}
#endregion
有一点需要注意的是,win32 平台是可以正常用的,any CPU 平台是不可以的,需要安装微软的程序等操作。
因为这个原因,我也没有找有没有操作格式的方法,有需要的自己找找看。
NPOI
这是花费时间最久的插件,用完只能说真的不好用。
想要用这个插件的需要在NuGet程序包里搜寻安装到项目中。
读写都不是很慢,但是数据量太大的无法处理,大概30000 * 20 的规模已经不能胜任了。
它对于.xls文件 和 .xlsx文件的处理也是不一样的:
ISheet sheet = null;
IWorkbook workbook = null;
switch (strExtension)
{
case ".xls":
workbook = new HSSFWorkbook(fs);
sheet = workbook.GetSheet(sheetName);
break;
case ".xlsx":
workbook = new XSSFWorkbook(fs);
sheet = workbook.GetSheet(sheetName);
break;
default:
break;
}
npoi读取Excel需要对内容进行类型判断
private static object GetValueType(ICell cell, IWorkbook workbook)
{
if (cell == null)
return null;
switch (cell.CellType)
{
case CellType.Blank: //BLANK:
return null;
case CellType.Boolean: //BOOLEAN:
return cell.BooleanCellValue;
case CellType.Numeric: //NUMERIC:
return cell.NumericCellValue;
case CellType.String: //STRING:
return cell.StringCellValue;
case CellType.Error: //ERROR:
return cell.ErrorCellValue;
case CellType.Formula: //FORMULA:
default:
return "=" + cell.CellFormula;
}
}
现在来说遇到的第一个问题,比如说,我希望读取A1的内容怎么办,用下面的方法可以吗?
IRow row1 = sheet.GetRow(0);
ICell cell1 = row1.GetCell(0);
object value = GetValueType(cell1, workbook);
一般情况,是可以的,但是如果是公式,出来的结果并不是希望的结果,对上面的方法进行修改:
private static object GetValueType(ICell cell, IWorkbook workbook)
{
if (cell == null)
return null;
switch (cell.CellType)
{
case CellType.Blank: //BLANK:
return null;
case CellType.Boolean: //BOOLEAN:
return cell.BooleanCellValue;
case CellType.Numeric: //NUMERIC:
return cell.NumericCellValue;
case CellType.String: //STRING:
return cell.StringCellValue;
case CellType.Error: //ERROR:
return cell.ErrorCellValue;
case CellType.Formula: //FORMULA:
IFormulaEvaluator evaluator = null;
if (workbook is HSSFWorkbook)
evaluator = new HSSFFormulaEvaluator(workbook);
else
evaluator = new XSSFFormulaEvaluator(workbook);
cell = evaluator.EvaluateInCell(cell);
return cell.NumericCellValue;
default:
return "=" + cell.CellFormula;
}
}
修改过后,会发现,一部分公式成功获得结果,一部分公式提示捕获异常,在进行修改:
private static object GetValueType(ICell cell, IWorkbook workbook)
{
if (cell == null)
return null;
switch (cell.CellType)
{
case CellType.Blank: //BLANK:
return null;
case CellType.Boolean: //BOOLEAN:
return cell.BooleanCellValue;
case CellType.Numeric: //NUMERIC:
return cell.NumericCellValue;
case CellType.String: //STRING:
return cell.StringCellValue;
case CellType.Error: //ERROR:
return cell.ErrorCellValue;
case CellType.Formula: //FORMULA:
IFormulaEvaluator evaluator = null;
if (workbook is HSSFWorkbook)
evaluator = new HSSFFormulaEvaluator(workbook);
else
evaluator = new XSSFFormulaEvaluator(workbook);
cell = evaluator.EvaluateInCell(cell);
try
{
return cell.NumericCellValue;
}
catch
{
return cell.StringCellValue;
}
default:
return "=" + cell.CellFormula;
}
}
这样应该能在大多数情况下,获取我们希望的值了。
第二个问题,同一个sheet中数据区域中间隔着很多空行,不知道你们有没有遇到过这种情况,npoi读取Excel文件的行数时,即使该行没有数据,但是只要最初的行高被动过,也或被读到,即,把3000行的行高改了,但是其实只有100行数据,它计算的行数就会是3000,如果你知道只需要读取100行,但是如果从200行开始又有100行数据怎么办?如果说这个也知道,那当我没说。事实上,虽然npoi统计出来有那么多行,但是当你获取空行时就会报异常,提示你引用对象没有获取实例,大概是这么个意思,这说明sheet里是没有存这些空行的信息的,除了行数。如果你对Excel结构不太清楚,那么就需要在用异常做流程处理,虽然这样做实际上效率很低。
///
/// Excel导入成Datable
///
/// 导入路径(包含文件名与扩展名)
///
private static DataTable ExcelToDataTable(string strExcelPath, string sheetName)
{
DataTable dataTable = new DataTable();
FileStream fs = null;
try
{
//获取文件扩展名
string strExtension = System.IO.Path.GetExtension(strExcelPath);
using (fs = new FileStream(strExcelPath, FileMode.Open, FileAccess.Read))
{
ISheet sheet = null;
IWorkbook workbook = null;
switch (strExtension)
{
case ".xls":
workbook = new HSSFWorkbook(fs);
sheet = workbook.GetSheet(sheetName);
break;
case ".xlsx":
workbook = new XSSFWorkbook(fs);
sheet = workbook.GetSheet(sheetName);
break;
default:
break;
}
if (sheet == null)
Console.WriteLine("文件后缀名错误!");
//表头
IRow header = sheet.GetRow(sheet.FirstRowNum);
List columns = new List();
object obj = null;
for (int i = 0, int dt_column_count = header.LastCellNum; i < dt_column_count; i++)
{
obj = GetValueType(header.GetCell(i), workbook); string a = header.GetCell(0).StringCellValue;
if (obj == null || obj.ToString() == string.Empty)
{
dataTable.Columns.Add(new DataColumn("Columns" + i.ToString()));
//continue;
}
else
dataTable.Columns.Add(new DataColumn(obj.ToString()));
columns.Add(i);
}
//数据
object value = null;
DataRow dr = null;
for (int i = sheet.FirstRowNum + 1, int dt_row_count = sheet.LastRowNum; i <= dt_row_count; i++)
{
try
{
value = GetValueType(sheet.GetRow(i).GetCell(0), workbook);
}
catch
{
continue;
}
if (value != null)
{
dr = dataTable.NewRow();
bool hasValue = false;
foreach (int j in columns)
{
dr[j] = GetValueType(sheet.GetRow(i).GetCell(j), workbook);
if (dr[j] != null && dr[j].ToString() != string.Empty)
hasValue = true;
}
if (hasValue)
dataTable.Rows.Add(dr);
}
}
return dataTable;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return dataTable;
}
finally
{
if(fs != null)
fs.Close();
}
}
读取的情况应该没什么大问题的,下面贴上Excel转datatable的方法,也可以转到集合里,按需求做些修改就行了。
第三个问题,关于这个写入的问题,实际上我到最终也没什么好方法,如果有人知道,欢迎帮我解惑。
先贴上一段写入的方法:
///
/// Datable导出成Excel
///
/// 数据源
/// 导出路径(包括文件名与扩展名)
/// 生成的sheet的name
///
private static bool TableToExcel(DataTable dt, string strExcelPath, string sheetName)
{
//获取文件扩展名
string strExtension = System.IO.Path.GetExtension(strExcelPath);
//string strFileName = System.IO.Path.GetFileName(strExcelPath);
IWorkbook workbook = null;
switch (strExtension)
{
case ".xls":
workbook = new HSSFWorkbook();
break;
case ".xlsx":
workbook = new XSSFWorkbook();
break;
default:
break;
}
if (workbook == null)
return false;
ISheet sheet = workbook.CreateSheet(sheetName);
//表头
ICell cell = null;
IRow row = sheet.CreateRow(0);
for (int i = 0,int column_count = dt.Columns.Count; i < column_count; i++)
{
cell = row.CreateCell(i);
cell.SetCellValue(dt.Columns[i].ColumnName);
}
//数据
IRow row1 = null;
ICell cell = null;
for (int i = 0, int row_count = dt.Rows.Count; i < row_count; i++)
{
row1 = sheet.CreateRow(i + 1);
for (int j = 0, int column_count = dt.Columns.Count; j < column_count; j++)
{
cell = row1.CreateCell(j);
cell.SetCellValue(dt.Rows[i][j].ToString());
}
}
//转为字节数组
MemoryStream stream = new MemoryStream();
workbook.Write(stream);
var buf = stream.ToArray();
//保存为Excel文件
FileMode mode = FileMode.OpenOrCreate;
FileAccess access = FileAccess.ReadWrite;
FileShare share = FileShare.ReadWrite;
using (FileStream fs = new FileStream(strExcelPath, mode, access, share))
{
fs.Write(buf, 0, buf.Length);
fs.Flush();
fs.Close();
}
return true;
}
事实上,我一开始在数据库里读了一些数据然后写入Excel,成功了,我就没管了。
知道我在一系列读取后在进行写入时出现了问题,整个Excel文件都无法打开了。我经过一系列调试,发现读取之后都是没问题的,写入的最后出现问题,即FileStream出现后的那几句代码,我怀疑过很多地方,比如之前的fs未close,实际上不需要手动close;再比如new FileStream时的参数,这让我又发现了一个问题,我进行写入操作的时候会将原先的删除再新建,并且更改参数并不起效果,我猜测应该将原有文件作为参数传进来,找了一下确实是这个问题,在已存在的Excel文件添加sheet的完整代码:
///
/// Datable导出成Excel
///
/// 数据源
/// 导出路径(包括文件名与扩展名)
/// 生成的sheet的name
/// 是新建or打开:true 为 create;false 为 open
///
private static bool TableToExcel(DataTable dt, string strExcelPath, string sheetName, bool flag)
{
//获取文件扩展名
string strExtension = System.IO.Path.GetExtension(strExcelPath);
//string strFileName = System.IO.Path.GetFileName(strExcelPath);
IWorkbook workbook = null;
switch (strExtension)
{
case ".xls":
if(flag)
workbook = new HSSFWorkbook();
else
workbook = new HSSFWorkbook(File.OpenRead(strExcelPath));
break;
case ".xlsx":
if(flag)
workbook = new XSSFWorkbook();
else
workbook = new XSSFWorkbook(File.OpenRead(strExcelPath));
break;
default:
break;
}
if (workbook == null)
return false;
ISheet sheet = workbook.CreateSheet(sheetName);
//表头
IRow row = sheet.CreateRow(0);
ICell cell = null;
for (int i = 0, int column_count = dt.Columns.Count; i < column_count; i++)
{
cell = row.CreateCell(i);
cell.SetCellValue(dt.Columns[i].ColumnName);
}
//数据
IRow row1 = null;
ICell cell = null;
for (int i = 0, int row_count = dt.Rows.Count; i < row_count; i++)
{
row1 = sheet.CreateRow(i + 1);
for (int j = 0, int column_count = dt.Columns.Count; j < column_count; j++)
{
cell = row1.CreateCell(j);
cell.SetCellValue(dt.Rows[i][j].ToString());
}
}
//转为字节数组
MemoryStream stream = new MemoryStream();
workbook.Write(stream);
var buf = stream.ToArray();
//保存为Excel文件
FileMode mode = FileMode.OpenOrCreate;
FileAccess access = FileAccess.ReadWrite;
FileShare share = FileShare.ReadWrite;
using (FileStream fs = new FileStream(strExcelPath, mode, access, share))
{
fs.Write(buf, 0, buf.Length);
fs.Flush();
fs.Close();
}
return true;
}
虽然上面代码可以添加sheet,并且原有的sheet依然存在,但是这有个前提条件:原先存在的sheet需要时通过npoi添加的,也就是说通过office新建的sheet,上面的方法是不行的,会导致文件损坏,无法打开。
为了解决这个问题,我看了很多资料,据说是因为npoi写入会在末尾添加一个标志,那么可以猜测一下,是不是给自己新建的sheet加一个这样的标志就能解决问题?我是没有进行这个测试,最后是新建了一个Excel文件。有哪位大神知道这种方法或者其他方法解决这个问题的,欢迎指导。
最后说一下,这个方式是可以设置单元格格式的,下面贴上一部分代码,对第一行设置格式:
//设置列宽
sheet.SetColumnWidth(0,25 * 256);
sheet.SetColumnWidth(1, 45 * 256);
sheet.SetColumnWidth(2, 10 * 256);
sheet.SetColumnWidth(3, 6 * 256);
sheet.SetColumnWidth(4, 20 * 256);
sheet.SetColumnWidth(5, 20 * 256);
sheet.SetColumnWidth(6, 20 * 256);
sheet.SetColumnWidth(7, 20 * 256);
//字体
IFont font = workbook.CreateFont();
font.IsBold = true; //字体为粗体
font.FontName = "等线";
//创建单元格格式
IDataFormat dataFormat = workbook.CreateDataFormat();
//ICellStyle style1 = workbook.CreateCellStyle();
//style1.DataFormat = dataFormat.GetFormat("0"); //精度到整数
//ICellStyle style2 = workbook.CreateCellStyle();
//style2.DataFormat = dataFormat.GetFormat("0.00%"); //百分数
//ICellStyle style3 = workbook.CreateCellStyle();
//style3.SetFont(font);
//ICellStyle style4 = workbook.CreateCellStyle();
//style4.FillBackgroundColor = IndexedColors.White.Index;
//style4.FillForegroundColor = IndexedColors.Black.Index;
//style4.FillPattern = FillPattern.SolidForeground;
//表头 style : 字体粗体,背景色为蓝,居中对齐
ICellStyle style_header = workbook.CreateCellStyle();
style_header.SetFont(font);
style_header.FillBackgroundColor = IndexedColors.White.Index;
style_header.Alignment = HorizontalAlignment.Center;
//style_header.FillForegroundColor = IndexedColors.LightBlue.Index;
style_header.FillForegroundColor = (short)44;
style_header.FillPattern = FillPattern.SolidForeground;
IRow row = sheet.CreateRow(0);
ICell cell = null;
for (int i = 0, int count = firstRowName.Count; i < count; i++)
{
cell = row.CreateCell(i);
cell.SetCellValue(firstRowName[i]);
cell.CellStyle = style_header;
}
epplus
这种方法挺不错的,但是由于某些原因并没有深入了解,不过数据量比npoi强,而且没那么多问题。
放一个比较全的测试方法吧,里面进行了很多功能测试,我自己测试没通过的都有标志,没标注的应该都是可以成功的。
public static Boolean Load_Main()
{
FileInfo newFile = new FileInfo(System.AppDomain.CurrentDomain.BaseDirectory + "test1.xlsx");
using (ExcelPackage package = new ExcelPackage(newFile)) //创建Excel
{
#region 获取sheet
if (package.Workbook.Worksheets["test1"] != null)
{
package.Workbook.Worksheets.Delete("test1");//删除工作表
}
//创建一个新的sheet
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("test1");
//获取excel中的一个sheet用于后续操作(通过模板写数据的可用此方法)
ExcelWorksheet worksheet2 = package.Workbook.Worksheets["Sheet1"];
if (package.Workbook.Worksheets["hidesheet"] != null)
{
package.Workbook.Worksheets.Delete("hidesheet");//删除工作表
}
ExcelWorksheet hideSheet = package.Workbook.Worksheets.Add("hidesheet");
#endregion
#region 添加数据
/* Epplus中给单元格赋值非常简单,两种方法:(ps:Epplus的所有行列数都是以1开始的)
* 1. worksheet.Cells[1, 1].Value = "名称";//直接指定行列数进行赋值
* 2. worksheet.Cells["A1"].Value = "名称";//直接指定单元格进行赋值
*/
worksheet.Cells[1, 1].Value = "名称";
worksheet.Cells[1, 2].Value = "单价";
worksheet.Cells[1, 3].Value = "销量";
worksheet.Cells[2, 1].Value = "洗衣机";
worksheet.Cells[2, 2].Value = 1000;
worksheet.Cells[2, 3].Value = 1000;
worksheet.Cells[3, 1].Value = "空调";
worksheet.Cells[3, 2].Value = 2300;
worksheet.Cells[3, 3].Value = 1760;
worksheet.Cells[4, 1].Value = "冰箱";
worksheet.Cells[4, 2].Value = 3400;
worksheet.Cells[4, 3].Value = 1530;
worksheet.Cells[5, 1].Value = "电视机";
worksheet.Cells[5, 2].Value = 7800;
worksheet.Cells[5, 3].Value = 2700;
#endregion
#region 公式应用
//乘法公式,第二列乘以第三列的值赋值给第四列
worksheet.Cells["E2"].Formula = "B2*C2";//单个单元格
worksheet.Cells["D2:D5"].Formula = "B2*C2";//多个单元格
//自动求和 第二列中,将2,3,4,5行求和值赋给第六行
//worksheet2.Cells[6, 2, 6, 4].Formula = string.Format("SUBTOTAL(9,{0})", new ExcelAddress(2, 2, 5, 2).Address);
worksheet.Cells["B7:D7"].Formula = string.Format("SUBTOTAL(9,{0})", new ExcelAddress(2, 2, 5, 2).Address);
#endregion
#region 设置单元格格式
worksheet.Cells[5, 3].Style.Numberformat.Format = "#,##0.00";//这是保留两位小数
#endregion
#region 设置单元格对齐方式
using (ExcelRange range = worksheet.Cells[1, 1, 5, 3])
{
range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;//水平居中
range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;//垂直居中
}
//worksheet.Cells[1, 4, 1, 5].Merge = true;//合并单元格
//worksheet.Cells.Style.WrapText = true;//自动换行
#endregion
#region 设置单元格字体样式
using (ExcelRange range = worksheet.Cells[1, 1, 1, 3])
{
range.Style.Font.Bold = true;
range.Style.Font.Color.SetColor(Color.White);
range.Style.Font.Name = "微软雅黑";
range.Style.Font.Size = 12;
range.Style.Fill.PatternType = ExcelFillStyle.Solid;
range.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(128, 128, 128));
}
#endregion
#region 设置单元格背景样式
worksheet.Cells[1, 1].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[1, 1].Style.Fill.BackgroundColor.SetColor(Color.FromArgb(128, 128, 128));//设置单元格背景色
#endregion
#region 设置单元格边框,两种方法
//worksheet.Cells[1, 1].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));//设置单元格所有边框
//worksheet.Cells[1, 1].Style.Border.Bottom.Style = ExcelBorderStyle.Thin;//单独设置单元格底部边框样式和颜色(上下左右均可分开设置)
//worksheet.Cells[1, 1].Style.Border.Bottom.Color.SetColor(Color.FromArgb(191, 191, 191));
worksheet.Cells[1, 1].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[1, 2].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[1, 3].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[2, 1].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[2, 2].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[2, 3].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[3, 1].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[3, 2].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[3, 3].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[4, 1].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[4, 2].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[4, 3].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[5, 1].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[5, 2].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
worksheet.Cells[5, 3].Style.Border.BorderAround(ExcelBorderStyle.Thin, Color.FromArgb(191, 191, 191));
#endregion
#region 设置单元格的行高和列宽
worksheet.Cells.Style.ShrinkToFit = true;//单元格自动适应大小
worksheet.Row(1).Height = 15;//设置行高
worksheet.Row(1).CustomHeight = true;//自动调整行高
worksheet.Column(1).Width = 15;//设置列宽
#endregion
#region 设置sheet背景
worksheet.View.ShowGridLines = false;//去掉sheet的网格线
worksheet.Cells.Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells.Style.Fill.BackgroundColor.SetColor(Color.LightGray);//设置背景色
//worksheet.BackgroundImage.Image = Image.FromFile(@"C:\Users\cai.j\Pictures\Saved Pictures\8.jpg");//设置背景图片 没看出效果
#endregion
#region 插入图片
ExcelPicture picture = worksheet.Drawings.AddPicture("logo1", Image.FromFile(@"C:\Users\cai.j\Pictures\Saved Pictures\9.jpg"));//插入图片
picture.SetPosition(100, 100);//设置图片的位置
picture.SetSize(100, 100);//设置图片的大小
#endregion
#region 插入形状 看不出来
//ExcelShape shape = worksheet.Drawings.AddShape("shape", eShapeStyle.Rect);//插入形状
//shape.Font.Color = Color.Red;//设置形状的字体颜色
//shape.Font.Size = 15;//字体大小
//shape.Font.Bold = true;//字体粗细
//shape.Fill.Style = eFillStyle.NoFill;//设置形状的填充样式
//shape.Border.Fill.Style = eFillStyle.NoFill;//边框样式
//shape.SetPosition(200, 300);//形状的位置
//shape.SetSize(80, 30);//形状的大小
//shape.Text = "test";//形状的内容
#endregion
#region 给图片加超链接 有问题
//picture = worksheet.Drawings.AddPicture("logo2", Image.FromFile(@"C:\Users\cai.j\Pictures\Saved Pictures\5.jpg"), new ExcelHyperLink("http:\\www.baidu.com", UriKind.Relative));
//picture.SetPosition(700, 700);//设置图片的位置
//picture.SetSize(100, 100);//设置图片的大小
#endregion
#region 给单元格加超链接
//worksheet.Cells[1, 1].Hyperlink = new ExcelHyperLink("http:\\www.baidu.com", UriKind.Relative);
#endregion
#region 隐藏sheet 有问题
//worksheet.Hidden = eWorkSheetHidden.Hidden;//隐藏sheet
//worksheet.Column(1).Hidden = true;//隐藏某一列
//worksheet.Row(1).Hidden = true;//隐藏某一行
#endregion
#region 创建柱状图
ExcelChart chart = worksheet.Drawings.AddChart("chart", eChartType.ColumnClustered);//创建图表
ExcelChartSerie serie = chart.Series.Add(worksheet.Cells[2, 3, 5, 3], worksheet.Cells[2, 1, 5, 1]);//选择数据,设置图表的x轴和y轴
serie.HeaderAddress = worksheet.Cells[1, 3];//设置图表的图例
chart.SetPosition(150, 10);//设置位置
chart.SetSize(500, 300);//设置大小
chart.Title.Text = "销量走势";//设置图表的标题
chart.Title.Font.Color = Color.FromArgb(89, 89, 89);//设置标题的颜色
chart.Title.Font.Size = 15;//标题的大小
chart.Title.Font.Bold = true;//标题的粗体
chart.Style = eChartStyle.Style15;//设置图表的样式
chart.Legend.Border.LineStyle = eLineStyle.Solid;
chart.Legend.Border.Fill.Color = Color.FromArgb(217, 217, 217);//设置图例的样式
#endregion
#region Excel加密和锁定 没测过
//worksheet.Protection.IsProtected = true;//设置是否进行锁定
//worksheet.Protection.SetPassword("yk");//设置密码
//worksheet.Protection.AllowAutoFilter = false;//下面是一些锁定时权限的设置
//worksheet.Protection.AllowDeleteColumns = false;
//worksheet.Protection.AllowDeleteRows = false;
//worksheet.Protection.AllowEditScenarios = false;
//worksheet.Protection.AllowEditObject = false;
//worksheet.Protection.AllowFormatCells = false;
//worksheet.Protection.AllowFormatColumns = false;
//worksheet.Protection.AllowFormatRows = false;
//worksheet.Protection.AllowInsertColumns = false;
//worksheet.Protection.AllowInsertHyperlinks = false;
//worksheet.Protection.AllowInsertRows = false;
//worksheet.Protection.AllowPivotTables = false;
//worksheet.Protection.AllowSelectLockedCells = false;
//worksheet.Protection.AllowSelectUnlockedCells = false;
//worksheet.Protection.AllowSort = false;
#endregion
#region 下拉框
var citys = InitCity();
for (int idx = 0, int count = citys.Count; idx < count; idx++)
{
hideSheet.Cells[idx + 1, 1].Value = citys[idx].Name;
//hideSheet.Cells[idx + 1, 2].Value = citys[idx].ParentId;
}
var val = worksheet.DataValidations.AddListValidation("H1");//设置下拉框显示的数据区域
val.Formula.ExcelFormula = "=hideSheet!$A:$A";//数据区域的名称 //数据需要时通过代码添加的
val.Prompt = "选择省份";//下拉提示
val.ShowInputMessage = true;//显示提示内容
#endregion
package.Save();//保存Excel
}
return true;
}
private static List InitCity()
{
var list = new List
{
new KeyValue {Id = 1, ParentId = 1, Name = "上海市1"},
new KeyValue {Id = 2, ParentId = 2, Name = "徐州市"},
new KeyValue {Id = 3, ParentId = 2, Name = "苏州市"},
new KeyValue {Id = 4, ParentId = 2, Name = "昆山市"},
new KeyValue {Id = 5, ParentId = 2, Name = "南京市"},
new KeyValue {Id = 6, ParentId = 3, Name = "北京市1"}
};
return list;
}
另外,直接将datatable转化成Excel的代码很简单,也可以将其他的集合输出到Excel,像list、dictionary等,需要的自己研究下,我就不贴了。
#region 导出到Excel ExportByEPPlus(DataTable sourceTable, string strFileName)
///
/// 使用EPPlus导出Excel(xlsx)
///
/// 数据源
/// xlsx文件名(不含后缀名)
public static string ExportByEPPlus(DataTable sourceTable, string strFileName)
{
string FileName = AppDomain.CurrentDomain.BaseDirectory + strFileName + ".xlsx";
if (System.IO.File.Exists(FileName)) //存在则删除
System.IO.File.Delete(FileName);
ExcelPackage ep = new ExcelPackage();
ExcelWorkbook wb = ep.Workbook;
ExcelWorksheet ws = wb.Worksheets.Add("result");
ws.Cells["A1"].LoadFromDataTable(sourceTable, false);
ep.SaveAs(new FileInfo(FileName));
return FileName;
}
#endregion
最后记录一下,写代码时考虑到的优化tip:
主要是为了内存考虑,局部变量在方法调用结束后就over了。另外,就是局部变量创建的速度更快。
减少重复计算
主要是针对循环里面
for (int idx = 0, int count = citys.Count; idx < count; idx++)
{
}
IRow row = null;
ICell cell = null;
for (int i = 0, int row_count = dt.Rows.Count; i < row_count; i++)
{
row = sheet.CreateRow(i + 1);
for (int j = 0, int column_count = dt.Columns.Count; j < column_count; j++)
{
cell = row.CreateCell(j);
cell.SetCellValue(dt.Rows[i][j].ToString());
}
}