您的当前位置:首页>全部文章>文章详情

【C#】C#实现Excel合并单元格数据导入数据集

CrazyPanda发表于:2024-01-29 23:43:34浏览:345次TAG:

目录

功能需求

Excel与DataSet的映射关系

范例运行环境

Excel DCOM 配置

设计实现

组件库引入

方法设计

返回值 

参数设计

打开数据源并计算Sheets

拆分合并的单元格

创建DataTable

将单元格数据写入DataTable


总结



功能需求

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

1.png

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

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

1.png

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

1.png

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

Excel与DataSet的映射关系

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

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

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

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

1.png


范例运行环境

操作系统: Windows Server 2019 DataCenter

操作系统上安装 Office Excel 2016

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

开发工具:VS2019  C#

Excel DCOM 配置

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

1.png

  

设计实现

组件库引入

1.png

方法设计

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

返回值 

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

参数设计
  1. string _filename:Excel 数据源文件路径

  2. bool hastitle: 是否包含标题,如果设置为true,则表示首行数据为列名称定义

  3. string startaddress:可指定有效的起始单元格地址,不设置则默认为“A1”(即第一个单元格)

  4. 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# Winfrom Chart 图表控件 柱状图、折线图
目录一、参考二、柱状图1.新建项目2.修改图表样式3.绑定数据4.删除Series1图例1)使用属性窗体删除2)使用代码删除5.自定义X轴的刻度值1)使用List绑定2)使用LabelStyle.Format绑定6.自定义Y轴的刻度值7.X轴刻度值显示不全的解决方法8.修改X Y刻度值的字体样式9.X轴刻度值旋转90°10.禁用Y轴刻度线11.去掉Y轴刻度值12.改变柱子的宽度13.设置网格线的颜色14.设置网格线的线型三、折线图1.图表设置2.绑定数据结束效果:一、参考c# Chart设置样式
发表于:2024-02-02 浏览:411 TAG:
【C#】C#超急速举例入门-适用有C/C++语言基础
前提编程环境:vs2022电脑系统:win10学习目的:能看懂c#,不纠结各种细节,快速适应开发。入门篇程序结构变量类型类似c语言,不掌握细节,int,float,double都有。输入输出Console.WriteLine(&quot;变量0:{0}&quot;,&nbsp;para0); var&nbsp;a=Console.ReadLine();&nbsp;运算符几乎相同。sizeof();typeof();取地址,取值:&amp;,*;三元运算符: ? :判断类型:is强制转换:as。注
发表于:2024-01-30 浏览:339 TAG:
【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
发表于:2024-02-06 浏览:370 TAG:
【C#】C# NLua Winform 热更新
一、概述NLua 是一个用于 .NET 平台的 Lua 脚本绑定库。它允许在 C# 代码中嵌入 Lua 脚本,并允许两者之间进行交互。NLua 的主要特点包括:轻量级:NLua 是一个轻量级的库,易于集成到现有的 .NET 项目中。动态类型:Lua 是动态类型的语言,这意味着变量的类型可以在运行时改变。灵活的绑定:NLua 提供了灵活的绑定机制,使得 C# 和 Lua 之间的数据交互变得简单。丰富的 API:NLua 提供了丰富的 API,以便在 Lua 脚本中调用 .NET 的类和方法。调试支
发表于:2024-02-18 浏览:342 TAG:
【C#】C#调用win10系统自带软键盘的方法
上次做了个笔记是关于调用windows系统自带的触摸键盘的方法:C#调用Windows系统自带触摸键盘的方法_c# 虚拟键盘-CSDN博客除了调用触摸键盘,我们也可以通过调用win10的自带软键盘作为输入途径。方法很简单。1、添加using System.Diagnostics引用。2、创建进程Process Winvirkey = Process.Start(&quot;osk.exe&quot;);3、打开键盘:Winvirkey = Process.Start(&quot;osk.exe&amp;
发表于:2024-01-29 浏览:356 TAG:
【C#】Winform NanUI 0.77版本 JS和C#相互调用
目录一、导入插件二、常用方法三、C#和JS相互调用1.C# 调用JS2.JS调用C#方法3.完整版C#代码4.完整版JS代码结束一、导入插件用的NanUI版本0.77参考官方地址:https://docs.formium.net/zh-hans/tutorial/first-app.html二、常用方法基础代码:using&nbsp;NetDimension.NanUI; using&nbsp;NetDimension.NanUI.Browser; &nbsp; class&nbsp;MainW
发表于:2024-02-06 浏览:384 TAG:
【C#】C# Winform 文本面板带滚动条
在PC软件开发中经常有这样的需求,需要在一个固定大小的面板中显示一些内容,并且面板能上下拖动,将所有的内容完整的展示,有点类似网页上看新闻,如果要在 winfrom 中要如何实现的呢,下面就演示如何实现的吧效果:1.新建一个winform 项目,在界面中拖入一个Panel&nbsp;将 panel1 的&nbsp;AutoScroll 设置为 True2.再次拖入一个&nbsp;Panel ,将高度拉长,这时就自动出现了滚动条,只是此时里面还没有任何内容,下面就在 panel2 中加入一点内容。
发表于:2024-02-03 浏览:322 TAG:
【C#】C# Winform 多个程序之间的通信(非Scoket)
效果功能:打开窗体自动连接主程序,并自动添加到列表,可以向子程序群发消息可以向单个程序单独发送消息在退出程序后,添加的程序列表会自动移除一、概述参考:C# Winfrom程序之间通讯_c# sendmessege copydatastruct 返回多个值_熊思宇的博客-CSDN博客在之前我写过 winform 程序与程序之间的通信,但是这个版本有个问题,那就是只能由两个程序进行通信,同时打开多个程序的话,接收方收到的数据就会一模一样,这次发表这个教程,也就是要解决这个问题。归根结底,还是&amp;nbs
发表于:2024-02-03 浏览:295 TAG:
【C#】C#二分查找(迭代与递归)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;二分搜索被定义为一种在排序数组中使用的搜索算法,通过重复将搜索间隔一分为二。二分查找的思想是利用数组已排序的信息,将时间复杂度降低到O(log N)。二分查找算法示例&nbsp;何时在数据结构中应用二分查找的条件: 应用二分查找算法: &nbsp; &nbsp; &nbsp; &nbsp; 1、数据结构必须是有序的。 &nbsp; &nbsp; &nbsp; &nbsp; 2、访问数据结构的任何元素都
发表于:2024-03-11 浏览:307 TAG:
【C#】C# Winform ListView用法
目录添加标题显示网格添加数据到表格中自定义其他属性结束添加标题在 Winfrom 界面中添加一个 ListView 组件,然后点击右上角的箭头,点击编辑列添加下面标题,然后点击确定此时 ListView 中还是一片空白,不能显示这些标题,在视图这里选择 Details就会如下图所示,虽然标题出来了,内容确实一块白版,此时还是觉得 DataGridView 组件好用啊显示网格此时,表格只是一片空白,可以在属性面板中设置,显示网格,如下图&nbsp;&nbsp;这时,就如下图所示,效果就出来了,但是
发表于:2024-01-31 浏览:367 TAG: