分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUt
自ExcelUtility类推出以来,经过项目中的实际使用与不断完善,现在又做了许多的优化并增加了许多的功能,本篇不再讲述原理,直接贴出示例代码以及相关的模板、结果图,以便大家快速掌握,另外这些示例说明我也已同步到GIT中,大家可以下载与学习,不足之处,
自ExcelUtility类推出以来,经过项目中的实际使用与不断完善,现在又做了许多的优化并增加了许多的功能,本篇不再讲述原理,直接贴出示例代码以及相关的模板、结果图,以便大家快速掌握,另外这些示例说明我也已同步到GIT中,大家可以下载与学习,不足之处,敬请见谅,谢谢!
一、ExcelUtility类库操作说明(模板导出示例)
1.
/// <summary> /// 测试方法:测试依据模板+DataTable来生成EXCEL /// </summary> [TestMethod] public void TestExportToExcelWithTemplateByDataTable() { DataTable dt = GetDataTable();//获取数据 string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/excel.xlsx"; //获得EXCEL模板路径 SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder();//实例化一个局部元素格式化器 partFormatterBuilder.AddFormatter("Title", "跨越IT学员");//将模板表格中Title的值设置为跨越IT学员 formatterContainers.AppendFormatterBuilder(partFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 CellFormatterBuilder cellFormatterBuilder = new CellFormatterBuilder();//实例化一个单元格格式化器 cellFormatterBuilder.AddFormatter("rptdate", DateTime.Today.ToString("yyyy-MM-dd HH:mm"));//将模板表格中rptdate的值设置为当前日期 formatterContainers.AppendFormatterBuilder(cellFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 //实例化一个表格格式化器,dt.Select()是将DataTable转换成DataRow[],name表示的模板表格中第一行第一个单元格要填充的数据参数名 TableFormatterBuilder<DataRow> tableFormatterBuilder = new TableFormatterBuilder<DataRow>(dt.Select(), "name"); tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<DataRow, object>>{ {"name",r=>r["Col1"]},//将模板表格中name对应DataTable中的列Col1 {"sex",r=>r["Col2"]},//将模板表格中sex对应DataTable中的列Col2 {"km",r=>r["Col3"]},//将模板表格中km对应DataTable中的列Col3 {"score",r=>r["Col4"]},//将模板表格中score对应DataTable中的列Col4 {"result",r=>r["Col5"]}//将模板表格中result对应DataTable中的列Co5 }); formatterContainers.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, "table", formatterContainers); Assert.IsTrue(File.Exists(excelPath)); }
模板如下:
导出结果如下:
2.
/// <summary> /// 测试方法:测试依据模板+List来生成EXCEL /// </summary> [TestMethod] public void TestExportToExcelWithTemplateByList() { List<Student> studentList = GetStudentList();//获取数据 string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/excel.xlsx"; //获得EXCEL模板路径 SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder();//实例化一个局部元素格式化器 partFormatterBuilder.AddFormatter("Title", "跨越IT学员");//将模板表格中Title的值设置为跨越IT学员 formatterContainers.AppendFormatterBuilder(partFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 CellFormatterBuilder cellFormatterBuilder = new CellFormatterBuilder();//实例化一个单元格格式化器 cellFormatterBuilder.AddFormatter("rptdate", DateTime.Today.ToString("yyyy-MM-dd HH:mm"));//将模板表格中rptdate的值设置为当前日期 formatterContainers.AppendFormatterBuilder(cellFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 //实例化一个表格格式化器,studentList本身就是可枚举的无需转换,name表示的模板表格中第一行第一个单元格要填充的数据参数名 TableFormatterBuilder<Student> tableFormatterBuilder = new TableFormatterBuilder<Student>(studentList, "name"); tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<Student, object>>{ {"name",r=>r.Name},//将模板表格中name对应Student对象中的属性Name {"sex",r=>r.Sex},//将模板表格中sex对应Student对象中的属性Sex {"km",r=>r.KM},//将模板表格中km对应Student对象中的属性KM {"score",r=>r.Score},//将模板表格中score对应Student对象中的属性Score {"result",r=>r.Result}//将模板表格中result对应Student对象中的属性Result }); formatterContainers.AppendFormatterBuilder(tableFormatterBuilder); string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, "table", formatterContainers); Assert.IsTrue(File.Exists(excelPath)); }
模板同上一个模板
导出结果如下:
3.
/// <summary> /// 测试方法:测试依据模板+DataTable来生成多表格EXCEL(注意:由于ExcelReport框架限制,目前仅支持模板文件格式为:xls) /// </summary> [TestMethod] public void TestExportToRepeaterExcelWithTemplateByDataTable() { DataTable dt = GetDataTable();//获取数据 string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/excel2.xls"; //获得EXCEL模板路径 SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 //实例化一个可重复表格格式化器,dt.Select()是将DataTable转换成DataRow[],rpt_begin表示的模板表格开始位置参数名,rpt_end表示的模板表格结束位置参数名 RepeaterFormatterBuilder<DataRow> tableFormatterBuilder = new RepeaterFormatterBuilder<DataRow>(dt.Select(), "rpt_begin", "rpt_end"); tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<DataRow, object>>{ {"sex",r=>r["Col2"]},//将模板表格中sex对应DataTable中的列Col2 {"km",r=>r["Col3"]},//将模板表格中km对应DataTable中的列Col3 {"score",r=>r["Col4"]},//将模板表格中score对应DataTable中的列Col4 {"result",r=>r["Col5"]}//将模板表格中result对应DataTable中的列Co5 }); PartFormatterBuilder<DataRow> partFormatterBuilder2 = new PartFormatterBuilder<DataRow>();//实例化一个可嵌套的局部元素格式化器 partFormatterBuilder2.AddFormatter("name", r => r["Col1"]);//将模板表格中name对应DataTable中的列Col1 tableFormatterBuilder.AppendFormatterBuilder(partFormatterBuilder2);//添加到可重复表格格式化器中,作为其子格式化器 CellFormatterBuilder<DataRow> cellFormatterBuilder = new CellFormatterBuilder<DataRow>();//实例化一个可嵌套的单元格格式化器 cellFormatterBuilder.AddFormatter("rptdate", r => DateTime.Today.ToString("yyyy-MM-dd HH:mm"));//将模板表格中rptdate的值设置为当前日期 tableFormatterBuilder.AppendFormatterBuilder(cellFormatterBuilder);//添加到可重复表格格式化器中,作为其子格式化器 formatterContainers.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, "multtable", formatterContainers); Assert.IsTrue(File.Exists(excelPath)); }
模板如下:
导出结果如下:
4.
/// <summary> /// 测试方法:测试依据复杂模板(含固定表格,可重复表格)+DataTable来生成EXCEL (注意:由于ExcelReport框架限制,目前仅支持模板文件格式为:xls) /// </summary> [TestMethod] public void TestExportToExcelWithTemplateByList2() { var schoolLevelList = SchoolLevel.GetList(); var classList = ClassInfo.GetList(); string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/mb.xls"; //获得EXCEL模板路径 SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder(); partFormatterBuilder.AddFormatter("school", "跨越小学"); formatterContainers.AppendFormatterBuilder(partFormatterBuilder); TableFormatterBuilder<SchoolLevel> tableFormatterBuilder = new TableFormatterBuilder<SchoolLevel>(schoolLevelList, "lv");//实例化一个表格格式化器 tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<SchoolLevel, object>> { {"lv",r=>r.LevelName}, //模板参数与数据源SchoolLevel属性对应关系,下同 {"clscount",r=>r.ClassCount}, {"lvmaster",r=>r.Master} }); formatterContainers.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 RepeaterFormatterBuilder<ClassInfo> repeaterFormatterBuilder = new RepeaterFormatterBuilder<ClassInfo>(classList, "lv_begin", "lv_end");//实例化一个可重复表格格式化器 repeaterFormatterBuilder.AddFormatters(new Dictionary<string, Func<ClassInfo, object>> { {"class",r=>r.ClassName}, //模板参数与数据源ClassInfo属性对应关系,下同 {"stucount",r=>r.StudentCount}, {"clsmaster",r=>r.Master}, {"lvitem",r=>r.LevelName} }); formatterContainers.AppendFormatterBuilder(repeaterFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, "school", formatterContainers); Assert.IsTrue(File.Exists(excelPath)); }
模板如下:
导出结果如下:
5.
/// <summary> /// 测试方法:测试依据复杂模板(含固定表格,可重复表格中嵌套表格)+DataTable来生成EXCEL (注意:由于ExcelReport框架限制,目前仅支持模板文件格式为:xls) /// </summary> [TestMethod] public void TestExportToExcelWithTemplateByList3() { var schoolLevelList = SchoolLevel.GetList(); var classList = ClassInfo.GetListWithLevels(); string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/mb1.xls"; //获得EXCEL模板路径 SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder(); partFormatterBuilder.AddFormatter("school", "跨越小学"); formatterContainers.AppendFormatterBuilder(partFormatterBuilder); TableFormatterBuilder<SchoolLevel> tableFormatterBuilder = new TableFormatterBuilder<SchoolLevel>(schoolLevelList, "lv");//实例化一个表格格式化器 tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<SchoolLevel, object>> { {"lv",r=>r.LevelName}, //模板参数与数据源SchoolLevel属性对应关系,下同 {"clscount",r=>r.ClassCount}, {"lvmaster",r=>r.Master} }); formatterContainers.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 RepeaterFormatterBuilder<KeyValuePair<string, List<ClassInfo>>> repeaterFormatterBuilder = new RepeaterFormatterBuilder<KeyValuePair<string, List<ClassInfo>>>(classList, "lv_begin", "lv_end"); repeaterFormatterBuilder.AddFormatter("lvitem",r=>r.Key); TableFormatterBuilder<KeyValuePair<string, List<ClassInfo>>,ClassInfo> tableFormatterBuilder2=new TableFormatterBuilder<KeyValuePair<string, List<ClassInfo>>,ClassInfo>(r=>r.Value,"class"); tableFormatterBuilder2.AddFormatter("class",r=>r.ClassName); tableFormatterBuilder2.AddFormatter("stucount",r=>r.StudentCount); tableFormatterBuilder2.AddFormatter("clsmaster",r=>r.Master); repeaterFormatterBuilder.AppendFormatterBuilder(tableFormatterBuilder2); formatterContainers.AppendFormatterBuilder(repeaterFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, "school", formatterContainers); Assert.IsTrue(File.Exists(excelPath)); }
模板如下:
导出结果如下:
6.
/// <summary> /// 测试方法:测试依据复杂模板(多工作薄,且含固定表格,可重复表格)+DataSet来生成EXCEL,只支持XLS /// </summary> [TestMethod] public void TestExportToExcelWithTemplateByDataSet() { var ds = GetDataSet(); string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/mb2.xls"; //获得EXCEL模板路径 Dictionary<string, SheetFormatterContainer> formatterContainerDic = new Dictionary<string, SheetFormatterContainer>(); //实例化一个模板数据格式化容器数组,包含两个SheetFormatterContainer用于格式化两个工作薄 #region 创建第一个工作薄格式化容器,并设置相关参数对应关系 SheetFormatterContainer formatterContainer1 = new SheetFormatterContainer(); PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder(); partFormatterBuilder.AddFormatter("school", "跨越小学"); formatterContainer1.AppendFormatterBuilder(partFormatterBuilder); TableFormatterBuilder<DataRow> tableFormatterBuilder = new TableFormatterBuilder<DataRow>(ds.Tables[0].Select(), "lv");//实例化一个表格格式化器 tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<DataRow, object>> { {"lv",r=>r["Col1"]}, //模板参数与数据源DataTable属性对应关系,下同 {"clscount",r=>r["Col2"]}, {"lvmaster",r=>r["Col3"]} }); formatterContainer1.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 RepeaterFormatterBuilder<DataRow> repeaterFormatterBuilder = new RepeaterFormatterBuilder<DataRow>(ds.Tables[1].Select(), "lv_begin", "lv_end");//实例化一个可重复表格格式化器 repeaterFormatterBuilder.AddFormatters(new Dictionary<string, Func<DataRow, object>> { {"class",r=>r["Col1"]}, //模板参数与数据源ClassInfo属性对应关系,下同 {"stucount",r=>r["Col2"]}, {"clsmaster",r=>r["Col3"]}, {"lvitem",r=>r["Col4"]} }); formatterContainer1.AppendFormatterBuilder(repeaterFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 formatterContainerDic.Add("table1", formatterContainer1);//添加到工作薄格式容器数组中,注意此处的Key值为模板上工作薄的名称,此处即为:table1 #endregion #region 创建第二个工作薄格式化容器,并设置相关参数对应关系 SheetFormatterContainer formatterContainer2 = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder2 = new PartFormatterBuilder();//实例化一个局部元素格式化器 partFormatterBuilder2.AddFormatter("Title", "跨越IT学员");//将模板表格中Title的值设置为跨越IT学员 formatterContainer2.AppendFormatterBuilder(partFormatterBuilder2);//添加到工作薄格式容器中,注意只有添加进去了才会生效 CellFormatterBuilder cellFormatterBuilder2 = new CellFormatterBuilder();//实例化一个单元格格式化器 cellFormatterBuilder2.AddFormatter("rptdate", DateTime.Today.ToString("yyyy-MM-dd HH:mm"));//将模板表格中rptdate的值设置为当前日期 formatterContainer2.AppendFormatterBuilder(cellFormatterBuilder2);//添加到工作薄格式容器中,注意只有添加进去了才会生效 //实例化一个表格格式化器,dt.Select()是将DataTable转换成DataRow[],name表示的模板表格中第一行第一个单元格要填充的数据参数名 TableFormatterBuilder<DataRow> tableFormatterBuilder2 = new TableFormatterBuilder<DataRow>(ds.Tables[2].Select(), "name"); tableFormatterBuilder2.AddFormatters(new Dictionary<string, Func<DataRow, object>>{ {"name",r=>r["Col1"]},//将模板表格中name对应DataTable中的列Col1 {"sex",r=>r["Col2"]},//将模板表格中sex对应DataTable中的列Col2 {"km",r=>r["Col3"]},//将模板表格中km对应DataTable中的列Col3 {"score",r=>r["Col4"]},//将模板表格中score对应DataTable中的列Col4 {"result",r=>r["Col5"]}//将模板表格中result对应DataTable中的列Co5 }); formatterContainer2.AppendFormatterBuilder(tableFormatterBuilder2);//添加到工作薄格式容器中,注意只有添加进去了才会生效 formatterContainerDic.Add("table2", formatterContainer2);//添加到工作薄格式容器数组中,注意此处的Key值为模板上工作薄的名称,此处即为:table2 #endregion string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, formatterContainerDic); Assert.IsTrue(File.Exists(excelPath)); }
模板如下:
导出结果如下:
二、ExcelUtility类库操作说明(嵌入图片示例)
一、 制作模板(含图片)
1. 制作模板的文件格式需为兼容格式,即:xls或xlt;
2. 模板变量(或称为占位符)定义与之前相同,即:$[变量名];
3. 图片变量定义如下:
a) 绘制一个图形,图形形状尽可能的与要显示的图片相同,比如:印章,则可绘制一个圆形;
b) 图形必需是透明背景,边框可要可不要,建议留着,这样后续调整比较方便,如下图中的蓝色透明背景圆形:
c) 图形大小尽可能与要显示的图片大小相同,如下图示:
由于EXCEL上大小默认采用厘米,而图片一般采用像素,所以需要自己换算一下像素对应的厘米数(也可将EXCEL计算单位设为像素,方法自行网上查找);也可网上下载单位转换工具
另外图形属性建议设置成如下图:
温馨提示:图形形状、属性若未设置一般不影响导出效果,但不排除其它异常情况,图形大小是一定要设置,且尽可能与要显示图形大小(高、宽)相同,否则有可能造成导出变形
代码示例:
/// <summary> /// 测试方法:测试依据模板+DataTable+图片来生成包含图片的EXCEL,只支持XLS /// </summary> [TestMethod] public void TestInsertPic() { DataTable dt = GetDataTable();//获取数据 string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/excel.xls"; //获得EXCEL模板路径 SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder();//实例化一个局部元素格式化器 partFormatterBuilder.AddFormatter("Title", "跨越IT学员");//将模板表格中Title的值设置为跨越IT学员d formatterContainers.AppendFormatterBuilder(partFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 CellFormatterBuilder cellFormatterBuilder = new CellFormatterBuilder();//实例化一个单元格格式化器 cellFormatterBuilder.AddFormatter("rptdate", DateTime.Today.ToString("yyyy-MM-dd HH:mm"));//将模板表格中rptdate的值设置为当前日期 formatterContainers.AppendFormatterBuilder(cellFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 //实例化一个表格格式化器,dt.Select()是将DataTable转换成DataRow[],name表示的模板表格中第一行第一个单元格要填充的数据参数名 TableFormatterBuilder<DataRow> tableFormatterBuilder = new TableFormatterBuilder<DataRow>(dt.Select(), "name"); tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<DataRow, object>>{ {"name",r=>r["Col1"]},//将模板表格中name对应DataTable中的列Col1 {"sex",r=>r["Col2"]},//将模板表格中sex对应DataTable中的列Col2 {"km",r=>r["Col3"]},//将模板表格中km对应DataTable中的列Col3 {"score",r=>r["Col4"]},//将模板表格中score对应DataTable中的列Col4 {"result",r=>r["Col5"]}//将模板表格中result对应DataTable中的列Co5 }); formatterContainers.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 string picPath = AppDomain.CurrentDomain.BaseDirectory + "\\tz.png";//图片路径 PictureWithShapeFormatterBuilder pictureBuilder = new PictureWithShapeFormatterBuilder();//实例化一个图片关联图形格式化器 //pictureBuilder.AddFormatter(picPath);//当sheet中只有一个图形时,我们可以省略指定区域,那么默认就是把整个工作薄区域当成一个寻找图形区域,若sheet中包含多个,则应指定区域,替换成如下语句 pictureBuilder.AddFormatter(picPath,5,60000, 0, 3, false);//第一个参数为图片路径,中间4个参数为数字型指定图形寻找的工作薄区域(行索引,列索引,索引从0开始计),最后一个为是否自适应大小,一般不建议使用,除非压缩图片 formatterContainers.AppendFormatterBuilder(pictureBuilder); string excelPath = ExcelUtility.Export.ToExcelWithTemplate(templateFilePath, "table", formatterContainers); Assert.IsTrue(File.Exists(excelPath)); }
模板如下:
注意图片若需要为透明背景格式,则必需使用PNG格式,NPOI支持的图片主要格式有:PNG,JPG
导出结果如下:
温馨提示:
pictureBuilder.AddFormatter(picPath);//当sheet中只有一个图形时,我们可以省略指定区域,那么默认就是把整个工作薄区域当成一个寻找图形区域,若sheet中包含多个,则应指定区域,替换成如下语句
pictureBuilder.AddFormatter(picPath,5,60000, 0, 3, false);//第一个参数为图片路径,中间4个参数为数字型指定图形寻找的工作薄区域(行索引(起止),列索引(起止),索引从0开始计),最后一个为是否自适应大小,一般不建议使用,除非压缩图片
如果图形可能随单元格进行位置调整,那么在指定图形区域时需注意,如果图形会随单元格下移,那么结束行索引(MinRow)就需要指定一个可能的最大值或不指定,如果图形会随单元格右移,那么结束列索引(MinColumn)就需要指定一个可能的最大值或不指定,如果存在多个图形区域,则上述情况都必需给定具体值(可能的最大值),以免造成区域交叉,从而导致图片显示不正确,如下示例:
//图形可能下移,可能右移,那么将结束行设为可能最大值:60000,结束列设为可能最大值:255
pictureBuilder.AddFormatter(picPath, 5, 60000, 0, 255, false);
//此处只指定开始行与开始列,与上面差不多,但建议使用上面的用法
pictureBuilder.AddFormatter(new PictureWithShapeInfo(picPath, new SheetRange() {MinRow=5,MinColumn=0 },false));
特别说明:
1.本类库是基于NPOI+ExcelReport,所有功能凡我的类库能够实现的,NPOI与ExcelReport都可以实现,只是用法及复杂程度不同而矣,我封装的目的就是为了降低大家的学习难度,提高使用效率,免费且开源,源代码同步更新至开源社区的GIT目录中,具体地址请看我该系列之前的文章有列出,在此就不再说明。
2.上述图片关联图形显示功能我是在ExcelReport基础上增加了一个PictureWithShapeFormatter类及其相关的类:PictureWithShapeInfo、SheetRange,因没有关联GIT,所以是在本地更新的,这几个类的代码如下:
PictureWithShapeFormatter:
using NPOI.Extend; using NPOI.HSSF.UserModel; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ExcelReport { public class PictureWithShapeFormatter : ElementFormatter { PRivate PictureWithShapeInfo PictureInfo = null; public PictureWithShapeFormatter(PictureWithShapeInfo pictureInfo) { this.PictureInfo = pictureInfo; } public override void Format(SheetAdapter sheetAdapter) { var sheet = sheetAdapter.CurrentSheet; var shapes = PictureInfo.GetShapes(sheet); bool isCompatible = false; if (sheet is HSSFSheet) { isCompatible = true; } if (shapes == null || shapes.Count <= 0) { throw new Exception(string.Format("未能获取到工作薄[{0}]指定区域的图形对象列表!", sheet.SheetName)); } byte[] bytes = System.IO.File.ReadAllBytes(PictureInfo.FilePath); int pictureIdx = -1; IDrawing drawing = null; IClientAnchor anchor = null; if (isCompatible) { var shape = shapes[0] as HSSFShape; anchor = shape.Anchor as IClientAnchor; drawing = shape.Patriarch; shape.LineStyle = LineStyle.None; } else { var shape = shapes[0] as XSSFShape; anchor = shape.GetAnchor() as IClientAnchor; drawing = shape.GetDrawing(); shape.LineStyle = LineStyle.None; } pictureIdx = sheet.Workbook.AddPicture(bytes, PictureInfo.PictureType); var picture = drawing.CreatePicture(anchor, pictureIdx); if (PictureInfo.AutoSize) { picture.Resize(); } } } }
PictureWithShapeInfo、SheetRange:
using NPOI.SS.UserModel; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using NPOI.HSSF.UserModel; using NPOI.XSSF.UserModel; using NPOI.Extend; namespace ExcelReport { /// <summary> /// 图片关联图形信息 /// 作者:Zuowenjun /// </summary> public class PictureWithShapeInfo { private SheetRange _SheetRange = new SheetRange(); public string FilePath { get; set; } public PictureType PictureType { get; set; } public SheetRange ShapeRange { get { return _SheetRange; } set { if (value != null) { _SheetRange = value; } } } public bool AutoSize { get; set; } public PictureWithShapeInfo() { } public PictureWithShapeInfo(string filePath, SheetRange shapeRange = null, bool autoSize = false) { this.FilePath = filePath; this.ShapeRange = shapeRange; this.AutoSize = autoSize; this.PictureType = GetPictureType(filePath); } public List<object> GetShapes(ISheet sheet) { List<object> shapeAllList = new List<object>(); var shapeContainer = sheet.DrawingPatriarch; if (sheet is HSSFSheet) { var shapeContainerHSSF = sheet.DrawingPatriarch as HSSFShapeContainer; if (null != shapeContainer) { var shapeList = shapeContainerHSSF.Children; foreach (var shape in shapeList) { if (shape is HSSFShape && shape.Anchor is HSSFClientAnchor) { var anchor = shape.Anchor as HSSFClientAnchor; if (IsInternalOrIntersect(ShapeRange.MinRow, ShapeRange.MaxRow, ShapeRange.MinColumn, ShapeRange.MaxColumn, anchor.Row1, anchor.Row2, anchor.Col1, anchor.Col2, true)) { shapeAllList.Add(shape); } } } } } else { var documentPartList = (sheet as XSSFSheet).GetRelations(); foreach (var documentPart in documentPartList) { if (documentPart is XSSFDrawing) { var drawing = (XSSFDrawing)documentPart; var shapeList = drawing.GetShapes(); foreach (var shape in shapeList) { var anchorResult = shape.GetAnchor(); if (shape is XSSFShape && anchorResult is XSSFClientAnchor) { var anchor = anchorResult as XSSFClientAnchor; if (IsInternalOrIntersect(ShapeRange.MinRow, ShapeRange.MaxRow, ShapeRange.MinColumn, ShapeRange.MaxColumn, anchor.Row1, anchor.Row2, anchor.Col1, anchor.Col2, true)) { shapeAllList.Add(shape); } } } } } } return shapeAllList; } private PictureType GetPictureType(string filePath) { string ext = Path.GetExtension(filePath).ToUpper(); switch (ext) { case ".JPG": { return PictureType.JPEG; } case ".PNG": { return PictureType.PNG; } default: { return PictureType.None; } } } private bool IsInternalOrIntersect(int? rangeMinRow, int? rangeMaxRow, int? rangeMinCol, int? rangeMaxCol, int pictureMinRow, int pictureMaxRow, int pictureMinCol, int pictureMaxCol, bool onlyInternal) { int _rangeMinRow = rangeMinRow ?? pictureMinRow; int _rangeMaxRow = rangeMaxRow ?? pictureMaxRow; int _rangeMinCol = rangeMinCol ?? pictureMinCol; int _rangeMaxCol = rangeMaxCol ?? pictureMaxCol; if (onlyInternal) { return (_rangeMinRow <= pictureMinRow && _rangeMaxRow >= pictureMaxRow && _rangeMinCol <= pictureMinCol && _rangeMaxCol >= pictureMaxCol); } else { return ((Math.Abs(_rangeMaxRow - _rangeMinRow) + Math.Abs(pictureMaxRow - pictureMinRow) >= Math.Abs(_rangeMaxRow + _rangeMinRow - pictureMaxRow - pictureMinRow)) && (Math.Abs(_rangeMaxCol - _rangeMinCol) + Math.Abs(pictureMaxCol - pictureMinCol) >= Math.Abs(_rangeMaxCol + _rangeMinCol - pictureMaxCol - pictureMinCol))); } } } /// <summary> /// 工作薄区域 /// 作者:Zuowenjun /// </summary> public class SheetRange { public int? MinRow { get; set; } public int? MaxRow { get; set; } public int? MinColumn { get; set; } public int? MaxColumn { get; set; } public SheetRange() { } public SheetRange(int minRow, int maxRow, int minColumn, int maxColumn) { this.MinRow = minRow; this.MaxRow = maxRow; this.MinColumn = minColumn; this.MaxColumn = maxColumn; } public override bool Equals(object obj) { bool equalResult = false; equalResult = base.Equals(obj); if (!equalResult) { var otherSheetRange = obj as SheetRange; if (otherSheetRange != null) { equalResult = (this.MinRow <= otherSheetRange.MinRow && this.MaxRow >= otherSheetRange.MaxRow && this.MinColumn <= otherSheetRange.MinColumn && this.MaxColumn >= otherSheetRange.MaxColumn); } } return equalResult; } public override int GetHashCode() { return this.ToString().GetHashCode(); } public override string ToString() { return string.Format("MinRow:{0},MaxRow:{1},MinColumn:{2},MaxColumn:{3}", this.MinRow, this.MaxRow, this.MinColumn, this.MaxColumn); } } }
分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility 其它相关文章链接:
分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续篇)

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Quark Netdisk und Baidu Netdisk sind beide sehr praktische Speichertools. Viele Benutzer fragen sich, ob diese beiden Softwareprogramme kompatibel sind. Wie teile ich Quark Netdisk mit Baidu Netdisk? Auf dieser Website erfahren Sie ausführlich, wie Sie Quark Network Disk-Dateien auf der Baidu Network Disk speichern. So speichern Sie Dateien von Quark Network Disk auf Baidu Network Disk Methode 1. Wenn Sie wissen möchten, wie Sie Dateien von Quark Network Disk auf Baidu Network Disk übertragen, laden Sie zunächst die Dateien herunter, die auf Quark Network Disk gespeichert werden müssen, und öffnen Sie sie dann Wählen Sie im Baidu Network Disk-Client den Ordner aus, in dem die komprimierte Datei gespeichert werden soll, und doppelklicken Sie, um den Ordner zu öffnen. 2. Klicken Sie nach dem Öffnen des Ordners oben links im Fenster auf „Hochladen“. 3. Suchen Sie die komprimierte Datei, die auf Ihren Computer hochgeladen werden soll, und klicken Sie, um sie auszuwählen.

Wenn wir diese Plattform zum Anhören von Liedern nutzen, sollten die meisten davon einige Lieder enthalten, die Sie hören möchten. Natürlich können einige Dinge nicht angehört werden, da es kein Urheberrecht gibt. Natürlich können wir einige Lieder auch direkt verwenden Lokal importiert. Gehen Sie dorthin, damit Sie zuhören können. Wir können einige Songs herunterladen und direkt in MP3-Formate konvertieren, sodass sie zum Importieren und für andere Zwecke auf dem Mobiltelefon gescannt werden können. Die meisten Benutzer wissen jedoch nicht viel über das Importieren lokaler Songinhalte. Um diese Probleme gut zu lösen, wird Ihnen der Herausgeber heute auch die Inhaltsmethode erklären, mit der Sie bessere Entscheidungen treffen können Sie sind interessiert,

1. Zuerst rufen wir NetEase Cloud Music auf und klicken dann auf die Software-Homepage-Oberfläche, um die Song-Wiedergabeoberfläche aufzurufen. 2. Suchen Sie dann in der Song-Wiedergabeoberfläche die Schaltfläche für die Freigabefunktion oben rechts, wie im roten Feld in der Abbildung unten gezeigt, klicken Sie, um den Freigabekanal im Freigabekanal auszuwählen, und klicken Sie auf die Option „Teilen für“. Wählen Sie unten den ersten Eintrag „WeChat Moments“ aus, mit dem Sie Inhalte für WeChat Moments freigeben können.

Wie implementiert man die doppelte WeChat-Anmeldung auf Huawei-Mobiltelefonen? Mit dem Aufkommen der sozialen Medien ist WeChat zu einem unverzichtbaren Kommunikationsmittel im täglichen Leben der Menschen geworden. Viele Menschen können jedoch auf ein Problem stoßen: Sie können sich gleichzeitig auf demselben Mobiltelefon bei mehreren WeChat-Konten anmelden. Für Huawei-Mobiltelefonbenutzer ist es nicht schwierig, eine doppelte WeChat-Anmeldung zu erreichen. In diesem Artikel wird erläutert, wie eine doppelte WeChat-Anmeldung auf Huawei-Mobiltelefonen erreicht wird. Erstens bietet das EMUI-System, das mit Huawei-Mobiltelefonen geliefert wird, eine sehr praktische Funktion – das doppelte Öffnen von Anwendungen. Durch die doppelte Öffnungsfunktion der Anwendung können Benutzer gleichzeitig

Die Programmiersprache PHP ist ein leistungsstarkes Werkzeug für die Webentwicklung, das eine Vielzahl unterschiedlicher Programmierlogiken und Algorithmen unterstützen kann. Unter diesen ist die Implementierung der Fibonacci-Folge ein häufiges und klassisches Programmierproblem. In diesem Artikel stellen wir vor, wie Sie die Fibonacci-Folge mit der Programmiersprache PHP implementieren, und fügen spezifische Codebeispiele bei. Die Fibonacci-Folge ist eine mathematische Folge, die wie folgt definiert ist: Das erste und das zweite Element der Folge sind 1, und ab dem dritten Element ist der Wert jedes Elements gleich der Summe der beiden vorherigen Elemente. Die ersten paar Elemente der Sequenz

Vor kurzem hat der Baidu Netdisk Android-Client eine neue Version 8.0.0 eingeführt. Diese Version bringt nicht nur viele Änderungen, sondern fügt auch viele praktische Funktionen hinzu. Am auffälligsten ist unter anderem die Verbesserung der Ordnerfreigabefunktion. Jetzt können Benutzer ganz einfach Freunde einladen, beizutreten und wichtige Dateien im Beruf und im Privatleben zu teilen, was eine bequemere Zusammenarbeit und gemeinsame Nutzung ermöglicht. Wie teilen Sie die Dateien, die Sie benötigen, mit Ihren Freunden? Ich hoffe, dass Ihnen der Herausgeber dieser Website weiterhelfen kann. 1) Öffnen Sie die Baidu Cloud APP, wählen Sie zuerst den entsprechenden Ordner auf der Startseite aus und klicken Sie dann auf das Symbol [...] in der oberen rechten Ecke der Benutzeroberfläche (wie unten gezeigt). 2) Klicken Sie dann auf [+]. Klicken Sie auf die Spalte „Gemeinsame Mitglieder“ 】 und markieren Sie abschließend alle

Titel: Methoden und Codebeispiele zur Lösung des Problems verstümmelter chinesischer Daten, die in Oracle importiert werden. Beim Importieren chinesischer Daten in die Oracle-Datenbank treten häufig verstümmelte Zeichen auf. Dies kann auf falsche Datenbank-Zeichensatzeinstellungen oder Probleme bei der Kodierungskonvertierung zurückzuführen sein. . Um dieses Problem zu lösen, können wir einige Methoden anwenden, um sicherzustellen, dass die importierten chinesischen Daten korrekt angezeigt werden können. Im Folgenden finden Sie einige Lösungen und spezifische Codebeispiele: 1. Überprüfen Sie die Zeichensatzeinstellungen der Datenbank. In der Oracle-Datenbank sind die Zeichensatzeinstellungen

So implementieren Sie die WeChat-Klonfunktion auf Huawei-Mobiltelefonen Mit der Popularität sozialer Software und der zunehmenden Bedeutung von Datenschutz und Sicherheit rückt die WeChat-Klonfunktion allmählich in den Mittelpunkt der Aufmerksamkeit der Menschen. Die WeChat-Klonfunktion kann Benutzern helfen, sich gleichzeitig bei mehreren WeChat-Konten auf demselben Mobiltelefon anzumelden, was die Verwaltung und Nutzung erleichtert. Es ist nicht schwierig, die WeChat-Klonfunktion auf Huawei-Mobiltelefonen zu implementieren. Sie müssen lediglich die folgenden Schritte ausführen. Schritt 1: Stellen Sie sicher, dass die Version Ihres Mobiltelefonsystems und die WeChat-Version den Anforderungen entsprechen. Stellen Sie zunächst sicher, dass die Version Ihres Huawei-Mobiltelefonsystems sowie die WeChat-App auf die neueste Version aktualisiert wurden.
