ExportExcel_EPPlus.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. using OfficeOpenXml;
  2. using OfficeOpenXml.Style;
  3. using System;
  4. using System.ComponentModel;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.Windows.Forms;
  8. using System.Threading.Tasks;
  9. using Dongke.IBOSS.PRD.Basics.BaseControls.ExportExcel;
  10. using Curtain.Log;
  11. public class ExportExcel_EPPlus : IDisposable
  12. {
  13. private ExportExcelProgress _form;
  14. private BackgroundWorker _worker;
  15. private ProgressBar _progressBar;
  16. private Label _statusLabel;
  17. public ExportParameters _exportParams;
  18. private string _title;
  19. public ExportExcel_EPPlus()
  20. {
  21. _form = new ExportExcelProgress();
  22. _progressBar = _form.StatusBar;
  23. _statusLabel = _form.StatusLabel;
  24. InitializeBackgroundWorker();
  25. }
  26. private void InitializeBackgroundWorker()
  27. {
  28. _worker = new BackgroundWorker
  29. {
  30. WorkerReportsProgress = true,
  31. WorkerSupportsCancellation = true
  32. };
  33. _worker.DoWork += Worker_DoWork;
  34. _worker.ProgressChanged += Worker_ProgressChanged;
  35. _worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
  36. }
  37. public static void ExportExcel(DataGridView dataGridView, string sheetName, string filePath, bool includeHiddenColumns = false)
  38. {
  39. ExportExcel_EPPlus _exporter = new ExportExcel_EPPlus();
  40. _exporter.ExportAsync(dataGridView, sheetName, filePath, false);
  41. }
  42. //public async Task
  43. public void ExportAsync(DataGridView dataGridView, string sheetName, string filePath, bool includeHiddenColumns = false)
  44. {
  45. _title = sheetName;
  46. using (SaveFileDialog saveFileDialog = new SaveFileDialog())
  47. {
  48. saveFileDialog.Title = "请选择要导出Excel文件的路径和文件名";
  49. saveFileDialog.AddExtension = true;
  50. saveFileDialog.OverwritePrompt = false;
  51. //saveFileDialog.DefaultExt = "xls";
  52. //saveFileDialog.Filter = "97-2003 Excel文件(*.xls)|*.xls|所有文件(*.*)|*.*";
  53. saveFileDialog.DefaultExt = "xlsx";
  54. saveFileDialog.Filter = "Excel文件(*.xlsx)|*.xlsx|所有文件(*.*)|*.*";
  55. saveFileDialog.FileName = filePath;
  56. if (saveFileDialog.ShowDialog() == DialogResult.Cancel)
  57. {
  58. return;
  59. }
  60. filePath = saveFileDialog.FileName;
  61. }
  62. if (File.Exists(filePath))
  63. {
  64. DialogResult dr = MessageBox.Show("选择的文件【" + filePath + "】已经存在,您确定要覆盖原来的文件吗?注意:覆盖原文件后,原文件中的全部数据都将丢失!\r\n单击[是]按钮,将覆盖原文件,单击[否]按钮,导出数据将以追加的形式添加到文件中,单击[取消]按钮将取消导出"
  65. , _title, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button3);
  66. switch (dr)
  67. {
  68. case DialogResult.Cancel:
  69. return;
  70. case DialogResult.Yes:
  71. try
  72. {
  73. File.Delete(filePath);
  74. }
  75. catch (Exception projectError)
  76. {
  77. Logger.Error(projectError, "ExportExcel_EPPlus");
  78. //MessageBox.Show(projectError.Message, sheetName, MessageBoxButtons.OK, MessageBoxIcon.Error);
  79. MessageBox.Show("数据导出失败,原因是文件【" + filePath + "】无法覆盖,可能该文件正在使用中,请关闭该文件后再试", _title, MessageBoxButtons.OK, MessageBoxIcon.Warning);
  80. return;
  81. }
  82. break;
  83. }
  84. }
  85. if (_worker.IsBusy)
  86. {
  87. MessageBox.Show("导出正在进行中,请稍候...", _title);
  88. return;
  89. }
  90. _form._eeep = this;
  91. _form.Text += _title;
  92. _form.Show();
  93. UpdateStatus("准备导出...");
  94. UpdateProgress(0);
  95. try
  96. {
  97. _exportParams = new ExportParameters
  98. {
  99. DataGridView = dataGridView,
  100. SheetName = sheetName,
  101. FilePath = filePath,
  102. IncludeHiddenColumns = includeHiddenColumns
  103. };
  104. //await Task.Run(() => _worker.RunWorkerAsync(exportParams));
  105. _worker.RunWorkerAsync(_exportParams);
  106. }
  107. catch (Exception ex)
  108. {
  109. MessageBox.Show($"导出失败: {ex.Message}", _title);
  110. UpdateStatus("导出失败");
  111. UpdateProgress(0);
  112. }
  113. }
  114. public void CancelExport()
  115. {
  116. if (_worker.IsBusy)
  117. {
  118. _worker.CancelAsync();
  119. UpdateStatus("正在取消...");
  120. }
  121. }
  122. private void Worker_DoWork(object sender, DoWorkEventArgs e)
  123. {
  124. var parameters = (ExportParameters)e.Argument;
  125. var dataGridView = parameters.DataGridView;
  126. var filePath = parameters.FilePath;
  127. //ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
  128. using (var excelPackage = new ExcelPackage())
  129. {
  130. var worksheet = excelPackage.Workbook.Worksheets.Add(parameters.SheetName);
  131. // 导出列标题
  132. _worker.ReportProgress(0, "导出开始");
  133. ExportHeaders(worksheet, dataGridView, parameters.IncludeHiddenColumns);
  134. _worker.ReportProgress(10, "导出表头完成");
  135. if (_worker.CancellationPending)
  136. {
  137. e.Cancel = true;
  138. return;
  139. }
  140. // 导出数据行
  141. ExportDataRows(worksheet, dataGridView, parameters.IncludeHiddenColumns, _worker);
  142. if (_worker.CancellationPending)
  143. {
  144. e.Cancel = true;
  145. return;
  146. }
  147. _worker.ReportProgress(85, "保存文件中...");
  148. // 应用格式
  149. ApplyFormatting(worksheet, dataGridView, parameters.IncludeHiddenColumns);
  150. _worker.ReportProgress(95, "保存文件中。。。");
  151. // 保存文件
  152. excelPackage.SaveAs(new FileInfo(filePath));
  153. _worker.ReportProgress(100, "保存文件完成");
  154. }
  155. }
  156. private void ExportHeaders(ExcelWorksheet worksheet, DataGridView dataGridView, bool includeHiddenColumns)
  157. {
  158. int colIndex = 1;
  159. foreach (DataGridViewColumn column in dataGridView.Columns)
  160. {
  161. if (column.Visible || includeHiddenColumns)
  162. {
  163. worksheet.Cells[1, colIndex].Value = column.HeaderText;
  164. colIndex++;
  165. }
  166. }
  167. // 设置标题行样式
  168. using (var range = worksheet.Cells[1, 1, 1, colIndex - 1])
  169. {
  170. range.Style.Font.Name = "宋体";
  171. range.Style.Font.Bold = true;
  172. range.Style.Font.Size = 9;//12;
  173. range.Style.Font.Color.SetColor(Color.White);
  174. range.Style.Fill.PatternType = ExcelFillStyle.Solid;
  175. //range.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(79, 129, 189));
  176. range.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(48, 58, 70));
  177. range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
  178. range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
  179. }
  180. }
  181. private void ExportDataRows(ExcelWorksheet worksheet, DataGridView dataGridView,
  182. bool includeHiddenColumns, BackgroundWorker worker)
  183. {
  184. int totalRows = dataGridView.Rows.Count;
  185. int exportedRows = 0;
  186. for (int rowIndex = 0; rowIndex < dataGridView.Rows.Count; rowIndex++)
  187. {
  188. if (dataGridView.Rows[rowIndex].IsNewRow)
  189. continue;
  190. int colIndex = 1;
  191. foreach (DataGridViewColumn column in dataGridView.Columns)
  192. {
  193. if (column.Visible || includeHiddenColumns)
  194. {
  195. var cellValue = dataGridView.Rows[rowIndex].Cells[column.Index].Value;
  196. worksheet.Cells[rowIndex + 2, colIndex].Value = FormatCellValue(cellValue, column.ValueType);
  197. colIndex++;
  198. }
  199. }
  200. exportedRows++;
  201. // 每处理10行报告一次进度
  202. if (exportedRows % 10 == 0 || exportedRows == totalRows)
  203. {
  204. int progress = 10 + (int)((double)exportedRows / totalRows * 70);
  205. worker.ReportProgress(progress, $"正在导出数据 ({exportedRows}/{totalRows})");
  206. if (worker.CancellationPending)
  207. return;
  208. }
  209. }
  210. }
  211. private object FormatCellValue(object value, Type valueType)
  212. {
  213. if (value == null || value == DBNull.Value)
  214. return string.Empty;
  215. if (valueType == typeof(DateTime) && value is DateTime dateTime)
  216. return dateTime;
  217. if (valueType == typeof(bool) && value is bool boolValue)
  218. return boolValue ? "是" : "否";
  219. if (valueType == typeof(decimal) || valueType == typeof(double) || valueType == typeof(float))
  220. return Convert.ToDecimal(value);
  221. return value.ToString();
  222. }
  223. private void ApplyFormatting(ExcelWorksheet worksheet, DataGridView dataGridView, bool includeHiddenColumns)
  224. {
  225. if (worksheet.Dimension == null)
  226. return;
  227. // 自动调整列宽
  228. //worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
  229. // 设置数据区域样式
  230. using (var range = worksheet.Cells[2, 1, worksheet.Dimension.End.Row, worksheet.Dimension.End.Column])
  231. {
  232. range.Style.Font.Name = "宋体";
  233. range.Style.Font.Size = 9;
  234. range.Style.Border.Top.Style = ExcelBorderStyle.Thin;
  235. range.Style.Border.Bottom.Style = ExcelBorderStyle.Thin;
  236. range.Style.Border.Left.Style = ExcelBorderStyle.Thin;
  237. range.Style.Border.Right.Style = ExcelBorderStyle.Thin;
  238. range.Style.HorizontalAlignment = ExcelHorizontalAlignment.Left;
  239. range.Style.VerticalAlignment = ExcelVerticalAlignment.Center;
  240. }
  241. // 设置数字格式
  242. int excelColIndex = 1;
  243. foreach (DataGridViewColumn column in dataGridView.Columns)
  244. {
  245. if (column.Visible || includeHiddenColumns)
  246. {
  247. var excelColumn = worksheet.Column(excelColIndex);
  248. if (column.ValueType == typeof(DateTime))
  249. {
  250. excelColumn.Style.Numberformat.Format = "yyyy-MM-dd hh:mm:ss";
  251. excelColumn.Style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
  252. }
  253. else if (column.ValueType == typeof(decimal) || column.ValueType == typeof(double))
  254. {
  255. //excelColumn.Style.Numberformat.Format = "#,##0.00";
  256. excelColumn.Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
  257. }
  258. else if (column.ValueType == typeof(int))
  259. {
  260. //excelColumn.Style.Numberformat.Format = "#,##0";
  261. excelColumn.Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
  262. }
  263. //excelColumn.AutoFit();
  264. excelColIndex++;
  265. }
  266. }
  267. // 设置交替行颜色
  268. /*for (int row = 2; row <= worksheet.Dimension.End.Row; row++)
  269. {
  270. if (row % 2 == 0) // 偶数行
  271. {
  272. using (var range = worksheet.Cells[row, 1, row, worksheet.Dimension.End.Column])
  273. {
  274. range.Style.Fill.PatternType = ExcelFillStyle.Solid;
  275. range.Style.Fill.BackgroundColor.SetColor(Color.FromArgb(242, 242, 242));
  276. }
  277. }
  278. }*/
  279. // 自动调整列宽
  280. //worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
  281. // 冻结首行
  282. worksheet.View.FreezePanes(2, 1);
  283. }
  284. private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
  285. {
  286. UpdateProgress(e.ProgressPercentage);
  287. if (e.UserState != null)
  288. {
  289. UpdateStatus(e.UserState.ToString());
  290. }
  291. }
  292. private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  293. {
  294. this._form.Done();
  295. if (e.Cancelled)
  296. {
  297. UpdateStatus("导出已取消");
  298. MessageBox.Show("导出操作已取消。", _title);
  299. }
  300. else if (e.Error != null)
  301. {
  302. UpdateStatus("导出失败");
  303. MessageBox.Show($"导出过程中发生错误: {e.Error.Message}", _title);
  304. }
  305. else
  306. {
  307. UpdateStatus("导出完成");
  308. MessageBox.Show("数据导出成功!", _title);
  309. }
  310. //UpdateProgress(0);
  311. }
  312. private void UpdateProgress(int value)
  313. {
  314. if (_progressBar != null && !_progressBar.IsDisposed)
  315. {
  316. _progressBar.Invoke(new Action(() =>
  317. {
  318. _progressBar.Value = value;
  319. }));
  320. }
  321. }
  322. private void UpdateStatus(string message)
  323. {
  324. if (_statusLabel != null && !_statusLabel.IsDisposed)
  325. {
  326. _statusLabel.Invoke(new Action(() =>
  327. {
  328. _statusLabel.Text = message;
  329. }));
  330. }
  331. }
  332. public void Dispose()
  333. {
  334. if (_worker != null)
  335. {
  336. _worker.Dispose();
  337. }
  338. }
  339. public class ExportParameters
  340. {
  341. public DataGridView DataGridView
  342. {
  343. get; set;
  344. }
  345. public string SheetName
  346. {
  347. get; set;
  348. }
  349. public string FilePath
  350. {
  351. get; set;
  352. }
  353. public bool IncludeHiddenColumns
  354. {
  355. get; set;
  356. }
  357. }
  358. }