在 WinForm 中调用 EPPlus 8 将 `DataGridView` 数据导出到 Excel 时,精确控制 Excel 工作表的行高和列宽是一个常见的进阶需求。通过 EPPlus 库提供的丰富 API,可以轻松实现从继承源控件样式到自定义逻辑设置的多种宽度控制策略 [ref_1]。核心方法是操作 `ExcelWorksheet` 的 `Column()` 和 `Row()` 属性。
### **核心方法:设置列宽与行高**
以下表格总结了 EPPlus 中用于设置列宽与行高的主要属性与方法:
| 操作对象 | 属性/方法 | 说明 | 示例代码片段 |
| :--- | :--- | :--- | :--- |
| **列(Column)** | `Worksheet.Column(colIndex).Width` | 设置**指定列**的宽度。单位为 Excel 中的“字符”宽度(近似值)。 | `worksheet.Column(2).Width = 20;` |
| | `Worksheet.Column(colIndex).AutoFit()` | 自动调整**指定列**的宽度以匹配其内容。 | `worksheet.Column(3).AutoFit();` |
| | `Worksheet.Column(colIndex).BestFit` | 自动调整**指定列**的宽度,是 `AutoFit()` 的另一种实现。 | `worksheet.Column(3).BestFit = true;` |
| **行(Row)** | `Worksheet.Row(rowIndex).Height` | 设置**指定行**的高度。单位为**磅(Point)**。 | `worksheet.Row(1).Height = 30;` |
| | `Worksheet.Row(rowIndex).CustomHeight` | 设置为 `true` 时,`Height` 属性定义的值为自定义高度;设置为 `false` 则为自动调整。 | `worksheet.Row(1).CustomHeight = true;` |
### **策略一:根据 DataGridView 列宽进行等比映射(推荐)**
将 `DataGridView` 中每一列的视觉宽度比例映射到 Excel 列宽,可以最大程度保持布局一致性。由于 WinForm 的 `DataGridViewColumn.Width` 属性单位是像素,而 Excel 的列宽单位是字符,需要进行转换。
```csharp
using OfficeOpenXml;
using System.IO;
using System.Windows.Forms;
private void ExportDataGridViewWithColumnWidth(DataGridView dgv, string filePath)
{
// 1. 设置许可证上下文,避免 LicenseException [ref_1]
ExcelPackage.LicenseContext = LicenseContext.NonCommercial; // 或 LicenseContext.Commercial
using (ExcelPackage excelPackage = new ExcelPackage())
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("Sheet1");
// 2. 写入表头和数据
// 写入表头
for (int i = 0; i < dgv.Columns.Count; i++)
{
worksheet.Cells[1, i + 1].Value = dgv.Columns[i].HeaderText;
worksheet.Cells[1, i + 1].Style.Font.Bold = true; // 加粗表头
}
// 写入数据行
int rowIndex = 2;
foreach (DataGridViewRow dgvRow in dgv.Rows)
{
if (!dgvRow.IsNewRow) // 跳过“新行”占位符
{
for (int col = 0; col < dgv.Columns.Count; col++)
{
worksheet.Cells[rowIndex, col + 1].Value = dgvRow.Cells[col].Value;
}
rowIndex++;
}
}
// 3. 核心:设置 Excel 列宽(基于 DataGridView 列宽比例)
for (int col = 0; col < dgv.Columns.Count; col++)
{
// DataGridView 列宽 (Width) 单位是像素,需要转换为 Excel 的字符宽度单位。
// 一个经验转换公式:Excel列宽 ≈ (DataGridView列宽像素值 / 7.5) + 1
// 7.5 是近似比例因子,可根据实际显示效果微调。
double excelColumnWidth = (dgv.Columns[col].Width / 7.5) + 1;
// 应用宽度,并确保宽度在合理范围内(例如,不小于5个字符)
worksheet.Column(col + 1).Width = Math.Max(excelColumnWidth, 5);
}
// 4. 设置表头行高
worksheet.Row(1).Height = 25; // 将表头行高度设置为25磅
// 5. 保存文件
excelPackage.SaveAs(new FileInfo(filePath));
}
}
```
### **策略二:固定列宽与自适应列宽混合设置**
有时需要对某些关键列(如“序号”、“操作”)设置固定宽度,对其他内容列进行自适应宽度调整。
```csharp
private void ExportWithMixedWidthStrategy(DataGridView dgv, string filePath)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage excelPackage = new ExcelPackage())
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("Data");
// ... [数据写入逻辑,同上] ...
// 混合宽度策略
for (int col = 0; col < dgv.Columns.Count; col++)
{
string columnHeader = dgv.Columns[col].HeaderText;
switch (columnHeader)
{
case "序号":
case "ID":
// 策略A:对特定列设置固定宽度
worksheet.Column(col + 1).Width = 10;
break;
case "描述":
case "备注":
// 策略B:对文本较多的列设置一个较大的固定宽度
worksheet.Column(col + 1).Width = 40;
break;
case "日期":
// 策略C:设置为刚好容纳标准日期格式的宽度
worksheet.Column(col + 1).Width = 15;
break;
default:
// 策略D:对其他列使用自动调整宽度
// 注意:AutoFit/BestFit 对大量数据可能影响性能
worksheet.Column(col + 1).BestFit = true; // 或 .AutoFit();
break;
}
}
// 设置所有数据行的行高(例如,统一为18磅)
if (worksheet.Dimension != null)
{
for (int row = 2; row <= worksheet.Dimension.Rows; row++)
{
worksheet.Row(row).Height = 18;
worksheet.Row(row).CustomHeight = true; // 明确设置为自定义高度
}
}
excelPackage.SaveAs(new FileInfo(filePath));
}
}
```
### **策略三:基于单元格内容动态计算最优列宽**
对于无法预知内容长度的列,可以在写入所有数据后,让 EPPlus 自动计算一个合适的宽度。这通常比简单的 `AutoFit()` 更灵活。
```csharp
private void ExportWithDynamicWidth(DataGridView dgv, string filePath)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage excelPackage = new ExcelPackage())
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("Sheet1");
// ... [数据写入逻辑] ...
// 写入数据后,对特定列范围进行自动调整
// 示例:自动调整第2列到倒数第2列的宽度(假设第一列是序号,最后一列是固定按钮列)
int startCol = 2;
int endCol = dgv.Columns.Count - 1; // 根据实际情况调整
for (int col = startCol; col <= endCol; col++)
{
// AutoFit() 方法会根据该列现有内容计算一个最小宽度
worksheet.Column(col).AutoFit();
// 但 AutoFit 的结果可能过窄或过宽,可以设置一个最小和最大限制
double currentWidth = worksheet.Column(col).Width;
worksheet.Column(col).Width = Math.Max(8, Math.Min(currentWidth, 50)); // 限制在8到50字符之间
}
excelPackage.SaveAs(new FileInfo(filePath));
}
}
```
### **高级技巧:批量设置与样式集成**
可以将宽度、行高设置与单元格样式(如字体、对齐、边框)结合起来,生成更专业的报表。
```csharp
private void ExportWithFullFormatting(DataGridView dgv, string filePath)
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage excelPackage = new ExcelPackage())
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("Report");
// 定义样式
var headerStyle = worksheet.Cells["A1"].Style;
headerStyle.Font.Bold = true;
headerStyle.Font.Size = 11;
headerStyle.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
headerStyle.Fill.BackgroundColor.SetColor(System.Drawing.Color.LightGray);
headerStyle.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
headerStyle.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
var dataStyle = worksheet.Cells["A2"].Style;
dataStyle.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
// ... [写入表头和数据,应用对应样式] ...
// 批量设置列宽:奇数列窄,偶数列宽
for (int col = 1; col <= dgv.Columns.Count; col++)
{
if (col % 2 == 1) // 奇数列
{
worksheet.Column(col).Width = 12;
}
else // 偶数列
{
worksheet.Column(col).Width = 25;
}
}
// 批量设置行高:表头行高30磅,数据行高20磅
worksheet.Row(1).Height = 30;
if (worksheet.Dimension != null)
{
for (int row = 2; row <= worksheet.Dimension.Rows; row++)
{
worksheet.Row(row).Height = 20;
}
}
// 为整个数据区域添加边框
if (worksheet.Dimension != null)
{
using (var range = worksheet.Cells[worksheet.Dimension.Address])
{
range.Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
range.Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
range.Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
range.Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
}
}
excelPackage.SaveAs(new FileInfo(filePath));
}
}
```
### **综合示例:一个完整的、可配置的导出方法**
以下方法整合了数据导出、许可证设置、宽度映射和基础样式,并提供了参数来控制是否自动调整列宽。
```csharp
/// <summary>
/// 将 DataGridView 数据导出到 Excel,并可控制列宽行为 [ref_1]。
/// </summary>
/// <param name="dataGridView">要导出的 DataGridView 控件。</param>
/// <param name="filePath">保存的 Excel 文件路径。</param>
/// <param name="mapColumnWidthFromDGV">是否根据 DataGridView 的列宽映射 Excel 列宽。</param>
/// <param name="defaultColumnWidth">当 mapColumnWidthFromDGV 为 false 时,使用的默认列宽。</param>
/// <param name="headerRowHeight">表头行的高度(磅)。</param>
/// <param name="dataRowHeight">数据行的高度(磅)。</param>
public static void ExportDataGridViewToExcel(DataGridView dataGridView,
string filePath,
bool mapColumnWidthFromDGV = true,
double defaultColumnWidth = 15,
double headerRowHeight = 25,
double dataRowHeight = 18)
{
// 许可证设置
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (var package = new ExcelPackage())
{
var worksheet = package.Workbook.Worksheets.Add("Sheet1");
// 写入表头
for (int col = 0; col < dataGridView.Columns.Count; col++)
{
worksheet.Cells[1, col + 1].Value = dataGridView.Columns[col].HeaderText;
// 应用表头样式
worksheet.Cells[1, col + 1].Style.Font.Bold = true;
worksheet.Cells[1, col + 1].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
}
// 写入数据
int excelRowIndex = 2;
foreach (DataGridViewRow dgvRow in dataGridView.Rows)
{
if (!dgvRow.IsNewRow)
{
for (int col = 0; col < dataGridView.Columns.Count; col++)
{
worksheet.Cells[excelRowIndex, col + 1].Value = dgvRow.Cells[col].Value;
}
excelRowIndex++;
}
}
// **设置列宽**
for (int col = 0; col < dataGridView.Columns.Count; col++)
{
double targetWidth;
if (mapColumnWidthFromDGV)
{
// 策略:从 DataGridView 映射
targetWidth = (dataGridView.Columns[col].Width / 7.5) + 1;
targetWidth = Math.Max(targetWidth, 5); // 设置最小宽度
}
else
{
// 策略:使用统一默认宽度
targetWidth = defaultColumnWidth;
}
worksheet.Column(col + 1).Width = targetWidth;
}
// **设置行高**
worksheet.Row(1).Height = headerRowHeight; // 表头行
if (worksheet.Dimension != null)
{
for (int row = 2; row <= worksheet.Dimension.Rows; row++)
{
worksheet.Row(row).Height = dataRowHeight;
worksheet.Row(row).CustomHeight = true;
}
}
// 可选:自动调整所有列宽(可能会覆盖之前的手动设置)
// worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
package.SaveAs(new FileInfo(filePath));
}
}
```
通过上述方法和策略,您可以灵活地控制导出 Excel 文件的行列尺寸,从而生成布局合理、阅读友好的电子表格 [ref_1]。关键是根据源 `DataGridView` 的显示需求和数据特点,选择最适合的宽度设置策略。