首页 > 其他分享 >Npoi 复制行的问题记录

Npoi 复制行的问题记录

时间:2024-07-15 14:42:19浏览次数:11  
标签:sourceRow 记录 样式 单元格 Npoi targetCell 复制 CellType sourceCell

最近在做Excel模板数据导出,要求在Excel展示数据分页结果,做分页时发现npoi复制行有个bug【这种情况并不会百分百复现,sheet.CopyRow,备注下面的那一列还是会正常被复制显示完整的】,

本来第一行的文字在A1下是可以完全显示的,但是复制的第二页之后,就不会完整显示了,如下

经过调查,原因是复制后的单元格被改成了stirng类型,默认应该是blank

 

 

为了避免这种随机情况,只能自己重写一下CopyRow方法了

 public static class NpoiExpand
    {
        /// <summary>
        /// sheet 页自动分页
        /// </summary>
        /// <param name="sheet">工作表</param>
        /// <param name="pageSize">每页占多少行</param>
        /// <param name="pageCount">总页数</param>
        /// <param name="workbook">工作簿</param>
        public static void AutoPage(this ISheet sheet, int pageSize, int pageCount, IWorkbook workbook)
        {
            for (int p = 1; p < pageCount; p++)
            {
                for (int i = 0; i < pageSize; i++)
                {
                    sheet.CopyRow(i, p * pageSize + i, workbook, false);
                }
                sheet.SetRowBreak(p * pageSize - 1);
            }
        }
        /// <summary>
        /// 将源单元格的值复制到目标单元格。
        /// </summary>
        /// <param name="sourceCell">要从中复制值的源单元格。</param>
        /// <param name="targetCell">要将值复制到的目标单元格。</param>
        public static void SetCellValue(this ICell sourceCell, ICell targetCell)
        {
            switch (sourceCell.CellType)
            {
                case CellType.Blank:
                    targetCell.SetBlank();
                    break;
                case CellType.Boolean:
                    targetCell.SetCellValue(sourceCell.BooleanCellValue);
                    break;
                case CellType.Error:
                    targetCell.SetCellErrorValue(sourceCell.ErrorCellValue);
                    break;
                case CellType.Formula:
                    targetCell.SetCellFormula(sourceCell.CellFormula);
                    break;
                case CellType.Numeric:
                    if (DateUtil.IsCellDateFormatted(sourceCell))
                    {
                        if (sourceCell.DateCellValue.HasValue)
                        {
                            targetCell.SetCellValue(sourceCell.DateCellValue.Value);
                        }
                        else
                        {
                            // 处理空日期值的情况,这里可以根据需要进行操作
                            // 例如:设置一个默认日期值或者其他处理方式
                        }
                    }
                    else
                    {
                        targetCell.SetCellValue(sourceCell.NumericCellValue);
                    }
                    break;
                case CellType.String:
                    targetCell.SetCellValue(sourceCell.RichStringCellValue);
                    break;
            }
        }

        /// <summary>
        /// 将源行的内容复制到目标工作表的目标行中。
        /// </summary>
        /// <param name="targetSheet">目标工作表</param>
        /// <param name="sourceRowNum">源行号</param>
        /// <param name="targetRowNum">目标行号</param>
        /// <param name="targetWorkbook">目标工作簿</param>
        /// <param name="isCopyCellValue">是否复制单元格的值</param>
        public static void CopyRow(this ISheet targetSheet, int sourceRowNum, int targetRowNum, IWorkbook targetWorkbook, bool isCopyCellValue = false)
        {
            IRow sourceRow = targetSheet.GetRow(sourceRowNum);
            IRow targetRow = targetSheet.GetRow(targetRowNum);
            if (targetRow == null)
            {
                targetRow = targetSheet.CreateRow(targetRowNum);
            }
            // 设置目标行的高度与源行相同
            targetRow.Height = sourceRow.Height;

            // 创建一个样式缓存,用于重用单元格样式
            Dictionary<ICellStyle, ICellStyle> styleCache = new Dictionary<ICellStyle, ICellStyle>();

            // 如果源行具有自定义样式,将此样式复制到目标行
            if (sourceRow.RowStyle != null)
            {
                ICellStyle newRowStyle;
                if (!styleCache.TryGetValue(sourceRow.RowStyle, out newRowStyle))
                {
                    // 如果样式尚未缓存,创建新样式并将其添加到缓存中
                    newRowStyle = targetWorkbook.CreateCellStyle();
                    newRowStyle.CloneStyleFrom(sourceRow.RowStyle);
                    styleCache[sourceRow.RowStyle] = newRowStyle;
                }
                targetRow.RowStyle = newRowStyle;
            }

            // 遍历源行的所有单元格进行复制
            for (int i = 0; i < sourceRow.LastCellNum; i++)
            {
                ICell sourceCell = sourceRow.GetCell(i);
                if (sourceCell != null)
                {
                    // 在目标行创建新的单元格,并设置相同的单元格类型
                    ICell targetCell = targetRow.CreateCell(i, sourceCell.CellType);
                    targetCell.SetCellType(sourceCell.CellType);
                    // 如果源单元格具有样式,将此样式复制到目标单元格
                    if (sourceCell.CellStyle != null)
                    {
                        ICellStyle newCellStyle;
                        if (!styleCache.TryGetValue(sourceCell.CellStyle, out newCellStyle))
                        {
                            // 如果样式尚未缓存,创建新样式并将其添加到缓存中
                            newCellStyle = targetWorkbook.CreateCellStyle();
                            newCellStyle.CloneStyleFrom(sourceCell.CellStyle);
                            styleCache[sourceCell.CellStyle] = newCellStyle;
                        }
                        targetCell.CellStyle = newCellStyle;

                    }

                    // 复制单元格的注释
                    if (sourceCell.CellComment != null)
                    {
                        targetCell.CellComment = sourceCell.CellComment;
                    }

                    // 复制单元格的超链接
                    if (sourceCell.Hyperlink != null)
                    {
                        targetCell.Hyperlink = sourceCell.Hyperlink;
                    }
                    if (isCopyCellValue)
                        sourceCell.SetCellValue(targetCell);

                }
            }
            for (int i = 0; i < targetSheet.NumMergedRegions; i++)
            {
                CellRangeAddress mergedRegion = targetSheet.GetMergedRegion(i);
                if (mergedRegion.FirstRow >= targetRowNum && mergedRegion.LastRow <= targetRowNum)
                {
                    // 如果合并区域与目标行重叠,将其拆分
                    targetSheet.RemoveMergedRegion(i);
                    i--;
                }
            }
            // 复制合并的单元格区域
            for (int i = 0; i < sourceRow.Sheet.NumMergedRegions; i++)
            {
                CellRangeAddress mergedRegion = sourceRow.Sheet.GetMergedRegion(i);
                if (mergedRegion.FirstRow == sourceRow.RowNum)
                {
                    // 如果源合并区域的首行是当前源行,将此合并区域复制到目标行
                    CellRangeAddress newMergedRegion = new CellRangeAddress(
                        targetRow.RowNum,
                        targetRow.RowNum + mergedRegion.LastRow - mergedRegion.FirstRow,
                        mergedRegion.FirstColumn,
                        mergedRegion.LastColumn
                    );

                    targetSheet.AddMergedRegion(newMergedRegion);
                }
            }
        }

        /// <summary>
        /// 仅复制源行的样式到目标工作表的目标行。
        /// </summary>
        /// <param name="targetSheet">目标工作表</param>
        /// <param name="sourceRowNum">源行号</param>
        /// <param name="targetRowNum">目标行号</param>
        /// <param name="targetWorkbook">目标工作簿</param>
        public static void CopyRowCellStyle(this ISheet targetSheet, int sourceRowNum, int targetRowNum, IWorkbook targetWorkbook)
        {
            IRow sourceRow = targetSheet.GetRow(sourceRowNum);
            IRow targetRow = targetSheet.GetRow(targetRowNum);
            if (targetRow == null)
            {
                targetRow = targetSheet.CreateRow(targetRowNum);
            }
            // 设置目标行的高度与源行相同
            targetRow.Height = sourceRow.Height;

            // 创建一个样式缓存,用于重用单元格样式
            Dictionary<ICellStyle, ICellStyle> styleCache = new Dictionary<ICellStyle, ICellStyle>();

            // 如果源行具有自定义样式,将此样式复制到目标行
            if (sourceRow.RowStyle != null)
            {
                ICellStyle newRowStyle;
                if (!styleCache.TryGetValue(sourceRow.RowStyle, out newRowStyle))
                {
                    // 如果样式尚未缓存,创建新样式并将其添加到缓存中
                    newRowStyle = targetWorkbook.CreateCellStyle();
                    newRowStyle.CloneStyleFrom(sourceRow.RowStyle);
                    styleCache[sourceRow.RowStyle] = newRowStyle;
                }
                targetRow.RowStyle = newRowStyle;
            }

            // 遍历源行的所有单元格进行样式复制
            for (int i = 0; i < sourceRow.LastCellNum; i++)
            {
                ICell sourceCell = sourceRow.GetCell(i);
                if (sourceCell != null)
                {
                    // 在目标行创建新的单元格,并设置相同的单元格类型
                    ICell targetCell = targetRow.CreateCell(i, sourceCell.CellType);
                    targetCell.SetCellType(sourceCell.CellType);
                    // 如果源单元格具有样式,将此样式复制到目标单元格
                    if (sourceCell.CellStyle != null)
                    {
                        ICellStyle newCellStyle;
                        if (!styleCache.TryGetValue(sourceCell.CellStyle, out newCellStyle))
                        {
                            // 如果样式尚未缓存,创建新样式并将其添加到缓存中
                            newCellStyle = targetWorkbook.CreateCellStyle();
                            newCellStyle.CloneStyleFrom(sourceCell.CellStyle);
                            styleCache[sourceCell.CellStyle] = newCellStyle;
                        }
                        targetCell.CellStyle = newCellStyle;
                    }
                }
            }
        }

        /// <summary>
        /// 将 .xls 文件转换为 .xlsx 文件
        /// </summary>
        /// <param name="filePath">要转换的文件路径</param>
        /// <returns>转换后的 .xlsx 文件路径</returns>
        public static string ConvertXlsToXlsx(this string filePath)
        {

            if (!File.Exists(filePath))
                throw new FileNotFoundException("文件不存在!", filePath);
            if (Path.GetExtension(filePath).ToLower() == ".xls")
            {
                var xlsxFilePath = filePath.Replace("xls", "xlsx");
                Application excelApp = new Application();
                Workbook workbook = excelApp.Workbooks.Open(filePath);
                workbook.Saved = true;
                workbook.SaveAs(xlsxFilePath, XlFileFormat.xlOpenXMLWorkbook);
                workbook.Close(true);
                excelApp.Quit();

                System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook);
                workbook = null;
                System.Runtime.InteropServices.Marshal.ReleaseComObject(excelApp);
                excelApp = null;
                GC.Collect();
                return xlsxFilePath;
            }

            return filePath;
        }

        /// <summary>
        /// 打开一个 Excel 文件并返回工作簿对象。
        /// </summary>
        /// <param name="filePath">Excel 文件的路径。</param>
        /// <returns>已打开的 Excel 工作簿对象。</returns>
        public static IWorkbook OpenExcel(this string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException("文件不存在!", filePath);
            IWorkbook workbook = WorkbookFactory.Create(filePath);
            return workbook;
        }

        /// <summary>
        /// 将工作簿保存为文件。
        /// </summary>
        /// <param name="workBook">要保存的工作簿对象。</param>
        /// <param name="saveFileFullPath">要保存的文件的完整路径。</param>
        /// <returns>保存的文件的完整路径。</returns>
        public static string SaveExcelToFile(this IWorkbook workBook, string saveFileFullPath)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                workBook.Write(memoryStream); // 将工作簿写入内存流
                var fileBytes = memoryStream.ToArray(); // 将内存流转换为字节数组
                File.WriteAllBytes(saveFileFullPath, fileBytes);
                return saveFileFullPath;
            }
        }

        /// <summary>
        /// 获取单元格的值。
        /// </summary>
        /// <param name="cell">要获取值的单元格</param>
        /// <returns>单元格的值,如果单元格为空则返回 DBNull.Value</returns>
        public static object GetCellValue(this ICell cell)
        {
            if (cell == null)
                return DBNull.Value;

            switch (cell.CellType)
            {
                case CellType.Numeric:
                    if (DateUtil.IsCellDateFormatted(cell))
                        return cell.DateCellValue;
                    else
                        return cell.NumericCellValue;

                case CellType.String:
                    return cell.StringCellValue;

                case CellType.Boolean:
                    return cell.BooleanCellValue;

                case CellType.Formula:
                    return cell.CellFormula;

                default:
                    return DBNull.Value;
            }
        }

    }
            var newFileSavePath = Path.Combine(Directory.GetCurrentDirectory(), "Source.output.xlsx");//保存生成的文件
            var path = Path.Combine(Directory.GetCurrentDirectory(), "source.xlsx");
            var workbook = path.OpenExcel();
            var sheet = workbook.GetSheetAt(0);

            sheet.AutoPage(14, 3, workbook);
Main方法

最终结果

 

标签:sourceRow,记录,样式,单元格,Npoi,targetCell,复制,CellType,sourceCell
From: https://www.cnblogs.com/INetIMVC/p/18303121

相关文章

  • SAP ABAP 写更改记录到表CDHDR/CDPOS 下篇
    表更改记录上篇写入表更改记录下篇发布日期:2024/07/11继上一篇的内容,用户测试的过程中发现,还是查不到写入记录。最后发现,我使用的系统环境,更改表equp时,对象类是QUOTEN2。基于此,当一个通过表TCDOB能查出多个对象类时,我们最好通过标准功能更改任意一条目标表的数据。再去查......
  • 构造题做题记录
    更新中。加训构造。/fendouCF1917E发现我们如果把一个\(2\times2\)的矩阵填满\(1\),不会有影响。当\(k\)为\(4\)的倍数,直接填就可以了。当\(k\)为奇数显然无解,因为你无法让每行奇偶性相同。当\(k\)除\(4\)余\(2\),如果\(k\)为\(2\)或\(n\timesn-2\)时无......
  • Latex学习记录
    目录Latex安装下载检查卸载TexStudio安装下载Latex安装下载标记说明:官网:TeXLive-TeXUsersGroup等待安装完成:检查输入latex-v出现Latex版本号等相关信息即为安装成功卸载找到例如J:\LATEX\texlive\2024\tlpkg\installer目录运行程序即可若担心......
  • 24.07 做题记录
    24.7降维技巧约定diff=1水题黄以下一眼切diff=2easy下位绿及黄能切diff=3medium特殊的黄;绿;较简单的蓝可能需要题解提供一步diff=4hard蓝色+需要题解diff=5不可做题紫色+做不了前缀和/差分P1115最大子段和diff:1前缀和板子。codeP3406海底高铁......
  • MySQL8.0.33 主主复制高可用部署
    一、服务器规划1.1服务器详情主机名IP配置备注MySQLMasterVIP10.0.0.10虚拟IP跳转MySQL连接地址MySQLMaster0110.0.0.112H4GMySQLMaster0210.0.0.122H4G1.2修改主机名##修改各主机的主机名如:10.0.0.11服务器[root@localhost~]#hostnamectlset-hostnamemysqlmas......
  • BUUCTF Crypto 做题记录
    目录:1.一眼就解密2.Url编码3.MD54.看我回旋踢5.摩丝6.password#1.一眼就解密https://buuoj.cn/challenges#一眼就解密ZmxhZ3tUSEVfRkxBR19PRl9USElTX1NUUklOR30=确实一眼看出是Base64,拿去解密即可2.Url编码https://buuoj.cn/challenges#Url编码直接网上搜urlenco......
  • 2024/7/13 ABC362 比赛记录
    7/14:昨晚打的abc,外面下着大雨;1650ptsrank975T1:简单签到题,愣是被我拖了7min死因:开赛时老师开始收手机,一直叫我名,我一着急装了两个翻译插件,导致页面错版。时间宝贵,于是我艰难的对照样例勉强读懂题(T2:计算几何?给平面直角坐标系3点,判rt三角形。直接double勾股定理算边......
  • 记录些Redis题集(2)
    Redis的多路IO复用多路I/O复用是一种同时监听多个文件描述符(如Socket)的状态变化,并能在某个文件描述符就绪时执行相应操作的技术。在Redis中,多路I/O复用技术主要用于处理客户端的连接请求和读写操作,以实现高并发、高性能的服务。Redis支持多种多路I/O复用机制,包括select、poll......
  • 记录些Redis题集(4)
    Redis通讯协议(RESP)Redis通讯协议(RedisSerializationProtocol,RESP)是Redis服务端与客户端之间进行通信的协议。它是一种二进制安全的文本协议,设计简洁且易于实现。RESP主要用于支持客户端和服务器之间的请求响应交互。RESP的主要特点:简单性:协议的设计非常简单,易于理......
  • 记录些Redis题集(1)
    Redis内存淘汰触发条件的相关配置如下:Redis通过配置项maxmemory来设定其允许使用的最大内存容量。当Redis实际占用的内存达到这一阈值时,将触发内存淘汰机制,开始删除部分数据以释放内存空间,防止服务因内存溢出而异常。Redis内存淘汰策略可在配置文件redis.conf中通过maxmemory......