ronald_han

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

前段时间项目需要,写了个操作Excel表格的程序。先介绍背景,合作单位每天有气井生产数据产生,他们的惯例是将数据存放在一个Excel表格中,通过日期及井口名称标识记录的唯一性,为陈述方便,此表称为总表。由于数据管理的落后,他们已经在总表中存放所有井口(约200口)近3年的生产数据,约有2万条记录,每天新增记录有200条。另外,单位要对每个井口的生产状况进行分析,他们现在的做法是为井口建立Excel文件,一般一个文件包含10个工作表,每个工作表存放一个井口从生产到当日的生产数据,这些Excel文件称为单井表。现在的一个工作流程是,手工将总表中每个井口当日生产数据复制到单井表中,一般熟练人员需要两三个小时才能完成此项单调工作。复制结束后,需要更新单井表中每口井的生产状态曲线,将当日数据追加上去。程序的目标是,自动化完成数据的复制及生产状态曲线的更新。

考虑到复制数据和更新曲线图会耗费一段时间,有必要在界面上对处理进度进行提示,由此选用BackgroundWorker,将耗时的复制、更新操作放入后台进行就变得必须了。

  BackgroundWorker对象有三个主要的事件:

  DoWork - 当BackgroundWorker对象的多线程操作被执行时触发。

  RunWokerCompleted - 当BackgroundWoker对象的多线程操作完成时触发。

  ProgressChanged - 当BackgroundWorker对象的多线程操作状态改变时触发。

  另外还有一个非常重要的属性WorkerReportsProgress - 如果想让BackgroundWorker对象以异步的方式报告线程实时进度,必须将该属性的值设为true。

  BackgroundWorker对象的ReportProgress方法用于向主线程返回后台线程执行的实时进度。

下面上代码:

  1         public void DoWork(object sender, DoWorkEventArgs e)
  2         {
  3             // 事件处理,指定处理函数  
  4             e.Result = ProcessProgress(bkWorker, e);
  5         }
  6 
  7         private int ProcessProgress(object sender, DoWorkEventArgs e)
  8         {
  9             //从一个excel表格中复制数据到另外一个表格中
 10             //打开一个工作表,找到日期一栏为当日日期的一行的位置
 11             //找到此工作表中日期为当日日期的最后一条记录的位置
 12             //循环访问中间的行,将其copy到对应的工作表中
 13             //循环体中要首先判断工作表中最后一条记录的位置
 14             //之后将copy的数据粘贴到其之后一行,保存文档
 15 
 16             //配置打开文件及存放文件路径,以后修改为使用xml文件保存参数的形式
 17             //string openFileName = @"D:\ExcelData\各气井每日生产数据.xls";
 18             //string saveFileName = @"D:\ExcelData\单井每日生产数据.xls";
 19             string openFileName = strAllData;
 20             string saveFileFolder = strSingleData;
 21 
 22             try
 23             {
 24                 //查询某一列第一次出现某个值所在单元格的位置
 25                 //查询某一列最后一次出现某个值所在单元格的位置
 26                 Excel.Workbook wb = p_eh.GetWBFromExcel(openFileName);
 27                 Excel.Worksheet ws = (Excel.Worksheet)wb.Worksheets[1];
 28                 string columnStart = "A1";
 29                 //需要查看此处date的格式
 30                 //strDate 2013-07-12格式
 31                 string strDate = DateTime.Now.Date.ToShortDateString();
 32                 strDate = strDate.Replace("-0", "-");
 33                 Excel.Range firstRange = p_eh.FindFirstMatch(ws, columnStart, strDate);
 34                 int rowIndexStart = firstRange.Row;
 35                 Excel.Range lastRange = p_eh.FindLastMatch(ws, columnStart, strDate);
 36                 int rowIndexEnd = lastRange.Row;
 37                 if (rowIndexStart.Equals(rowIndexEnd))
 38                 {
 39                     MessageBox.Show("输入文件数据有误");
 40                 }
 41                                
 42                 Excel.Workbook swb = null;
 43                 Excel.Worksheet sws = null;
 44                 string saveFileName;
 45                 for (int i = 0; i <= rowIndexEnd - rowIndexStart; i++)
 46                 {
 47                     //将所在行的数据拷贝到对应的文件worksheet中
 48                     //获取名称单元格
 49                     Excel.Range nameRange = ws.get_Range("B" + (i + rowIndexStart).ToString(), Type.Missing);
 50                     string wellName = nameRange.Text.ToString();
 51                     if (wellName != null)
 52                     {
 53                         //根据获取的名称,查询所在的xls文件名称
 54                         saveFileName = GetSaveFileName(wellName);
 55                         swb = p_eh.GetWBFromExcel(saveFileFolder + "\\" + saveFileName);
 56                         sws = p_eh.GetWSByName(swb, wellName);
 57                         //需要找到当前工作表最后一条记录所在的位置
 58                         int lastUsedRowIndex = p_eh.GetLastUsedRow(sws, columnStart);
 59                         int lastUsedColumnIndex = p_eh.GetLastUsedColumn(sws, "A5");
 60                         string columnName = p_eh.GetExcelColumnName(lastUsedColumnIndex);
 61                         Excel.Range dataRange = ws.get_Range("A" + (rowIndexStart + i).ToString(), columnName + (rowIndexStart + i).ToString());
 62                         Excel.Range distinationRange = sws.get_Range("A" + (lastUsedRowIndex + int.Parse("1")).ToString(), columnName + (lastUsedRowIndex + int.Parse("1")).ToString());
 63                         //此种复制不能保持原有格式
 64                         //dataRange.Copy(Type.Missing);
 65                         //distinationRange.PasteSpecial(Microsoft.Office.Interop.Excel.XlPasteType.xlPasteValues, Microsoft.Office.Interop.Excel.XlPasteSpecialOperation.xlPasteSpecialOperationNone, false, false);
 66                         //需要保持原有格式
 67                         sws.Cells.Columns.AutoFit();
 68                         dataRange.Copy(distinationRange);
 69                         //Excel.Range dateRange = sws.get_Range("A" + (lastUsedRowIndex + int.Parse("1")).ToString(), Type.Missing);
 70                         //dateRange.Value2 = strDate;
 71                         //dataRange.Cells.Font.Size = 10;
 72                         //dataRange.VerticalAlignment = Excel.XlVAlign.xlVAlignCenter;
 73                         //dataRange.HorizontalAlignment = Excel.XlVAlign.xlVAlignCenter;
 74                         swb.Save();
 75                     }
 76                     // 状态报告  
 77                     bkWorker.ReportProgress((int)(100*i / (rowIndexEnd - rowIndexStart)));
 78                     // 等待,用于UI刷新界面,很重要  
 79                     System.Threading.Thread.Sleep(1);
 80                 }
 81                 wb.Close(Type.Missing, Type.Missing, Type.Missing);
 82                 swb.Close(Type.Missing, Type.Missing, Type.Missing);
 83                 //此处注销使用的各种excel对象
 84                 if (ws != null)
 85                 {
 86                     System.Runtime.InteropServices.Marshal.ReleaseComObject(ws);
 87                 }
 88                 if (wb != null)
 89                 {
 90                     System.Runtime.InteropServices.Marshal.ReleaseComObject(wb);
 91                 }
 92                 if (sws != null)
 93                 {
 94                     System.Runtime.InteropServices.Marshal.ReleaseComObject(sws);
 95                 }
 96                 if (swb != null)
 97                 {
 98                     System.Runtime.InteropServices.Marshal.ReleaseComObject(swb);
 99                 }
100             }
101             catch (System.Exception ex)
102             {
103                 p_eh.Dispose();
104             }
105             p_eh.KillAllExcelProcess();
106             return -1;
107         }
108 
109         public void ProgessChanged(object sender, ProgressChangedEventArgs e)
110         {
111             this.progressBar1.Value = e.ProgressPercentage;
112             int percent = e.ProgressPercentage;
113             this.label1.Text = "处理进度:" + Convert.ToString(percent) + "%";
114         }

其他有关BackgroundWorker设置如下:

1 CheckForIllegalCrossThreadCalls = false;
2 bkWorker.WorkerReportsProgress = true;
3 bkWorker.WorkerSupportsCancellation = true;
4 bkWorker.DoWork += new DoWorkEventHandler(DoWork);
5 bkWorker.ProgressChanged += new ProgressChangedEventHandler(ProgessChanged);
6 bkWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(CompleteWork);

以上设置中第一行不检测跨线程操作,是一种简单的避免跨线程操作异常的方式,第二行则提示状态更改。

程序运行界面如下:

这个程序中主要有两点,一个是BackgroundWorker的用法,另一个是写了个操作Excel表格的类,完成数据表格和内存中DataTable的互相转换,Excel表格的各种查询。

写这篇文章是因为复习到多线程,想起来要把这点总结下,已经看到一篇写的不错的文章,贴下地址:C#多线程总结

另外本文写作参考链接:1.多线程:C#.NET中使用BackgroundWorker在模态对话框中显示进度条;2.C#中跨线程操作控件

posted on 2013-08-30 19:17  ronald_han  阅读(3268)  评论(0编辑  收藏  举报