游戏开发中最常用的工具应该当属Excel了,而Excel在数据量比较大,或者多张表进行关联的时候操作比较繁琐,偶尔会有一些工具需要,减化上述的一些操作,提升工作效率。
最开始想使用Node.js,发现二个问题,Github上第三方库,在保存 Excel 的时候会将格式给丢掉。找来找来,发现 C# 的 Epplus 比较符合需求。
第一步:准备工作
(1) 通过 NuGet 下载 Epplus
(2)Form 的构造函数中添加许可
代码语言:javascript复制using OfficeOpenXml;
using OfficeOpenXml.Drawing;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Style;
using LicenseContext = OfficeOpenXml.LicenseContext;
public Form1()
{
InitializeComponent();
// EPPlus需要添加许可
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
}
第二步:根据需求对Excel进行操作
ExcelPackage 常用的API
加载或创建 Excel 文件:
ExcelPackage.Load:从现有的 Excel 文件加载数据。
ExcelPackage.Save:保存 Excel 文件。
访问工作表和单元格:
ExcelPackage.Workbook:获取工作簿对象。
ExcelWorkbook.Worksheets:获取工作表集合。
ExcelWorksheet.Cells:获取单元格集合。
ExcelRange.Value:获取或设置单元格的值。
操作工作表:
ExcelWorksheets.Add:添加新的工作表。
ExcelWorksheets.Delete:删除指定的工作表。
操作单元格:
ExcelRange.Merge:合并单元格。
ExcelRange.Style:设置单元格样式。
保存和关闭 Excel 文件:
ExcelPackage.Save:保存 Excel 文件。
ExcelPackage.Dispose:释放 ExcelPackage 对象。
代码语言:javascript复制using (var package = new ExcelPackage(new FileInfo("path/to/excel/file.xlsx")))
{
// 获取工作簿对象
ExcelWorkbook workbook = package.Workbook;
// 获取工作表集合
ExcelWorksheets worksheets = workbook.Worksheets;
// 获取指定工作表
ExcelWorksheet worksheet = worksheets["Sheet1"]; // 如果 Sheet1位于第一个位置,使用 worksheets[0] 也可以
// 遍历每个工作表
foreach (ExcelWorksheet worksheet in workbook.Worksheets)
{
// 获取工作表名称
string sheetName = worksheet.Name;
Console.WriteLine($"工作表名称:{sheetName}");
// 获取工作表的行数和列数
int rowCount = worksheet.Dimension.Rows;
int columnCount = worksheet.Dimension.Columns;
Console.WriteLine($"行数:{rowCount}, 列数:{columnCount}");
// 遍历每行
for (int row = 1; row <= rowCount; row )
{
// 遍历每列
for (int column = 1; column <= columnCount; column )
{
// 获取单元格的值
object cellValue = worksheet.Cells[row, column].Value;
Console.WriteLine($"单元格({row}, {column}) 值:{cellValue}");
}
}
}
// 保存 Excel 文件
package.Save();
}
第三步:其它注意事项
(1)上面的 Save / SaveAs 函数,对涉及到对 Excel 的读写,如果 Excel 是只读就会报错,最好在操作之前就检测一下。
代码语言:javascript复制// 获取Excel的读写属性
FileAttributes attributes = File.GetAttributes(文件的完整路径);
// 检查文件的读写属性
if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
MessageBox.Show($"文本配置表Excel为只读模式,请修改后再操作。");
return;
}
(2)部分Excel很大,处理起来非常耗时,有以下几个方法进行优化
用 Task 或者线程单独处理 Excel 的读、写、保存操作,处理过程中如果要操作主界面的UI(如果更新进度,打印输出日志)
代码语言:javascript复制private void writeLogByTask(string str)
{
this.Invoke((Action)(() =>
{
TextBox_Log.AppendText(str "n");
// 滚动至底部
//TextBox_Log.ScrollToCaret();
}));
}
Task.Run(parseDefaultTextExcelHandler).ContinueWith(task =>
{
this.Invoke((Action)(() =>
{
// 操作主界面 UI
Button_SaveText.Enabled = true;
Button_SaveText.Text = "保存文本";
}));
});
(3)当数据量过大时,特别是需要插入数据时,注意使用数组替代List,有插入操作创建数组时预留比较大的空间
代码语言:javascript复制private int getInsertIdx(int nId)
{
int idx = -1;
for (int i = 3; i < _textIdsArr.Length; i )
{
if (nId > _textIdsArr[i - 1] && (nId < _textIdsArr[i] || _textIdsArr[i] == -1))
{
idx = i;
break;
}
}
if (idx != -1)
{
// 后移操作
for (int i = _textIdsArr.Length - 1; i > idx; i--)
{
_textIdsArr[i] = _textIdsArr[i - 1];
}
// 插入操作
_textIdsArr[idx] = nId;
}
return idx;
}
ExcelRange的读取操作,注意判断得到的值是否为空
代码语言:javascript复制ExcelWorksheet _worksheet = _excelPackage.Workbook.Worksheets[0];
string strContent = _worksheet.Cells[$"B{rowNum}"].Value?.ToString();
// 获取单元格的值
object cellValue = worksheet.Cells[row, column].Value;
// 用 "B1" 可以获取单元格的值,用 worksheet.Cells[1, 2] 也可以
(4) 单元格样式的操作方法:边框、行高、合并、背景色、文字颜色、文字大小
代码语言:javascript复制// 检查是否存在名为 "xxxx" 的工作表
bool sheetExists = excelPackage.Workbook.Worksheets.Any(sheet => sheet.Name == SHEET_NAME);
if (sheetExists)
{
excelPackage.Workbook.Worksheets.Delete(SHEET_NAME);
//excelPackage.Save();
}
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add(SHEET_NAME);
// 设置XX项目xx版本周报
// 合并 A1 到 ?1 的单元格
char colStart = 'A';
char colEnd = (char)(colStart (DEST_RELATE_DICTIONARY.Count - 1));
string strRange = string.Format("A1:{0}1", colEnd);
worksheet.Cells[strRange].Merge = true;
setCellBorder(worksheet.Cells[strRange]);
// 设置 A1 到 ?1 的单元格背景颜色为黄色
worksheet.Cells[strRange].Style.Fill.PatternType = ExcelFillStyle.Solid;
worksheet.Cells[strRange].Style.Fill.BackgroundColor.SetColor(Color.White);
// 设置 A1 到 ?1 的单元格高度为 30
worksheet.Row(1).Height = 30;
// 在 A1 到 ?1 的单元格中添加文本
worksheet.Cells["A1"].Value = "XX项目XX版本";
worksheet.Cells["A1"].Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
worksheet.Cells["A1"].Style.VerticalAlignment = ExcelVerticalAlignment.Center;
worksheet.Cells["A1"].Style.Font.Name = FONT_NAME;
worksheet.Cells["A1"].Style.Font.Bold = true;
worksheet.Cells["A1"].Style.Font.Size = 20;
// 构建第二列,定义自定义颜色
Color myColor = Color.FromArgb(255, 153, 0);
worksheet.Row(2).Height = 20;
for (int i = 0; i < FIELD_COLUMN_LIST.Count; i )
{
string secondRowColIdx = $"{(char)('A' i)}2";
ExcelRange cell = worksheet.Cells[secondRowColIdx];
// 在 B 列中设置单元格值
cell.Value = FIELD_COLUMN_LIST[i];
// 设置单元格背景颜色
cell.Style.Fill.PatternType = ExcelFillStyle.Solid;
cell.Style.Fill.BackgroundColor.SetColor(myColor);
cell.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
cell.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
cell.Style.Font.Name = FONT_NAME;
cell.Style.Font.Bold = true;
cell.Style.Font.Size = 10;
setCellBorder(cell);
}
private void setCellBorder(ExcelRange cell)
{
// 设置单元格所有线框
cell.Style.Border.Top.Style = ExcelBorderStyle.Thin;
cell.Style.Border.Left.Style = ExcelBorderStyle.Thin;
cell.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
cell.Style.Border.Right.Style = ExcelBorderStyle.Thin;
cell.Style.Font.Name = FONT_NAME;
}
(5)复制多行单元格(保留其样式),并在指定的行插入复制的行
代码语言:javascript复制private void copyRowDataAndPaste(ExcelWorksheet worksheet, RowRangeInfo rowRangeInfo, int insertRowIdx)
{
// 复制行数据
int sourceRowStart = rowRangeInfo.sourceRowStartIdx;
int sourceRowEnd = rowRangeInfo.sourceRowEndIdx;
for (int row = sourceRowEnd; row >= sourceRowStart; row--)
{
// copyStylesFromRow
worksheet.InsertRow(insertRowIdx, 1, row);
writeLogToMainThread($"在第 {insertRowIdx} 行插入一行,复制第 {row} 行的样式");
ExcelRange sourceRange = worksheet.Cells[row, 1, row, worksheet.Dimension.Columns];
ExcelRange destinationRange = worksheet.Cells[insertRowIdx, 1, insertRowIdx, worksheet.Dimension.Columns];
// update value
destinationRange.Value = sourceRange.Value;
}
}
代码语言:javascript复制// InsertRow函数的使用说明
using (var package = new ExcelPackage(new FileInfo("path/to/excel/file.xlsx")))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets["Sheet1"];
// 在第3行之后插入2行
worksheet.InsertRow(3, 2);
// 从第1行复制样式到新插入的行
worksheet.InsertRow(3, 2, 1);
// 保存修改后的Excel文件
package.Save();
}
(6)对指定的单元格设置注释
代码语言:javascript复制using (var package = new ExcelPackage(new FileInfo("path/to/excel/file.xlsx")))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets["Sheet1"];
// 获取单元格A1
ExcelRange cell = worksheet.Cells["A1"];
// 设置注释
ExcelComment comment = cell.AddComment("这是一个注释");
comment.Author = "John";
comment.AutoFit = true;
// 获取注释的 RichText 对象
ExcelRichText richText = comment.RichText.Add("这是注释的文本");
// 设置文字大写
richText.Font.Uppercase = true;
// 设置其他字体属性
richText.Font.Size = 12;
richText.Font.Bold = true;
richText.Font.Italic = true;
richText.Font.Color = Color.Red;
// 设置注释框的大小
comment.SetSize(200, 100);
// 保存修改后的Excel文件
package.Save();
}
(7)删除指定的行
代码语言:javascript复制using (var package = new ExcelPackage(new FileInfo("path/to/excel/file.xlsx")))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets["Sheet1"];
// 删除第5行
int rowIndex = 5;
worksheet.DeleteRow(rowIndex);
// 保存修改后的 Excel 文件
package.Save();
}
更多操作方法,请使用 ChatGPT 进行查询