ExcelExtension.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /* ==============================================================================
  2. * 功能描述:ExcelExtension
  3. * 创 建 者:Garrett
  4. * 创建日期:2019/1/14 10:16:40
  5. * ==============================================================================*/
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using NPOI.SS.UserModel;
  12. using NPOI.SS.Util;
  13. namespace SAGA.DotNetUtils.NPOI
  14. {
  15. /// <summary>
  16. /// 表示单元格的维度,通常用于表达合并单元格的维度
  17. /// </summary>
  18. public struct Dimension
  19. {
  20. /// <summary>
  21. /// 含有数据的单元格(通常表示合并单元格的第一个跨度行第一个跨度列),该字段可能为null
  22. /// </summary>
  23. public ICell DataCell;
  24. /// <summary>
  25. /// 行跨度(跨越了多少行)
  26. /// </summary>
  27. public int RowSpan;
  28. /// <summary>
  29. /// 列跨度(跨越了多少列)
  30. /// </summary>
  31. public int ColumnSpan;
  32. /// <summary>
  33. /// 合并单元格的起始行索引
  34. /// </summary>
  35. public int FirstRowIndex;
  36. /// <summary>
  37. /// 合并单元格的结束行索引
  38. /// </summary>
  39. public int LastRowIndex;
  40. /// <summary>
  41. /// 合并单元格的起始列索引
  42. /// </summary>
  43. public int FirstColumnIndex;
  44. /// <summary>
  45. /// 合并单元格的结束列索引
  46. /// </summary>
  47. public int LastColumnIndex;
  48. }
  49. public static class ExcelExtension
  50. {
  51. /// <summary>
  52. /// 判断指定行列所在的单元格是否为合并单元格,并且输出该单元格的维度
  53. /// </summary>
  54. /// <param name="sheet">Excel工作表</param>
  55. /// <param name="rowIndex">行索引,从0开始</param>
  56. /// <param name="columnIndex">列索引,从0开始</param>
  57. /// <param name="dimension">单元格维度</param>
  58. /// <returns>返回是否为合并单元格的布尔(Boolean)值</returns>
  59. public static bool IsMergeCell(this ISheet sheet, int rowIndex, int columnIndex, out Dimension dimension)
  60. {
  61. dimension = new Dimension
  62. {
  63. DataCell = null,
  64. RowSpan = 1,
  65. ColumnSpan = 1,
  66. FirstRowIndex = rowIndex,
  67. LastRowIndex = rowIndex,
  68. FirstColumnIndex = columnIndex,
  69. LastColumnIndex = columnIndex
  70. };
  71. for (int i = 0; i < sheet.NumMergedRegions; i++)
  72. {
  73. CellRangeAddress range = sheet.GetMergedRegion(i);
  74. sheet.IsMergedRegion(range);
  75. //这种算法只有当指定行列索引刚好是合并单元格的第一个跨度行第一个跨度列时才能取得合并单元格的跨度
  76. //if (range.FirstRow == rowIndex && range.FirstColumn == columnIndex)
  77. //{
  78. // dimension.DataCell = sheet.GetRow(range.FirstRow).GetCell(range.FirstColumn);
  79. // dimension.RowSpan = range.LastRow - range.FirstRow + 1;
  80. // dimension.ColumnSpan = range.LastColumn - range.FirstColumn + 1;
  81. // dimension.FirstRowIndex = range.FirstRow;
  82. // dimension.LastRowIndex = range.LastRow;
  83. // dimension.FirstColumnIndex = range.FirstColumn;
  84. // dimension.LastColumnIndex = range.LastColumn;
  85. // break;
  86. //}
  87. if ((rowIndex >= range.FirstRow && range.LastRow >= rowIndex) && (columnIndex >= range.FirstColumn && range.LastColumn >= columnIndex))
  88. {
  89. dimension.DataCell = sheet.GetRow(range.FirstRow).GetCell(range.FirstColumn);
  90. dimension.RowSpan = range.LastRow - range.FirstRow + 1;
  91. dimension.ColumnSpan = range.LastColumn - range.FirstColumn + 1;
  92. dimension.FirstRowIndex = range.FirstRow;
  93. dimension.LastRowIndex = range.LastRow;
  94. dimension.FirstColumnIndex = range.FirstColumn;
  95. dimension.LastColumnIndex = range.LastColumn;
  96. break;
  97. }
  98. }
  99. bool result;
  100. if (rowIndex >= 0 && sheet.LastRowNum > rowIndex)
  101. {
  102. IRow row = sheet.GetRow(rowIndex);
  103. if (columnIndex >= 0 && row.LastCellNum > columnIndex)
  104. {
  105. ICell cell = row.GetCell(columnIndex);
  106. result = cell.IsMergedCell;
  107. if (dimension.DataCell == null)
  108. {
  109. dimension.DataCell = cell;
  110. }
  111. }
  112. else
  113. {
  114. result = false;
  115. }
  116. }
  117. else
  118. {
  119. result = false;
  120. }
  121. return result;
  122. }
  123. /// <summary>
  124. /// 判断指定行列所在的单元格是否为合并单元格,并且输出该单元格的行列跨度
  125. /// </summary>
  126. /// <param name="sheet">Excel工作表</param>
  127. /// <param name="rowIndex">行索引,从0开始</param>
  128. /// <param name="columnIndex">列索引,从0开始</param>
  129. /// <param name="rowSpan">行跨度,返回值最小为1,同时表示没有行合并</param>
  130. /// <param name="columnSpan">列跨度,返回值最小为1,同时表示没有列合并</param>
  131. /// <returns>返回是否为合并单元格的布尔(Boolean)值</returns>
  132. public static bool IsMergeCell(this ISheet sheet, int rowIndex, int columnIndex, out int rowSpan, out int columnSpan)
  133. {
  134. Dimension dimension;
  135. bool result = sheet.IsMergeCell(rowIndex, columnIndex, out dimension);
  136. rowSpan = dimension.RowSpan;
  137. columnSpan = dimension.ColumnSpan;
  138. return result;
  139. }
  140. /// <summary>
  141. /// 判断指定单元格是否为合并单元格,并且输出该单元格的维度
  142. /// </summary>
  143. /// <param name="cell">单元格</param>
  144. /// <param name="dimension">单元格维度</param>
  145. /// <returns>返回是否为合并单元格的布尔(Boolean)值</returns>
  146. public static bool IsMergeCell(this ICell cell, out Dimension dimension)
  147. {
  148. return cell.Sheet.IsMergeCell(cell.RowIndex, cell.ColumnIndex, out dimension);
  149. }
  150. /// <summary>
  151. /// 判断指定单元格是否为合并单元格,并且输出该单元格的行列跨度
  152. /// </summary>
  153. /// <param name="cell">单元格</param>
  154. /// <param name="rowSpan">行跨度,返回值最小为1,同时表示没有行合并</param>
  155. /// <param name="columnSpan">列跨度,返回值最小为1,同时表示没有列合并</param>
  156. /// <returns>返回是否为合并单元格的布尔(Boolean)值</returns>
  157. public static bool IsMergeCell(this ICell cell, out int rowSpan, out int columnSpan)
  158. {
  159. return cell.Sheet.IsMergeCell(cell.RowIndex, cell.ColumnIndex, out rowSpan, out columnSpan);
  160. }
  161. /// <summary>
  162. /// 返回上一个跨度行,如果rowIndex为第一行,则返回null
  163. /// </summary>
  164. /// <param name="sheet">Excel工作表</param>
  165. /// <param name="rowIndex">行索引,从0开始</param>
  166. /// <param name="columnIndex">列索引,从0开始</param>
  167. /// <returns>返回上一个跨度行</returns>
  168. public static IRow PrevSpanRow(this ISheet sheet, int rowIndex, int columnIndex)
  169. {
  170. return sheet.FuncSheet(rowIndex, columnIndex, (currentDimension, isMerge) =>
  171. {
  172. //上一个单元格维度
  173. Dimension prevDimension;
  174. sheet.IsMergeCell(currentDimension.FirstRowIndex - 1, columnIndex, out prevDimension);
  175. return prevDimension.DataCell.Row;
  176. });
  177. }
  178. /// <summary>
  179. /// 返回下一个跨度行,如果rowIndex为最后一行,则返回null
  180. /// </summary>
  181. /// <param name="sheet">Excel工作表</param>
  182. /// <param name="rowIndex">行索引,从0开始</param>
  183. /// <param name="columnIndex">列索引,从0开始</param>
  184. /// <returns>返回下一个跨度行</returns>
  185. public static IRow NextSpanRow(this ISheet sheet, int rowIndex, int columnIndex)
  186. {
  187. return sheet.FuncSheet(rowIndex, columnIndex, (currentDimension, isMerge) =>
  188. isMerge ? sheet.GetRow(currentDimension.FirstRowIndex + currentDimension.RowSpan) : sheet.GetRow(rowIndex));
  189. }
  190. /// <summary>
  191. /// 返回上一个跨度行,如果row为第一行,则返回null
  192. /// </summary>
  193. /// <param name="row">行</param>
  194. /// <returns>返回上一个跨度行</returns>
  195. public static IRow PrevSpanRow(this IRow row)
  196. {
  197. return row.Sheet.PrevSpanRow(row.RowNum, row.FirstCellNum);
  198. }
  199. /// <summary>
  200. /// 返回下一个跨度行,如果row为最后一行,则返回null
  201. /// </summary>
  202. /// <param name="row">行</param>
  203. /// <returns>返回下一个跨度行</returns>
  204. public static IRow NextSpanRow(this IRow row)
  205. {
  206. return row.Sheet.NextSpanRow(row.RowNum, row.FirstCellNum);
  207. }
  208. /// <summary>
  209. /// 返回上一个跨度列,如果columnIndex为第一列,则返回null
  210. /// </summary>
  211. /// <param name="row">行</param>
  212. /// <param name="columnIndex">列索引,从0开始</param>
  213. /// <returns>返回上一个跨度列</returns>
  214. public static ICell PrevSpanCell(this IRow row, int columnIndex)
  215. {
  216. return row.Sheet.FuncSheet(row.RowNum, columnIndex, (currentDimension, isMerge) =>
  217. {
  218. //上一个单元格维度
  219. Dimension prevDimension;
  220. row.Sheet.IsMergeCell(row.RowNum, currentDimension.FirstColumnIndex - 1, out prevDimension);
  221. return prevDimension.DataCell;
  222. });
  223. }
  224. /// <summary>
  225. /// 返回下一个跨度列,如果columnIndex为最后一列,则返回null
  226. /// </summary>
  227. /// <param name="row">行</param>
  228. /// <param name="columnIndex">列索引,从0开始</param>
  229. /// <returns>返回下一个跨度列</returns>
  230. public static ICell NextSpanCell(this IRow row, int columnIndex)
  231. {
  232. return row.Sheet.FuncSheet(row.RowNum, columnIndex, (currentDimension, isMerge) =>
  233. row.GetCell(currentDimension.FirstColumnIndex + currentDimension.ColumnSpan));
  234. }
  235. /// <summary>
  236. /// 返回上一个跨度列,如果cell为第一列,则返回null
  237. /// </summary>
  238. /// <param name="cell">单元格</param>
  239. /// <returns>返回上一个跨度列</returns>
  240. public static ICell PrevSpanCell(this ICell cell)
  241. {
  242. return cell.Row.PrevSpanCell(cell.ColumnIndex);
  243. }
  244. /// <summary>
  245. /// 返回下一个跨度列,如果columnIndex为最后一列,则返回null
  246. /// </summary>
  247. /// <param name="cell">单元格</param>
  248. /// <returns>返回下一个跨度列</returns>
  249. public static ICell NextSpanCell(this ICell cell)
  250. {
  251. return cell.Row.NextSpanCell(cell.ColumnIndex);
  252. }
  253. /// <summary>
  254. /// 返回指定行索引所在的合并单元格(区域)中的第一行(通常是含有数据的行)
  255. /// </summary>
  256. /// <param name="sheet">Excel工作表</param>
  257. /// <param name="rowIndex">行索引,从0开始</param>
  258. /// <returns>返回指定列索引所在的合并单元格(区域)中的第一行</returns>
  259. public static IRow GetDataRow(this ISheet sheet, int rowIndex)
  260. {
  261. return sheet.FuncSheet(rowIndex, 0, (currentDimension, isMerge) => sheet.GetRow(currentDimension.FirstRowIndex));
  262. }
  263. /// <summary>
  264. /// 返回指定列索引所在的合并单元格(区域)中的第一行第一列(通常是含有数据的单元格)
  265. /// </summary>
  266. /// <param name="row">行</param>
  267. /// <param name="columnIndex">列索引</param>
  268. /// <returns>返回指定列索引所在的合并单元格(区域)中的第一行第一列</returns>
  269. public static ICell GetDataCell(this IRow row, int columnIndex)
  270. {
  271. return row.Sheet.FuncSheet(row.RowNum, columnIndex, (currentDimension, isMerge) => currentDimension.DataCell);
  272. }
  273. private static T FuncSheet<T>(this ISheet sheet, int rowIndex, int columnIndex, Func<Dimension, bool, T> func)
  274. {
  275. //当前单元格维度
  276. Dimension currentDimension;
  277. //是否为合并单元格
  278. bool isMerge = sheet.IsMergeCell(rowIndex, columnIndex, out currentDimension);
  279. return func(currentDimension, isMerge);
  280. }
  281. }
  282. }