老师布置了一个任务,就是从txt文件中提取到一些信息
然后有一个Excel的模板
需要把数据导入进去,并且完成校验,最后保存Excel文件等一系列事件

我首先想到了国人开发的NPOI—因为它可以处理Excel和Word文件等等
在我处理的过程中,出现了一个问题,可能因为自己学艺不精,所以在此贴出自己写的代码为以后学会了纠错

方法一:NPOI——现在没有找到为什么ShiftRow会删除下两行的内容的问题

这些代码分别是打开workbook、插入N行数据

  1. public HSSFWorkbook NPOIOpenExcel(string FileName)
  2. {
  3. HSSFWorkbook MyHSSFWorkBook;
  4. FileStream fs = new FileStream(FileName, FileMode.Open);
  5. MyHSSFWorkBook = new HSSFWorkbook(fs);
  6. return MyHSSFWorkBook;
  7. }
  8. public XSSFWorkbook NPOIOpenExcel2(string FileName)
  9. {
  10. XSSFWorkbook MyXSSFWorkBook;
  11. FileStream fs = new FileStream(FileName, FileMode.Open);
  12. MyXSSFWorkBook = new XSSFWorkbook(fs);
  13. return MyXSSFWorkBook;
  14. }
  15. //参数说明
  16. //第一个:指定操作的Sheet。
  17. //第二个:指定在第几行指入(插入行的位置)
  18. //第三个:指定要插入多少行
  19. //第四个:源单元格格式的行,
  20. private void MyInsertRow(ref HSSFSheet sheet, int startrow, int rowcount, HSSFRow FormatRow)
  21. {
  22. //批量移动行
  23. sheet.ShiftRows(startrow,sheet.LastRowNum,rowcount,true,false,true);
  24. //对批量移动后空出的空行插,创建相应的行,并以插入行的上一行为格式源(即:插入行-1的那一行)
  25. for (int i = startrow; i < startrow + rowcount - 1; i++)
  26. {
  27. HSSFRow targetRow = null;
  28. HSSFCell sourceCell = null;
  29. HSSFCell targetCell = null;
  30. targetRow = (HSSFRow)sheet.CreateRow(i + 1);
  31. for (int m = FormatRow.FirstCellNum; m < FormatRow.LastCellNum; m++)
  32. {
  33. sourceCell = (HSSFCell)FormatRow.GetCell(m);
  34. if (sourceCell == null)
  35. continue;
  36. targetCell = (HSSFCell)targetRow.CreateCell(m);
  37. //targetCell.Encoding = sourceCell.Encoding;
  38. targetCell.CellStyle = sourceCell.CellStyle;
  39. targetCell.SetCellType(sourceCell.CellType);
  40. }
  41. //CopyRow(sourceRow, targetRow);
  42. //Util.CopyRow(sheet, sourceRow, targetRow);
  43. }
  44. HSSFRow firstTargetRow = (HSSFRow)sheet.GetRow(startrow);
  45. HSSFCell firstSourceCell = null;
  46. HSSFCell firstTargetCell = null;
  47. for (int m = FormatRow.FirstCellNum; m < FormatRow.LastCellNum; m++)
  48. {
  49. firstSourceCell = (HSSFCell)FormatRow.GetCell(m);
  50. if (firstSourceCell == null)
  51. continue;
  52. firstTargetCell = (HSSFCell)firstTargetRow.CreateCell(m);
  53. //firstTargetCell.Encoding = firstSourceCell.Encoding;
  54. firstTargetCell.CellStyle = firstSourceCell.CellStyle;
  55. firstTargetCell.SetCellType(firstSourceCell.CellType);
  56. }
  57. }
  58. private void MyInsertRow(ref XSSFSheet sheet, int startrow, int rowcount, XSSFRow FormatRow)
  59. {
  60. //批量移动行
  61. sheet.ShiftRows(startrow, sheet.LastRowNum, rowcount, true, false);
  62. //对批量移动后空出的空行插,创建相应的行,并以插入行的上一行为格式源(即:插入行-1的那一行)
  63. for (int i = startrow; i < startrow + rowcount - 1; i++)
  64. {
  65. XSSFRow targetRow = null;
  66. XSSFCell sourceCell = null;
  67. XSSFCell targetCell = null;
  68. targetRow = (XSSFRow)sheet.CreateRow(i + 1);
  69. for (int m = FormatRow.FirstCellNum; m < FormatRow.LastCellNum; m++)
  70. {
  71. sourceCell = (XSSFCell)FormatRow.GetCell(m);
  72. if (sourceCell == null)
  73. continue;
  74. targetCell = (XSSFCell)targetRow.CreateCell(m);
  75. //targetCell.Encoding = sourceCell.Encoding;
  76. targetCell.CellStyle = sourceCell.CellStyle;
  77. targetCell.SetCellType(sourceCell.CellType);
  78. }
  79. //CopyRow(sourceRow, targetRow);
  80. //Util.CopyRow(sheet, sourceRow, targetRow);
  81. }
  82. XSSFRow firstTargetRow = (XSSFRow)sheet.GetRow(startrow);
  83. XSSFCell firstSourceCell = null;
  84. XSSFCell firstTargetCell = null;
  85. for (int m = FormatRow.FirstCellNum; m < FormatRow.LastCellNum; m++)
  86. {
  87. firstSourceCell = (XSSFCell)FormatRow.GetCell(m);
  88. if (firstSourceCell == null)
  89. continue;
  90. firstTargetCell = (XSSFCell)firstTargetRow.CreateCell(m);
  91. //firstTargetCell.Encoding = firstSourceCell.Encoding;
  92. firstTargetCell.CellStyle = firstSourceCell.CellStyle;
  93. firstTargetCell.SetCellType(firstSourceCell.CellType);
  94. }
  95. }
  96. private void SaveExcel(string dir,XSSFWorkbook workbook)
  97. {
  98. using (FileStream files = new FileStream(dir, FileMode.Create))
  99. {
  100. workbook.Write(files);
  101. }
  102. }

image.png

但是插入N行之后,最后三行footer内容消失了
image.png
用的是这个方法

  1. private void ButtonClick(object sender,ItemClickEventArgs e)
  2. {
  3. DirectoryInfo d = GetDirectoryInfo(3);
  4. string modelPath = d.FullName + "CTCS_ExcelModel.xlsx";
  5. List<TxtPoint> points = new List<TxtPoint>();
  6. points.Add(new TxtPoint { Bear = 0.29038476533, DeltaBear = 0.089987576 });
  7. points.Add(new TxtPoint { Bear = 0.23984676, DeltaBear = 0.1231243566 });
  8. points.Add(new TxtPoint { Bear = 0.012398675, DeltaBear = 11.2345645756 });
  9. points.Add(new TxtPoint { Bear = 0.29038476533, DeltaBear = 0.089987576 });
  10. points.Add(new TxtPoint { Bear = 0.23984676, DeltaBear = 0.1231243566 });
  11. points.Add(new TxtPoint { Bear = 0.012398675, DeltaBear = 11.2345645756 });
  12. points.Add(new TxtPoint { Bear = 0.012398675, DeltaBear = 11.2345645756 });
  13. points.Add(new TxtPoint { Bear = 0.29038476533, DeltaBear = 0.089987576 });
  14. points.Add(new TxtPoint { Bear = 0.23984676, DeltaBear = 0.1231243566 });
  15. points.Add(new TxtPoint { Bear = 0.012398675, DeltaBear = 11.2345645756 });
  16. XSSFWorkbook workbook = NPOIOpenExcel2(modelPath);
  17. XSSFSheet sheet = (XSSFSheet)workbook.GetSheetAt(0);
  18. XSSFRow formatRow = (XSSFRow)sheet.GetRow(3);
  19. MyInsertRow(ref sheet, 6, points.Count - 4, formatRow);
  20. ////ImportData(ref sheet, 2, points);//导入数据
  21. SaveExcel(d.FullName + "副本CTCS_ExcelModel.xlsx", workbook);
  22. }

否则用这个方法,则会出现这个问题

  1. /// <summary>
  2. /// NPOI使用ShiftRows向Excel插入行,并复制原有样式
  3. /// </summary>
  4. /// <param name="file"></param>模板文件
  5. /// <param name="dir">导出路径</param>
  6. public void ShiftRows(string file,string dir,List<TxtPoint> txtPoints)
  7. {
  8. try
  9. {
  10. FileStream fs = new FileStream(file, FileMode.Open);
  11. XSSFWorkbook workbook = new XSSFWorkbook(fs);
  12. ISheet sheet = (XSSFSheet)workbook.GetSheetAt(0);
  13. int startRow = 3;// 6;//开始插入行索引
  14. //列控数据表0-地理信息表默认可填充四行数据
  15. //当导入的DataTable超出4行时,使用ShiftRows插入行
  16. if (txtPoints.Count > 4)
  17. {
  18. //插入行
  19. Console.WriteLine(sheet.LastRowNum.ToString());
  20. sheet.ShiftRows(startRow, sheet.LastRowNum, txtPoints.Count - 4, false, false);// txtPoints.Count - 4, true, false);
  21. //sheet.ShiftRows(startRow, sheet.LastRowNum, txtPoints.Count - 4, true, false);
  22. var rowSource = sheet.GetRow(2);
  23. var rowStyle = rowSource.RowStyle;//获取当前行样式
  24. for (int i = startRow; i < startRow + txtPoints.Count-1; i++)
  25. {
  26. var rowInsert = sheet.CreateRow(i);
  27. if (rowStyle != null)
  28. {
  29. rowInsert.RowStyle = rowStyle;
  30. rowInsert.Height = rowSource.Height;
  31. }
  32. for (int col = 0; col < rowSource.LastCellNum; col++)
  33. {
  34. var cellsource = rowSource.GetCell(col);
  35. var cellInsert = rowInsert.CreateCell(col);
  36. var cellStyle = cellsource.CellStyle;
  37. //设置单元格样式
  38. if (cellStyle != null)
  39. {
  40. cellInsert.CellStyle = cellsource.CellStyle;
  41. }
  42. }
  43. }
  44. }
  45. //绑定数据
  46. for (int j = 0; j < txtPoints.Count; j++)
  47. {
  48. TxtPoint t = txtPoints[j];
  49. IRow r = sheet.GetRow(j + 2);
  50. r.Cells[0].SetCellValue(j + 1);
  51. r.Cells[1].SetCellValue("江仓站");
  52. r.Cells[9].SetCellValue(t.Bear);
  53. r.Cells[10].SetCellValue(t.DeltaBear);
  54. }
  55. //sheet.GetRow
  56. //后续操作
  57. using (FileStream files = new FileStream(dir, FileMode.Create))
  58. {
  59. workbook.Write(files);
  60. }
  61. fs.Dispose();
  62. }
  63. catch(Exception ex)
  64. {
  65. XtraMessageBox.Show(ex.Message);
  66. }
  67. }

不仅出现下三行内容消失,新增的行高度和格式也不同

这真是个无解的问题
在我一筹莫展的过了一天,然后想到DevExpress有一个可视的报表软件SpreadSheetControl

方法二:解决了我之前的更改模板的问题,而且可以在不影响Header和Footer的情况下,在中间插入格式的N行,并且暂时没发现问题!Nice!

我是直接用的DevExpress的Template Gallery中的SpreadSheet
步骤如下
基本操作,选择项目
image.png
找到SpreadSheet就可以很方便啦
image.png

首先贴出一些常用的打开。保存。插入N行的代码

  1. //打开文件操作
  2. public static string OpenExcel()
  3. {
  4. OpenFileDialog fileDialog = new OpenFileDialog();
  5. fileDialog.Multiselect = true;
  6. fileDialog.Title = "请选择文件";
  7. fileDialog.Filter = "所有文件(*.xls)|*.xls"; //设置要选择的文件的类型
  8. if (fileDialog.ShowDialog() == DialogResult.OK)
  9. {
  10. return fileDialog.FileName;//返回文件的完整路径
  11. }
  12. else
  13. {
  14. return null;
  15. }
  16. }
  17. //导入Excel
  18. private void ImportData(string filePath)//导入按钮
  19. {
  20. if (!string.IsNullOrEmpty(filePath))
  21. {
  22. IWorkbook workbook = spreadsheetControl.Document;
  23. workbook.LoadDocument(filePath);
  24. }
  25. }
  26. //保存Excel文件
  27. private void SaveExcel()
  28. {
  29. try
  30. {
  31. string s = GetDirectoryInfo(3).FullName + "副本CTCS_ExcelModel.xlsx";
  32. spreadsheetControl.SaveDocument(s,DocumentFormat.Xlsx);
  33. }
  34. catch (Exception ExError)
  35. {
  36. XtraMessageBox.Show("该文件正被别的地方占用");
  37. ExError.ToString();
  38. }
  39. }
  40. public static DirectoryInfo GetDirectoryInfo(int index)
  41. {
  42. string directoryindex = "";
  43. for (int i = 0; i < index; i++)
  44. {
  45. directoryindex += @"..\";
  46. }
  47. DirectoryInfo di = new DirectoryInfo(string.Format("{0}{1}", System.Windows.Forms.Application.StartupPath, directoryindex));
  48. return di;
  49. }

不过本来写了这些打开、导入方法,好像这个demo已经包含了这些功能
所以这些我就注释掉了

  1. private void spreadsheetCommandBarButtonItem190_ItemClick(object sender, ItemClickEventArgs e)
  2. {
  3. //DirectoryInfo di = GetDirectoryInfo(3);
  4. //string file = di.FullName + "CTCS_ExcelModel.xlsx";
  5. //ImportExcel(file);
  6. }

一些插入行和获取行列信息的代码

  1. //访问行
  2. Workbook workbook=new Workbook();
  3. //Access a Collection of rows
  4. RowCollection rows = workbook.Worksheets[0].Rows;
  5. //Access the first row by its index in the collection of rows;
  6. Row firstRow_byIndex = rows[0];
  7. //Access ths first row by its unique name
  8. Row firstRow_byName = rows["1"];
  9. 访问列
  10. Workbook workbook=new Workbook();
  11. //Access a collection of columns
  12. ColumnCollection columns = workbook.Worksheets[0].Columns.
  13. //ColumnCollection columns=workbook.Worksheets[0].Columns;
  14. Column firstColumn_byIndex = columns[0];
  15. //Access the first column by its unique name;
  16. Column firstColumn_byName = columns["A"];//

一些更具体的使用方法来自如下:
https://blog.csdn.net/hotmee/article/details/50554381

接下来插入N行的思路是:先在指定位置添加Rows,这些Row是空白的无格式的,之后就需要CopyFrom,从现有行复制模板
这里要注意Insert(int index,int count);这里的index指的是,
image.png

  1. /// <summary>
  2. /// 插入N行
  3. /// </summary>
  4. /// <param name="workbook">Excel工作区</param>
  5. /// <param name="sheetIndex">sheet索引</param>
  6. /// <param name="startRowIndex">开始插入行的索引(向下插入表的)</param>
  7. /// <param name="rowcount">插入的行数</param>
  8. private void InsertRows(IWorkbook workbook,int sheetIndex,int startRowIndex,int rowcount)
  9. {
  10. Worksheet sheet = workbook.Worksheets[sheetIndex];
  11. RowCollection rows = sheet.Rows;
  12. //example:
  13. //sheet.Rows.Insert(6, 5);//在行索引为6的行下方插入5行,即增加7、8、9、10、11
  14. sheet.Rows.Insert(startRowIndex, rowcount);
  15. for(int j=startRowIndex; j<startRowIndex+rowcount;j++)
  16. {
  17. sheet.Rows[j].CopyFrom(sheet.Rows[3]);//复制行格式
  18. }
  19. }
  20. //使用方法//我在第6行下方添加了5行,并更改了格式
  21. //InsertRows(spreadsheetControl.Document, 0,6,5);

图示:
image.png
插入5行
image.png
稍微更改了一下
加上了formatRow的index

  1. /// <summary>
  2. /// 插入N行
  3. /// </summary>
  4. /// <param name="workbook">Excel工作区</param>
  5. /// <param name="sheetIndex">sheet索引</param>
  6. /// <param name="startRowIndex">开始插入行的索引(向下插入表的)</param>
  7. /// <param name="rowcount">插入的行数</param>
  8. /// <param name="formatRowIndex">格式行的索引值</param>
  9. private void InsertRows(IWorkbook workbook,int sheetIndex,int startRowIndex,int rowcount,int formatRowIndex)
  10. {
  11. Worksheet sheet = workbook.Worksheets[sheetIndex];
  12. RowCollection rows = sheet.Rows;
  13. //example:
  14. //sheet.Rows.Insert(6, 5);//在行索引为6的行下方插入5行,即增加7、8、9、10、11
  15. sheet.Rows.Insert(startRowIndex, rowcount);
  16. for(int j=startRowIndex; j<startRowIndex+rowcount;j++)
  17. {
  18. sheet.Rows[j].CopyFrom(sheet.Rows[formatRowIndex-1]);//复制行格式
  19. }
  20. }

就成了复制第三行的样式
image.png

SpreadSheet合并和取消合并方法
//取消合并
sheet.UnMergeCells(sheet.Range[“D2:J2”]);、
//合并
sheet.MergeCells(sheet.Range[“D2:D3”]);
行删除
//更改cell的边框格式为OutLine
sheet.Cells[“D2”].Borders.SetOutsideBorders(Color.Black, BorderLineStyle.Medium);