NPOI实现两级分组合并功能(示例讲解)
程序员文章站
2023-12-19 16:26:22
npoi版本:2.2.1.0
最近公司有这样的需求:
统计每个部门下面,多个费用使用情况。部门存在多级,但统计时,只需统计到2级,2级以下的,归到第2级的部门下。并且要...
npoi版本:2.2.1.0
最近公司有这样的需求:
统计每个部门下面,多个费用使用情况。部门存在多级,但统计时,只需统计到2级,2级以下的,归到第2级的部门下。并且要求,第2级部门有个小计,第1级部门需要有个合计。最后,还需提供总计。
本来对npoi研究的还不够深入的,以前都是直接通过别人提供的代码来实现对datatable中的数据进行全部导出,但里面不带合并,及合计功能,不满足当前需求。不得已,只有好好地研究一下了。还好,最终实现了要求。
在此,也感谢其他提供相关资料的人员,让我实现了此功能。
简要说明一下使用:
1、export2template2方法直接使用。datatable原始数据,必须是已经按要求排好序的数据。全部是逐行向下处理。
2、要导出的列名,取自cellkeys中。列名必须为source中存在的。
3、相同值合并的第1列,为cellkeys[0],合并的第2列,为cellkeys[1],如需要其它列的合并,可以此基础上,按自己的需求进行调整。(合并时,只会比较上下行的数据内容)
4、要导出的数据中,数值类型,自动居右。其它类型,自动居中。
5、小计,合计,总计的字体,全部加黑
6、小计,合计,总计,自动对数值类型进行汇总。其它类型数据全部置空。
7、合并的列数:mergecolumns。如果>2,自动只处理前2列。如果<1,则不做合并处理。
直接上可用的代码:
/// <summary> /// 根据模版导出excel -- 特别处理,每个分组带合计 /// </summary> /// <param name="source">源datatable</param> /// <param name="cellkeys">需要导出的对应的列字段 例:string[] cellkeys = { "date","remarks" };</param> /// <param name="strfilename">要保存的文件名称(包含后缀) 例:"要保存的文件名.xls"</param> /// <param name="templatefile">模版文件名(包含路径后缀) 例:"模板文件名.xls"</param> /// <param name="rowindex">从第几行开始创建数据行,第一行为0</param> /// <param name="mergecolumns">值相同时,可合并的前几列 最多支持2列 1=只合并第一列,2=判断前2列</param> /// <param name="isconver">是否覆盖数据,=false,将把原数据下移。=true,将覆盖插入行后面的数据</param> /// <param name="istotal">是否带小计/合计项</param> /// <param name="addalltotal">是否添加总计项</param> /// <returns>是否导出成功</returns> public static bool export2template2(datatable source, string[] cellkeys, string strfilename, string templatefile, int rowindex, int mergecolumns, bool isconver, bool istotal, bool addalltotal) { bool bn = false; int cellcount = cellkeys.length; //总列数,第一列为0 // iworkbook workbook = null; hssfworkbook workbook = null; string temp0 = "", temp1 = ""; int start0 = 0, start1 = 0; // 记录1,2列值相同的开始序号 int end0 = 0, end1 = 0;// 记录1,2列值相同的结束序号 try { using (filestream file = new filestream(templatefile, filemode.open, fileaccess.read)) { workbook = new hssfworkbook(file); } #region 定义四类数据的单元格样式 // 内容数据格式 -- 数值 icellstyle stylenum = workbook.createcellstyle(); stylenum.borderbottom = borderstyle.thin; stylenum.borderleft = borderstyle.thin; stylenum.borderright = borderstyle.thin; stylenum.bordertop = borderstyle.thin; // stylenum.verticalalignment = verticalalignment.center; // stylenum.alignment = horizontalalignment.center; // 内容数据格式 -- 字符串(做居中处理) icellstyle stylestr = workbook.createcellstyle(); stylestr.borderbottom = borderstyle.thin; stylestr.borderleft = borderstyle.thin; stylestr.borderright = borderstyle.thin; stylestr.bordertop = borderstyle.thin; stylestr.verticalalignment = verticalalignment.center; stylestr.alignment = horizontalalignment.center; // 汇总数据格式 -- 数值 icellstyle styletotalnum = workbook.createcellstyle(); styletotalnum.borderbottom = borderstyle.thin; styletotalnum.borderleft = borderstyle.thin; styletotalnum.borderright = borderstyle.thin; styletotalnum.bordertop = borderstyle.thin; styletotalnum.fillforegroundcolor = npoi.hssf.util.hssfcolor.grey25percent.index; styletotalnum.fillpattern = fillpattern.solidforeground; styletotalnum.fillbackgroundcolor = npoi.hssf.util.hssfcolor.red.index; // 设置字体颜色 hssffont ffont0 = (hssffont)workbook.createfont(); // ffont0.fontheight = 14 * 14; // ffont0.fontname = "宋体"; ffont0.isbold = true; //ffont0.color = hssfcolor.red.index; styletotalnum.setfont(ffont0); // 汇总数据格式 -- 字符串(做居中处理) icellstyle styletotalstr = workbook.createcellstyle(); styletotalstr.borderbottom = borderstyle.thin; styletotalstr.borderleft = borderstyle.thin; styletotalstr.borderright = borderstyle.thin; styletotalstr.bordertop = borderstyle.thin; styletotalstr.verticalalignment = verticalalignment.center; styletotalstr.alignment = horizontalalignment.center; styletotalstr.fillforegroundcolor = npoi.hssf.util.hssfcolor.grey25percent.index; styletotalstr.fillpattern = fillpattern.solidforeground; // 设置字体颜色 hssffont ffont1 = (hssffont)workbook.createfont(); // ffont1.fontheight = 14 * 14; // ffont1.fontname = "宋体"; ffont1.isbold = true; //ffont.color = hssfcolor.red.index; styletotalstr.setfont(ffont1); #endregion isheet sheet = workbook.getsheetat(0); // 打开第一个sheet页 if (sheet != null && source != null && source.rows.count > 0) // 模板内容为空,不做处理 { irow row; for (int i = 0, len = source.rows.count; i < len; i++) { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 #region 第一行,写入数据后,对变量赋初值 if (i == 0) // 第一行,赋初值 { row = sheet.createrow(rowindex); #region 创建列并插入数据 //创建列并插入数据 for (int index = 0; index < cellcount; index++) { icell cell = row.createcell(index); string strvalue = !(source.rows[i][cellkeys[index]] is dbnull) ? source.rows[i][cellkeys[index]].tostring() : string.empty; // 其它列数据,数值进行汇总 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": int intv = 0; int.tryparse(strvalue, out intv); cell.cellstyle = stylenum; // 设置格式 cell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": double doubv = 0; double.tryparse(strvalue, out doubv); cell.cellstyle = stylenum; // 设置格式 cell.setcellvalue(doubv); break; default: cell.cellstyle = stylestr; // 设置格式 cell.setcellvalue(strvalue); break; } } #endregion if (mergecolumns > 0) { temp0 = source.rows[i][cellkeys[0]].tostring(); // 保存第1列值 start0 = rowindex; end0 = rowindex; } if (mergecolumns > 1) { temp1 = source.rows[i][cellkeys[1]].tostring(); // 保存第2列值 start1 = rowindex; end1 = rowindex; } rowindex++; continue; } #endregion // 不是第一行数据的处理 // 判断1列值变化没 string celltext0 = source.rows[i][cellkeys[0]].tostring(); if (temp0 != celltext0) // 第1列值有变化 { #region 第2列要合并 if (mergecolumns > 1) // 第2列要合并 { if (start1 != end1) // 开始行和结束行不相同,才进行合并 { cellrangeaddress region1 = new cellrangeaddress(start1, end1, 1, 1); // 合并第二列 sheet.addmergedregion(region1); } #region 第2列加小计 if (istotal) // 加小计 { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 irow rowtotal1 = sheet.createrow(rowindex); //创建列并插入数据 #region 插入小计数据 for (int index = 0; index < cellcount; index++) { object obj1; icell newcell = rowtotal1.createcell(index); if (index == 0) //第1列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue(temp0); continue; } if (index == 1) // 第2列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue("小计"); continue; } // 其它列数据,数值进行汇总 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' and {2} = '{3}' ", cellkeys[0], temp0, cellkeys[1], temp1)); int intv = 0; int.tryparse(obj1.tostring(), out intv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' and {2} = '{3}' ", cellkeys[0], temp0, cellkeys[1], temp1)); double doubv = 0; double.tryparse(obj1.tostring(), out doubv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(doubv); break; default: newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); break; } } #endregion // 合并小计 cellrangeaddress region0 = new cellrangeaddress(rowindex, rowindex, 1, 2); // 合并小计 sheet.addmergedregion(region0); } #endregion temp1 = source.rows[i][cellkeys[1]].tostring(); end0++; rowindex++; } #endregion #region 第1列要合并 if (mergecolumns > 0) // 第1列要合并 { if (start0 != end0) // 开始行和结束行不相同,才进行合并 { cellrangeaddress region0 = new cellrangeaddress(start0, end0, 0, 0); // 合并第二列 sheet.addmergedregion(region0); } #region 第1列加合计 if (istotal) // 加合计 { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 irow rowtotal0 = sheet.createrow(rowindex); //创建列并插入数据 #region 加合计列 for (int index = 0; index < cellcount; index++) { object obj1; icell newcell = rowtotal0.createcell(index); if (index == 0) { newcell.cellstyle = styletotalstr; newcell.setcellvalue("合计"); //第1列 continue; } if (index == 1) { newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); // 第2列 continue; } switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' ", cellkeys[0], temp0)); int intv = 0; int.tryparse(obj1.tostring(), out intv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' ", cellkeys[0], temp0)); double doubv = 0; double.tryparse(obj1.tostring(), out doubv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(doubv); break; default: newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); break; } } #endregion // 合并合计 cellrangeaddress region0 = new cellrangeaddress(rowindex, rowindex, 0, 2); // 合并合计 sheet.addmergedregion(region0); end0++; rowindex++; } #endregion temp0 = celltext0; } #endregion // 重新赋值 start0 = rowindex; end0 = rowindex; start1 = rowindex; end1 = rowindex; } else // 第1列值没有变化 { end0++; // 判断第2列是否有变化 string celltext1 = source.rows[i][cellkeys[1]].tostring(); if (celltext1 != temp1) // 第1列没变,第2列变化 { #region 第2列要合并 if (mergecolumns > 1) // 第2列要合并 { if (start1 != end1) // 开始行和结束行不相同,才进行合并 { cellrangeaddress region1 = new cellrangeaddress(start1, end1, 1, 1); // 合并第二列 sheet.addmergedregion(region1); } #region 第2列加小计 if (istotal) // 加小计 { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 irow rowtotal1 = sheet.createrow(rowindex); //创建列并插入数据 #region 插入小计数据 for (int index = 0; index < cellcount; index++) { object obj1; icell newcell = rowtotal1.createcell(index); if (index == 0) //第1列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue(temp0); continue; } if (index == 1) // 第2列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue("小计"); continue; } // 其它列数据,数值进行汇总 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' and {2} = '{3}' ", cellkeys[0], temp0, cellkeys[1], temp1)); int intv = 0; int.tryparse(obj1.tostring(), out intv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' and {2} = '{3}' ", cellkeys[0], temp0, cellkeys[1], temp1)); double doubv = 0; double.tryparse(obj1.tostring(), out doubv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(doubv); break; default: newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); break; } } #endregion // 合并小计 cellrangeaddress region0 = new cellrangeaddress(rowindex, rowindex, 1, 2); // 合并小计 sheet.addmergedregion(region0); end0++; rowindex++; } temp1 = celltext1; // 要合并,才进行重新赋值 start1 = rowindex; end1 = rowindex; #endregion } #endregion } else // 第1列值没变,第2列也没变 end1++; } // 插入当前数据 row = sheet.createrow(rowindex); #region 创建行并插入当前记录的数据 //创建行并插入当前记录的数据 for (int index = 0; index < cellcount; index++) { icell cell = row.createcell(index);<br> string strvalue = !(source.rows[i][cellkeys[index]] is dbnull) ? source.rows[i][cellkeys[index]].tostring() : string.empty; // 取值 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": int intv = 0; int.tryparse(strvalue, out intv); cell.cellstyle = stylenum; cell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": double doubv = 0; double.tryparse(strvalue, out doubv); cell.cellstyle = stylenum; cell.setcellvalue(doubv); break; default: cell.cellstyle = stylestr; cell.setcellvalue(strvalue); break; } } #endregion // 下移一行 rowindex++; } // 最后一条记录的合计 #region 对第2列进行合并 if (mergecolumns > 1) // 对第2列合并 { if (start1 != end1) // 开始行和结束行不等,进行合并 { cellrangeaddress region1 = new cellrangeaddress(start1, end1, 1, 1); // 合并第二列 sheet.addmergedregion(region1); } #region 第2列加小计 if (istotal) // 加小计 { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 irow rowtotal1 = sheet.createrow(rowindex); //创建列并插入数据 #region 插入小计数据 for (int index = 0; index < cellcount; index++) { object obj1; icell newcell = rowtotal1.createcell(index); #region 列值处理 if (index == 0) //第1列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue(temp0); continue; } if (index == 1) // 第2列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue("小计"); continue; } // 其它列数据,数值进行汇总 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' and {2} = '{3}' ", cellkeys[0], temp0, cellkeys[1], temp1)); int intv = 0; int.tryparse(obj1.tostring(), out intv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' and {2} = '{3}' ", cellkeys[0], temp0, cellkeys[1], temp1)); double doubv = 0; double.tryparse(obj1.tostring(), out doubv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(doubv); break; default: newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); break; } #endregion } #endregion // 合并小计 cellrangeaddress region0 = new cellrangeaddress(rowindex, rowindex, 1, 2); // 合并小计 sheet.addmergedregion(region0); rowindex++; end0++; } #endregion } #endregion #region 对第1列合并 if (mergecolumns > 0) // 对第1列合并 { if (start0 != end0) // 开始行和结束行不等,进行合并 { cellrangeaddress region1 = new cellrangeaddress(start0, end0, 0, 0); // 合并第二列 sheet.addmergedregion(region1); } #region 第1列加合计 if (istotal) // 加合计 { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 irow rowtotal0 = sheet.createrow(rowindex); //创建列并插入数据 #region 插入合计数据 for (int index = 0; index < cellcount; index++) { object obj1; icell newcell = rowtotal0.createcell(index); #region 列值处理 if (index == 0) //第1列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue("合计"); continue; } if (index == 1) // 第2列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); continue; } // 其它列数据,数值进行汇总 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' ", cellkeys[0], temp0)); int intv = 0; newcell.cellstyle = styletotalnum; int.tryparse(obj1.tostring(), out intv); newcell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), string.format("{0} = '{1}' ", cellkeys[0], temp0)); double doubv = 0; double.tryparse(obj1.tostring(), out doubv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(doubv); break; default: newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); break; } #endregion } #endregion // 合并合计 cellrangeaddress region0 = new cellrangeaddress(rowindex, rowindex, 0, 2); // 合并合计 sheet.addmergedregion(region0); } rowindex++; #endregion } #endregion #region 进行汇总 - 加总计 if (addalltotal) // 加总计 { if (!isconver) sheet.shiftrows(rowindex, sheet.lastrownum, 1, true, false); // 不覆盖,数据向下移 irow rowtotal0 = sheet.createrow(rowindex); //创建列并插入数据 #region 插入总计数据 for (int index = 0; index < cellcount; index++) { object obj1; icell newcell = rowtotal0.createcell(index); #region 列值处理 if (index == 0) //第1列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue("总计"); continue; } if (index == 1) // 第2列 { newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); continue; } // 其它列数据,数值进行汇总 switch (source.columns[cellkeys[index]].datatype.tostring()) { case "system.int16": //整型 case "system.int32": case "system.int64": case "system.byte": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), ""); int intv = 0; int.tryparse(obj1.tostring(), out intv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(intv); break; case "system.decimal": //浮点型 case "system.double": case "system.single": obj1 = source.compute(string.format("sum({0})", cellkeys[index]), ""); double doubv = 0; double.tryparse(obj1.tostring(), out doubv); newcell.cellstyle = styletotalnum; newcell.setcellvalue(doubv); break; default: newcell.cellstyle = styletotalstr; newcell.setcellvalue(""); break; } #endregion } #endregion // 合并总计 cellrangeaddress region0 = new cellrangeaddress(rowindex, rowindex, 0, 2); // 合并总计 sheet.addmergedregion(region0); } #endregion } return save2xls(strfilename, workbook); // 保存为xls文件 } catch (exception ex) { // filehelper.writeline(logfile, "处理数据异常:" + ex.message); // msg = ex.message; } return bn; }
保存文件的代码:
public static bool save2xls(string filename, iworkbook workbook) { bool bn = false; try { filestream fs = new filestream(filename, filemode.openorcreate); memorystream ms = new memorystream(); workbook.write(ms); binarywriter w = new binarywriter(fs); w.write(ms.toarray()); fs.close(); ms.close(); bn = true; } catch(exception ex) { //filehelper.writeline(logfile, "保存文件异常:" + ex.message); } return bn; }
以上这篇npoi实现两级分组合并功能(示例讲解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。