一:前言
时隔两年再次操刀对象NPOI的合并单元格。廉颇老矣尚能饭否,NPOI小小插件大大能量,黄口小儿,唧唧呱呱。
NPOI相对与以前来说两年有了一点儿小的进步,去识别泛型。更简易的代码书写方式,更多的去思考,去实践。想两年前,初出江湖。大山一座座。连最基本的api都不能看懂,一波三折,觉得其难之又难。
二:反射和泛型实现ListToDataTable和获取特性定义的文字做列头,废话少说点,上代码
由于本人是写的Demo就用的类库,所以这里的特性继承的是FlagsAttribute(其实他也是继承自Attribute),这是一个简单的列名的特性,方便数据填充
[AttributeUsage(AttributeTargets.Property, Inherited = true)] public class EnitityAttribute: FlagsAttribute { private string columnName; /// <summary> /// 列名 /// </summary> public string ColumnName { get { return columnName; } set { columnName = value; } } }View Code
三:ListToDataTable(感觉Table才是NPOI的绝配吧)
/// <summary> /// 讲list集合转换成datatable /// </summary> /// <param name="list"></param> /// <returns></returns> public static DataTable ListToDataTable(IList list) { DataTable result = new System.Data.DataTable(); if (list.Count > 0) { PropertyInfo[] propertys = list[0].GetType().GetProperties(); foreach (PropertyInfo pi in propertys) { //获取类型 Type colType = pi.PropertyType; //当类型为Nullable<>时 if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>))) { colType = colType.GetGenericArguments()[0]; } //判断是否含有EnitityAttribute if (pi.IsDefined(typeof(EnitityAttribute),true)) { //获取EnitityAttribute所对应的ColumnName值 var objecet = pi.GetCustomAttribute(typeof(EnitityAttribute), true); EnitityAttribute attr = objecet as EnitityAttribute; result.Columns.Add(attr.ColumnName, colType); } else { result.Columns.Add(pi.Name, colType); } } for (int i = 0; i < list.Count; i++) { ArrayList tempList = new ArrayList(); foreach (PropertyInfo pi in propertys) { object obj = pi.GetValue(list[i], null); tempList.Add(obj); } object[] array = tempList.ToArray(); result.LoadDataRow(array, true); } } return result; }View Code
四:去实现单元格的合并
HSSFWorkbook book = new HSSFWorkbook(); NPOI.SS.UserModel.ISheet sheet = book.CreateSheet("Sheet1"); NPOI.SS.UserModel.IRow headerrow = sheet.CreateRow(0); ICellStyle style = book.CreateCellStyle(); style.Alignment = HorizontalAlignment.Center; style.FillForegroundColor = NPOI.HSSF.Util.HSSFColor.DarkTeal.Index; var font = book.CreateFont(); font.FontHeightInPoints = 10; font.IsBold = true; style.SetFont(font); //添加列名 for (int i = 0; i < dt.Columns.Count; i++) { ICell cell = headerrow.CreateCell(i); cell.SetCellValue(dt.Columns[i].ColumnName); cell.CellStyle = style; } //添加第一行数据 IRow row = sheet.CreateRow(1); for (int j = 0; j < dt.Columns.Count; j++) { string cellText = dt.Rows[0][j].ToString(); if (!string.IsNullOrWhiteSpace(cellText)) row.CreateCell(j).SetCellValue(cellText); else row.CreateCell(j).SetCellValue("-"); } //从第二行开始循环,和上一行进行判断,如果相同,则合并 for (int i = 1; i < dt.Rows.Count; i++) { row = sheet.CreateRow(i + 1); for (int j = 0; j < dt.Columns.Count; j++) { string cellText = dt.Rows[i][j].ToString(); if (!string.IsNullOrWhiteSpace(cellText)) row.CreateCell(j).SetCellValue(cellText); else row.CreateCell(j).SetCellValue("-"); } } //合并单元格样式 ICellStyle cellstyle = book.CreateCellStyle(); cellstyle.VerticalAlignment = VerticalAlignment.Center; cellstyle.Alignment = HorizontalAlignment.Center; //合并行 //总计单元行数 int count = dt.Rows.Count+1; for (int k = 0; k < dt.Columns.Count; k++) { for (int i = 1; i < count; i++) { //获取当前行第0列 string value = sheet.GetRow(i).GetCell(k).StringCellValue; int end = i; //找到结束为止 for (int j = i + 1; j < count; j++) { //获取下一行行第0列 string value1 = sheet.GetRow(j).GetCell(k).StringCellValue; if (value != value1) { end = j - 1; break; } else if (value == value1 || j == dt.Rows.Count) { if (!string.IsNullOrEmpty(value) && !string.IsNullOrEmpty(value1) && !value.Equals("-") && !value1.Equals("-")) { end = j; } } } if (i != end) { sheet.AddMergedRegion(new CellRangeAddress(i, end, k, k));//和并单元格 } var cell = sheet.GetRow(i).GetCell(k); cell.CellStyle = cellstyle; i = end; } } style.Alignment = HorizontalAlignment.Center; style.VerticalAlignment = VerticalAlignment.Center; FileStream fileStream = new FileStream($"D:\\{title}.xls", FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite); book.Write(fileStream); fileStream.Close();View Code
基本上照着抄就应该可以实现了。少Copy多写代码