目录

功能需求

Excel与DataSet的映射关系

范例运行环境

Excel DCOM 配置

设计实现

组件库引入

​方法设计

返回值 

参数设计

打开数据源并计算Sheets

拆分合并的单元格

创建DataTable

将单元格数据写入DataTable

总结

功能需求

将Excel里的worksheet表格导入到DataSet里,是项目应用里常用的一种操作。一般情况下,worksheet是一个标准的二维数组,如下图:

我们可以效仿 MS SQL SERVER 的一些基本导入选项,如首行是否包含数据,要导入哪个Sheet?还是遍历Sheets?

实际的情况,客户经常会提供一些合并单元格的Excel表格,如下图中的“所在部门名称”列:

再畅想一下,假设有跨列的情况如下:

解决导入,一种方法,是让客户进行单元格拆分或技术服务人员进行拆分后再导入。另一种就是我们要继续完善应用,处理实现合并单元格的自动化处理。

Excel与DataSet的映射关系

下图是 Excel 与 DataSet 的映射关系图:

1、Excel应用的Workbook对象与 DataSet 同为容器对象

2、Worksheets和Tables均代表各自的表集合

3、Worksheet与Table进行对应,产生和导入实际的数据

范例运行环境

操作系统: Windows Server 2019 DataCenter

操作系统上安装 Office Excel 2016

.net版本: .netFramework4.7.1 或以上

开发工具:VS2019  C#

Excel DCOM 配置

请参考我的文章《C# 读取Word表格到DataSet》有对Office DCOM详细配置介绍,这里不再赘述,Excel的对应配置名称如下图所示:

  

设计实现

组件库引入

方法设计

设计  object[] ExcelAsDataSet(string _filename,bool hastitle,string startaddress,string endaddress) 方法

返回值 

方法返回object数组,共包括两个object对象,如果成功转化则 object[0] 存储 DataSet对象,否则为 null。如果不成功则 object[1] 存储string 错误信息对象,可根据object[1].ToString()!="" 来判断是否转化成功。

参数设计

string _filename:Excel 数据源文件路径bool hastitle: 是否包含标题,如果设置为true,则表示首行数据为列名称定义string startaddress:可指定有效的起始单元格地址,不设置则默认为“A1”(即第一个单元格)string endaddress:可指定有效的截止单元格地址,不设置则默认为最后一个有值单元格(即XlCellType.xlCellTypeLastCell 枚举) 

    通过3、4参数的定义,可以定义出有效的导入矩形区域。

打开数据源并计算Sheets

object[] rv=new object[2];

rv[0]=null;

rv[1]="";

//创建一个名为ExcelApp的组件对象

// ExcelApplication excel = new ExcelApplication();

Excel.Application excel = new Excel.Application();

excel.DisplayAlerts=false;

excel.AskToUpdateLinks=false;

Excel.Workbook xb=excel.Workbooks.Add(_filename);

//获取活动的 worksheet和 excel sheet的个数,准备遍历sheets

Worksheet worksheet = (Worksheet) excel.ActiveSheet;

sheetCount=excel.Sheets.Count;

int startSheetIndex=1;

int endSheetIndex=sheetCount;

DataSet ds=new DataSet();

//遍历sheets

for (int currentIndex = startSheetIndex; currentIndex <= endSheetIndex; currentIndex++)

{

worksheet = (Worksheet)excel.Worksheets[currentIndex];

worksheet.Activate();

//处理每一个sheet.....

}

拆分合并的单元格

在获取有效的单元格区域后,就开始遍历单元格对象,判断单元格对象 MergeCells 属性即可,判断 Cell.MergeCells.ToString() == "True"  即表示该单元格为合并单元格对象。

示例代码如下:

//获取起始单元和截止单元格,以确定有效区域

Excel.Range _startcell=worksheet.Range["A1","A1"]; //默认为第一个单元格

if(startaddress!="")

{

try

{

_startcell=worksheet.Range[startaddress,startaddress];

}

catch(Exception ex)

{

rv[1]+=string.Format("{1}指定的起始单元格地址{0},不是合法的地址。\r\n",startaddress,worksheet.Name);

// KillProcessByStartTime("EXCEL",beforetime,aftertime);

continue;

}

}

Excel.Range _lastcell=worksheet.Cells.SpecialCells(XlCellType.xlCellTypeLastCell,Type.Missing);

//默认获取有值的最后一个有效的单元格

if(endaddress!="")

{

try

{

_lastcell=worksheet.Range[endaddress,endaddress];

}

catch(Exception ex)

{

rv[1]+=string.Format("{1}指定的结束单元格地址{0},不是合法的地址。\r\n",endaddress,worksheet.Name);

// KillProcessByStartTime("EXCEL",beforetime,aftertime);

// return rv;

continue;

}

}

//遍历有效区域单元格

foreach (Excel.Range aicell in worksheet.Range[_startcell,_lastcell])

{

if (aicell.MergeCells.ToString() == "True")

{

//处理合并单元格

object temp_merge_value = aicell.Value2; //备份单元格的值

int u_row = aicell.Row; //记录单元格的首行索引

int u_rows = aicell.MergeArea.Rows.Count; //记录单元格的合并区域包含的行数

int u_col = aicell.Column; //记录单元格的首列索引

int u_cols = aicell.MergeArea.Columns.Count; //记录单元格的合并区域包含的列数

aicell.MergeArea.UnMerge(); //取消合并,拆分单元格

Excel.Range new_aicell = worksheet.Range[worksheet.Cells[u_row, u_col], worksheet.Cells[u_row + u_rows - 1, u_col + u_cols - 1]]; //获取拆分后单元格后的有效区域

new_aicell.Value2 = temp_merge_value; //将拆分的单元格重新赋值(备份值)

}

}

创建DataTable

如果首行是列数据,则以该行的值创建表结构,否则自动创建以“C”为前缀的列名,如C1、C2...Cn以此类推。

System.Data.DataTable dt=ds.Tables.Add();

dt.TableName=worksheet.Name; //表名为worksheet的名称

for(int i=_startcell.Column;i<=_lastcell.Column;i++)

{

Excel.Range _cell=worksheet.Range[worksheet.Cells[_startcell.Row,i],worksheet.Cells[_startcell.Row,i]];

string _colname=hastitle==true?_cell.Value2.ToString():"C"+(i-_startcell.Column+1).ToString(); //如果第一行是标题,则赋单元格的值,否则以C开头加序号

DataColumn dc=dt.Columns.Add();

dc.ColumnName=_colname;

dc.DataType=System.Type.GetType("System.String");

dc.AllowDBNull=true;

}

将单元格数据写入DataTable

object[,] cells=null; 定义二维对象数组

if(hastitle) //如果首行包含列,则加行索引加1取数据行

{

startrow=_startcell.Row+1;

}

//将有效区域单元格转化赋值为 object[,]

cells=(object[,])worksheet.Range[worksheet.Cells[startrow,_startcell.Column],worksheet.Cells[_lastcell.Row,_lastcell.Column]].Value2;

//遍历数组,添加行数据到 DataTable里

int _rowcount=cells.GetLength(0);

int _colcount=cells.GetLength(1);

for(int i=0;i<_rowcount;i++)

{

object[] newrowdata=new object[_colcount];

for(int j=0;j<_colcount;j++)

{

newrowdata[j]=cells[i,j];

}

DataRow dr=dt.Rows.Add(newrowdata);

}

总结

在实际的应用中,还可以设定多种参数选项:

1、如导入单元格的数据,是格式化后的数据(ExcelReport.ImportDataType.FormattingValue),还是原始数据(ExcelReport.ImportDataType.OriginalValue),这也是Cell.Value和Cell.Value2的区别

2、创建表列名字段过度依赖于单元格的值,可能会创建失败,建议定义参数指定是否重写列名

3、是否只导入指定的sheet或活动的sheet。

这些选项都可以根据实际的业务进行扩展,我们在此仅讲述了一些操作Excel相关的关键方法和属性,这里仅作参考,欢迎大家评论指教!

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: