NPOI擴充套件--判斷指定單元格是否為合併單元格和輸出該單元格的行列跨度(維度)

暖暖De幸福發表於2024-03-26
namespace NPOI
{
    /// <summary>
    /// 表示單元格的維度,通常用於表達合併單元格的維度
    /// </summary>
    public struct Dimension
    {
        /// <summary>
        /// 含有資料的單元格(通常表示合併單元格的第一個跨度行第一個跨度列),該欄位可能為null
        /// </summary>
        public ICell DataCell;

        /// <summary>
        /// 行跨度(跨越了多少行)
        /// </summary>
        public int RowSpan;

        /// <summary>
        /// 列跨度(跨越了多少列)
        /// </summary>
        public int ColumnSpan;

        /// <summary>
        /// 合併單元格的起始行索引
        /// </summary>
        public int FirstRowIndex;

        /// <summary>
        /// 合併單元格的結束行索引
        /// </summary>
        public int LastRowIndex;

        /// <summary>
        /// 合併單元格的起始列索引
        /// </summary>
        public int FirstColumnIndex;

        /// <summary>
        /// 合併單元格的結束列索引
        /// </summary>
        public int LastColumnIndex;
    }

    public static class ExcelExtension
    {
        /// <summary>
        /// 判斷指定行列所在的單元格是否為合併單元格,並且輸出該單元格的維度
        /// </summary>
        /// <param name="sheet">Excel工作表</param>
        /// <param name="rowIndex">行索引,從0開始</param>
        /// <param name="columnIndex">列索引,從0開始</param>
        /// <param name="dimension">單元格維度</param>
        /// <returns>返回是否為合併單元格的布林(Boolean)值</returns>
        public static bool IsMergeCell(this ISheet sheet, int rowIndex, int columnIndex, out Dimension dimension)
        {
            dimension = new Dimension
            {
                DataCell = null,
                RowSpan = 1,
                ColumnSpan = 1,
                FirstRowIndex = rowIndex,
                LastRowIndex = rowIndex,
                FirstColumnIndex = columnIndex,
                LastColumnIndex = columnIndex
            };

            for (int i = 0; i < sheet.NumMergedRegions; i++)
            {
                CellRangeAddress range = sheet.GetMergedRegion(i);
                sheet.IsMergedRegion(range);

                //這種演算法只有當指定行列索引剛好是合併單元格的第一個跨度行第一個跨度列時才能取得合併單元格的跨度
                //if (range.FirstRow == rowIndex && range.FirstColumn == columnIndex)
                //{
                //    dimension.DataCell = sheet.GetRow(range.FirstRow).GetCell(range.FirstColumn);
                //    dimension.RowSpan = range.LastRow - range.FirstRow + 1;
                //    dimension.ColumnSpan = range.LastColumn - range.FirstColumn + 1;
                //    dimension.FirstRowIndex = range.FirstRow;
                //    dimension.LastRowIndex = range.LastRow;
                //    dimension.FirstColumnIndex = range.FirstColumn;
                //    dimension.LastColumnIndex = range.LastColumn;
                //    break;
                //}

                if ((rowIndex >= range.FirstRow && range.LastRow >= rowIndex) && (columnIndex >= range.FirstColumn && range.LastColumn >= columnIndex))
                {
                    dimension.DataCell = sheet.GetRow(range.FirstRow).GetCell(range.FirstColumn);
                    dimension.RowSpan = range.LastRow - range.FirstRow + 1;
                    dimension.ColumnSpan = range.LastColumn - range.FirstColumn + 1;
                    dimension.FirstRowIndex = range.FirstRow;
                    dimension.LastRowIndex = range.LastRow;
                    dimension.FirstColumnIndex = range.FirstColumn;
                    dimension.LastColumnIndex = range.LastColumn;
                    break;
                }
            }

            bool result;
            if (rowIndex >= 0 && sheet.LastRowNum > rowIndex)
            {
                IRow row = sheet.GetRow(rowIndex);
                if (columnIndex >= 0 && row.LastCellNum > columnIndex)
                {
                    ICell cell = row.GetCell(columnIndex);
                    result = cell.IsMergedCell;

                    if (dimension.DataCell == null)
                    {
                        dimension.DataCell = cell;
                    }
                }
                else
                {
                    result = false;
                }
            }
            else
            {
                result = false;
            }

            return result;
        }

        /// <summary>
        /// 判斷指定行列所在的單元格是否為合併單元格,並且輸出該單元格的行列跨度
        /// </summary>
        /// <param name="sheet">Excel工作表</param>
        /// <param name="rowIndex">行索引,從0開始</param>
        /// <param name="columnIndex">列索引,從0開始</param>
        /// <param name="rowSpan">行跨度,返回值最小為1,同時表示沒有行合併</param>
        /// <param name="columnSpan">列跨度,返回值最小為1,同時表示沒有列合併</param>
        /// <returns>返回是否為合併單元格的布林(Boolean)值</returns>
        public static bool IsMergeCell(this ISheet sheet, int rowIndex, int columnIndex, out int rowSpan, out int columnSpan)
        {
            Dimension dimension;
            bool result = sheet.IsMergeCell(rowIndex, columnIndex, out dimension);

            rowSpan = dimension.RowSpan;
            columnSpan = dimension.ColumnSpan;

            return result;
        }

        /// <summary>
        /// 判斷指定單元格是否為合併單元格,並且輸出該單元格的維度
        /// </summary>
        /// <param name="cell">單元格</param>
        /// <param name="dimension">單元格維度</param>
        /// <returns>返回是否為合併單元格的布林(Boolean)值</returns>
        public static bool IsMergeCell(this ICell cell, out Dimension dimension)
        {
            return cell.Sheet.IsMergeCell(cell.RowIndex, cell.ColumnIndex, out dimension);
        }

        /// <summary>
        /// 判斷指定單元格是否為合併單元格,並且輸出該單元格的行列跨度
        /// </summary>
        /// <param name="cell">單元格</param>
        /// <param name="rowSpan">行跨度,返回值最小為1,同時表示沒有行合併</param>
        /// <param name="columnSpan">列跨度,返回值最小為1,同時表示沒有列合併</param>
        /// <returns>返回是否為合併單元格的布林(Boolean)值</returns>
        public static bool IsMergeCell(this ICell cell, out int rowSpan, out int columnSpan)
        {
            return cell.Sheet.IsMergeCell(cell.RowIndex, cell.ColumnIndex, out rowSpan, out columnSpan);
        }

        /// <summary>
        /// 返回上一個跨度行,如果rowIndex為第一行,則返回null
        /// </summary>
        /// <param name="sheet">Excel工作表</param>
        /// <param name="rowIndex">行索引,從0開始</param>
        /// <param name="columnIndex">列索引,從0開始</param>
        /// <returns>返回上一個跨度行</returns>
        public static IRow PrevSpanRow(this ISheet sheet, int rowIndex, int columnIndex)
        {
            return sheet.FuncSheet(rowIndex, columnIndex, (currentDimension, isMerge) =>
            {
                //上一個單元格維度
                Dimension prevDimension;
                sheet.IsMergeCell(currentDimension.FirstRowIndex - 1, columnIndex, out prevDimension);
                return prevDimension.DataCell.Row;
            });
        }

        /// <summary>
        /// 返回下一個跨度行,如果rowIndex為最後一行,則返回null
        /// </summary>
        /// <param name="sheet">Excel工作表</param>
        /// <param name="rowIndex">行索引,從0開始</param>
        /// <param name="columnIndex">列索引,從0開始</param>
        /// <returns>返回下一個跨度行</returns>
        public static IRow NextSpanRow(this ISheet sheet, int rowIndex, int columnIndex)
        {
            return sheet.FuncSheet(rowIndex, columnIndex, (currentDimension, isMerge) =>
                isMerge ? sheet.GetRow(currentDimension.FirstRowIndex + currentDimension.RowSpan) : sheet.GetRow(rowIndex));
        }

        /// <summary>
        /// 返回上一個跨度行,如果row為第一行,則返回null
        /// </summary>
        /// <param name="row">行</param>
        /// <returns>返回上一個跨度行</returns>
        public static IRow PrevSpanRow(this IRow row)
        {
            return row.Sheet.PrevSpanRow(row.RowNum, row.FirstCellNum);
        }

        /// <summary>
        /// 返回下一個跨度行,如果row為最後一行,則返回null
        /// </summary>
        /// <param name="row">行</param>
        /// <returns>返回下一個跨度行</returns>
        public static IRow NextSpanRow(this IRow row)
        {
            return row.Sheet.NextSpanRow(row.RowNum, row.FirstCellNum);
        }

        /// <summary>
        /// 返回上一個跨度列,如果columnIndex為第一列,則返回null
        /// </summary>
        /// <param name="row">行</param>
        /// <param name="columnIndex">列索引,從0開始</param>
        /// <returns>返回上一個跨度列</returns>
        public static ICell PrevSpanCell(this IRow row, int columnIndex)
        {
            return row.Sheet.FuncSheet(row.RowNum, columnIndex, (currentDimension, isMerge) =>
            {
                //上一個單元格維度
                Dimension prevDimension;
                row.Sheet.IsMergeCell(row.RowNum, currentDimension.FirstColumnIndex - 1, out prevDimension);
                return prevDimension.DataCell;
            });
        }

        /// <summary>
        /// 返回下一個跨度列,如果columnIndex為最後一列,則返回null
        /// </summary>
        /// <param name="row">行</param>
        /// <param name="columnIndex">列索引,從0開始</param>
        /// <returns>返回下一個跨度列</returns>
        public static ICell NextSpanCell(this IRow row, int columnIndex)
        {
            return row.Sheet.FuncSheet(row.RowNum, columnIndex, (currentDimension, isMerge) =>
                row.GetCell(currentDimension.FirstColumnIndex + currentDimension.ColumnSpan));
        }

        /// <summary>
        /// 返回上一個跨度列,如果cell為第一列,則返回null
        /// </summary>
        /// <param name="cell">單元格</param>
        /// <returns>返回上一個跨度列</returns>
        public static ICell PrevSpanCell(this ICell cell)
        {
            return cell.Row.PrevSpanCell(cell.ColumnIndex);
        }

        /// <summary>
        /// 返回下一個跨度列,如果columnIndex為最後一列,則返回null
        /// </summary>
        /// <param name="cell">單元格</param>
        /// <returns>返回下一個跨度列</returns>
        public static ICell NextSpanCell(this ICell cell)
        {
            return cell.Row.NextSpanCell(cell.ColumnIndex);
        }

        /// <summary>
        /// 返回指定行索引所在的合併單元格(區域)中的第一行(通常是含有資料的行)
        /// </summary>
        /// <param name="sheet">Excel工作表</param>
        /// <param name="rowIndex">行索引,從0開始</param>
        /// <returns>返回指定列索引所在的合併單元格(區域)中的第一行</returns>
        public static IRow GetDataRow(this ISheet sheet, int rowIndex)
        {
            return sheet.FuncSheet(rowIndex, 0, (currentDimension, isMerge) => sheet.GetRow(currentDimension.FirstRowIndex));
        }

        /// <summary>
        /// 返回指定列索引所在的合併單元格(區域)中的第一行第一列(通常是含有資料的單元格)
        /// </summary>
        /// <param name="row">行</param>
        /// <param name="columnIndex">列索引</param>
        /// <returns>返回指定列索引所在的合併單元格(區域)中的第一行第一列</returns>
        public static ICell GetDataCell(this IRow row, int columnIndex)
        {
            return row.Sheet.FuncSheet(row.RowNum, columnIndex, (currentDimension, isMerge) => currentDimension.DataCell);
        }

        private static T FuncSheet<T>(this ISheet sheet, int rowIndex, int columnIndex, Func<Dimension, bool, T> func)
        {
            //當前單元格維度
            Dimension currentDimension;
            //是否為合併單元格
            bool isMerge = sheet.IsMergeCell(rowIndex, columnIndex, out currentDimension);

            return func(currentDimension, isMerge);
        }
    }
}

相關文章