如何在.NET 6项目中使用NPOI将图片居中显示在Excel单元格中?
问题
我正在开发一个.NET 6项目,需要使用NPOI库生成.xlsx文件,并在单元格中添加图片且使其居中。
首先我创建了XSSFWorkbook,添加工作表、行和单元格:
var workbook = new XSSFWorkbook(); var currentWorksheet = workbook.CreateSheet("MyWorksheet"); // 设置列默认宽度 currentWorksheet.SetColumnWidth(0, 600); // 创建行 var currentRow = currentWorksheet.CreateRow(0); currentRow.Height = 600;
现在我已完成上述操作,想要添加居中的图片,于是通过Base64字符串添加图片:
// 将Base64字符串转换为字节数组 var img = Convert.FromBase64String("MyBase64Image"); var pictureIndex = workbook.AddPicture(img, PictureType.PNG); IDrawing drawing = currentWorksheet.CreateDrawingPatriarch(); // 定义图片锚点 IClientAnchor anchor = drawing.CreateAnchor(200, 200, 400, 400, 0, 0, 0, 0); anchor.AnchorType = AnchorType.MoveDontResize; var picture = drawing.CreatePicture(anchor, pictureIndex); picture.Resize(); // 调整图片到指定尺寸 var imageSize = picture.GetImageDimension(); double resizeX = 50.0 / imageSize.Width; double resizeY = 50.0 / imageSize.Height; picture.Resize(resizeX, resizeY);
但无论我修改锚点的dx1、dx2、dy1、dy2参数(最后四个参数除外),图片都固定在单元格的左上角,位置完全不变。请问如何让图片在单元格中居中?
解决方法
问题核心在于调用无参的picture.Resize()时,NPOI会自动覆盖锚点的偏移参数(dx1、dy1等),导致手动设置的偏移失效。要实现图片居中,需先计算单元格与图片的尺寸差值,手动设置锚点偏移,且避免使用无参的Resize()方法。
具体实现步骤:
- 明确NPOI单位规则:列宽单位是1/256字符宽度,行高单位是1/20点;锚点偏移参数
dx1/dy1/dx2/dy2的单位是EMU(English Metric Unit),1像素=9525 EMU - 计算目标图片尺寸与单元格尺寸的差值,得出居中所需的偏移量
- 用计算后的偏移量创建锚点,再调用带参数的
Resize()调整图片尺寸
修正后的完整代码
using System.Drawing; using System.IO; using NPOI.XSSF.UserModel; using NPOI.SS.UserModel; using NPOI.SS.Util; var workbook = new XSSFWorkbook(); var currentWorksheet = workbook.CreateSheet("MyWorksheet"); // 设置列宽度(单位:1/256字符宽度) int columnWidth = 600; currentWorksheet.SetColumnWidth(0, columnWidth); // 创建行并设置高度(单位:1/20点) int rowHeight = 600; var currentRow = currentWorksheet.CreateRow(0); currentRow.Height = (short)rowHeight; // 将Base64字符串转换为字节数组 var imgBytes = Convert.FromBase64String("MyBase64Image"); var pictureIndex = workbook.AddPicture(imgBytes, PictureType.PNG); IDrawing drawing = currentWorksheet.CreateDrawingPatriarch(); // 获取图片原始尺寸 using var imgStream = new MemoryStream(imgBytes); var imageDimension = Image.FromStream(imgStream); double originalImgWidth = imageDimension.Width; double originalImgHeight = imageDimension.Height; // 定义目标图片尺寸 double targetImgWidth = 50; double targetImgHeight = 50; // 计算缩放比例 double resizeX = targetImgWidth / originalImgWidth; double resizeY = targetImgHeight / originalImgHeight; // 转换单元格尺寸为像素(列宽=列宽值/256 * 默认字体宽度;行高=行高值/20 * 96/72,96是屏幕DPI,72是打印DPI) // 假设默认字体宽度为10(可根据实际使用的字体调整) double cellWidthPx = columnWidth / 256.0 * 10; double cellHeightPx = rowHeight / 20.0 * 96 / 72; // 计算居中偏移量(转成EMU单位) long dx1 = (long)((cellWidthPx - targetImgWidth) / 2 * 9525); long dy1 = (long)((cellHeightPx - targetImgHeight) / 2 * 9525); long dx2 = dx1 + (long)(targetImgWidth * 9525); long dy2 = dy1 + (long)(targetImgHeight * 9525); // 创建锚点:dx1/dy1是图片左上角偏移,dx2/dy2是右下角偏移;后四个参数指定图片绑定的单元格范围(0,0到0,0表示仅A1单元格) IClientAnchor anchor = drawing.CreateAnchor(dx1, dy1, dx2, dy2, 0, 0, 0, 0); anchor.AnchorType = AnchorType.MoveDontResize; // 创建图片并应用缩放 var picture = drawing.CreatePicture(anchor, pictureIndex); picture.Resize(resizeX, resizeY);
关键注意事项
- 不要调用无参的
picture.Resize(),它会自动重置锚点偏移,覆盖手动设置的值 - 列宽转像素时的默认字体宽度需根据实际使用的字体调整(比如常用的Calibri 11号字体,宽度约为10)
- 若需适配不同DPI环境,可根据实际屏幕DPI调整行高的转换公式
内容的提问来源于stack exchange,提问作者Thomas Fabre
相关产品推荐
相关产品推荐

