自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 其它相關文章鏈接:
