【C#】C#实现Excel合并单元格数据导入数据集
目录
功能需求
将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相关的关键方法和属性,这里仅作参考,欢迎大家评论指教!
原文链接https://blog.csdn.net/michaelline/article/details/133157434
猜你喜欢
- 【C#】C# Winform 三层架构
- 一、介绍三层架构是 C# 桌面开发中比较常用的框架,是由 表示层(UI)、业务逻辑层(BLL)和数据访问层(DAL)三层架构组成,目的是为了 “高内聚,低耦合”。开发人员分工更明确,将精力更专注于应用系统核心业务逻辑的分析、设计和开发,加快项目的进度,提高了开发效率,有利于项目的更新和维护工作。从三层架构可以看到,很类似于 Web 前端开发的 MVC 框架(视图View,模型Model,控制Contorller),但本质上也有不同的地方,比如都有视图(三层中叫 UI),Mod
- 【C#】C# Winform 文本面板带滚动条
- 在PC软件开发中经常有这样的需求,需要在一个固定大小的面板中显示一些内容,并且面板能上下拖动,将所有的内容完整的展示,有点类似网页上看新闻,如果要在 winfrom 中要如何实现的呢,下面就演示如何实现的吧效果:1.新建一个winform 项目,在界面中拖入一个Panel 将 panel1 的 AutoScroll 设置为 True2.再次拖入一个 Panel ,将高度拉长,这时就自动出现了滚动条,只是此时里面还没有任何内容,下面就在 panel2 中加入一点内容。
- 【C#】C# Winform 日志系统
- 目录一、效果1.刷新日志效果2.单独日志的分类3.保存日志的样式二、概述三、日志系统API1.字段Debug.IsScrollingDebug.VersionDebug.LogMaxLenDebug.LogTitleDebug.IsConsoleShowLog2.方法Debug.Log(string)Debug.Log(string, params object[])Debug.Logs(string)Debug.Logs(string, params object[])Debug.LogSav
- 【C#】C# Winform GDI+ 绘图
- 目录一、概述二、绘图1.画直线2.画矩形3.画圆、圆弧4.画扇形5.画多边形6.绘制字符串7.填充图形结束一、概述Graphics类是GDI+技术的一个基本类。GDI+(Graphics Device Interface)是.NET框架的重要组成部分,提供对二维图形图像和文字排版处理的支持。GDI+相关的类分布在下列命名空间中: System.Drawing:提供了最基本的绘图功能(比如画直线、矩形、椭圆等); System.Drawing.Drawing2D: 提供了高级的二维和矢量绘图功能(
- 【C#】C# Winfrom 常用功能整合-2
- 目录Winfrom 启动一个外部exe文件,并传入参数Winform ListBox用法HTTP下载文件(推荐)Winform HTTP下载并显示进度Winform HTTP下载文件Winform 跨线程访问UI组件Winform 改变文字的颜色和大小Winfrom 启动一个外部exe文件,并传入参数在我们常用的一些软件中,经常有些软件,双击之后根本打不开,这是因为启动时做了限制,我们需要传入一些参数才能打开,在工作中,这个需求也可以用在软件的自动更新上,
- 【C#】从零开始用C#写一个桌面应用程序(一)基础操作
- 准备winform应用程序编写桌面应用客户端的技术。xaml一种标记语言。winform程序组成。 程序入口: form.cs和它的设计文件: 启动的过程以及涉及的文件:main函数: form1的构造函数和它的设计文件: main-》构造form-》initializeComponent-》 拖入一个 button控件可以看到: 这时我们已经梳理启动过程。使用组件的方法 可以在
- 【C#】C# NLua Winform 热更新
- 一、概述NLua 是一个用于 .NET 平台的 Lua 脚本绑定库。它允许在 C# 代码中嵌入 Lua 脚本,并允许两者之间进行交互。NLua 的主要特点包括:轻量级:NLua 是一个轻量级的库,易于集成到现有的 .NET 项目中。动态类型:Lua 是动态类型的语言,这意味着变量的类型可以在运行时改变。灵活的绑定:NLua 提供了灵活的绑定机制,使得 C# 和 Lua 之间的数据交互变得简单。丰富的 API:NLua 提供了丰富的 API,以便在 Lua 脚本中调用 .NET 的类和方法。调试支
- 【C#】Winform NanUI 0.88版本 JS和C#相互调用
- 目录一、需求版本二、实例JS调用C#注册的只读属性JS调用C#注册的字段JS调用C#注册的同步方法JS调用C#注册的异步方法C#注册一个方法,JS调用并传递参数C#注册一个方法,JS调用并接收C#返回值C#注册一个方法,接收JS的数组参数C#注册一个方法,接收JS的一个函数,执行这个JS函数,并将C#的值传递过去三、结束一、需求在软件的界面和软件逻辑分离后,最重要的就是要处理参数的传递,和函数的调用,因此存在JS中和C#相互调用的需求。版本NanUI 版本:0.8.80.191二、实例using