zoukankan      html  css  js  c++  java
  • C# 使用BackgroundWorker实现WinForm异步

    写了一个基于BackgorundWorker演示异步操作的例子。由于这个理基本上实现了BackgorundWorker的大部分功能:异步操作的启动、操作结束后的回调、异步操作的撤销和进度报告等等。尽管没有太多的技术含量,姑且放上来与大家分享。

    1、场景描述

    下面是程序运行时的截图。本程序模拟这样的一个场景:有两组相互独立的数据需要逐条获取和显示,当点击Start按钮,以异步的方式从存储介质中逐条获取数据,并将获取的数据追加到对应的ListBox中,ProgressBar真实反映以获取的数据条数和总记录条数的百分比,同时,当前获取的条数也会在下方的Label上随着操作的继续而动态变化。此外通过点击Stop按钮,可以中止掉当前的操作。当操作被中止后,ProgressBar和Label反映中止的那一刻的状态。

     

    2、代码实现

    由于界面上两个互不干扰、相互独立的操作,所以分别创建了BackgroundWorker组件来负责。

     

    将BackgroundWorker的WorkerReportsProgress和WorkerSupportsCancellation设为true。

     

    我们假设获取的记录数固定,我们为此定义一个常量:

    private static int MaxRecords = 100;

    下面是Start按钮的Click event handler:

    当Start按钮被点击后,RunWorkerAsync方法被掉调用,我们定义的常量
    (MaxRecords )当作参数被掺入。随后,将会触发其DoWork事件,Dowork event handler处理代码如下:

    复制代码
     1 private void btnStart_Click(object sender, EventArgs e)
     2         {
     3             if (this.backgroundWorker1.IsBusy)
     4             {
     5                 return;
     6             }
     7 
     8             this.listBox1.Items.Clear();
     9             this.backgroundWorker1.RunWorkerAsync(maxRecords);
    10             this.btnStart.Enabled = false;
    11             this.btnCancle.Enabled = true;
    12         }
    复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
            {
                try
                {
                    e.Result=this.RetrieveData(this.backgroundWorker1,e);
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    throw;
                }
            }

    调用RetrieveData方法逐条获取数据。注意该方法的两个参数:

    BackgroundWorker和DoWorkEventArgs 对象,返回值是返回数据的数量。由于在Start_Click中,我们将常量MaxRecords 作为参数传入了BackgroundWorker的RunWorkerAsync方法, 此时的e.Argument = MaxRecords。之所以要将这两个参数传入RetrieveData()方法,是因为该方法是为两个BackgroundWorker服务的,需要通过参数来区别当前是哪个BackgroundWorker。我们再来看看RetrieveData方法的定义:

    复制代码
     1  private int RetrieveData(BackgroundWorker backgroundWorker, DoWorkEventArgs e)
     2         {
     3             int SumRecords = (int)e.Argument;
     4             int pecent = 0;
     5             for (int i = 0; i < SumRecords; i++)
     6             {
     7                 if (backgroundWorker.CancellationPending)
     8                     return i;
     9                 pecent = (int)((double)i / (double)SumRecords * 100);
    10                 backgroundWorker.ReportProgress(pecent, new KeyValuePair<int, string>(i, Guid.NewGuid().ToString()));
    11                 Thread.Sleep(1000);
    12                     
    13             }
    14             return SumRecords;
    15         }
    复制代码

    通过e.Argument,获得最大数据获取量之后,进行一个for循环,在每次迭代中,如何worker.CancellationPending==true,代表异步操作被显示取消,则直接返回;否则,调用BackgroundWorker的ReportProgress方法。ReportProgress具有两个重载:

    • public void ReportProgress(int percentProgress);
    • public void ReportProgress(int percentProgress, object userState);

    percentProgress代表当前进度,从0-100。userState便于传入一些额外的参数。在界面上,由于数据的当前数量需要实时地显示,而记录也是现取现加(取出一条就在ListBox上追加)。所以制定一个KeyValuePair<int,string>对象作为第二个参数。其中Key为当前记录数,Value是一个Guid,代表取出的数据。

    ReportProgress的调用将会导致ProgressChanged事件被触发。ProgressChanged event handler用于显示当前进度、当前记录数量和显示获取的纪录:

    复制代码
     1 private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
     2         {
     3             KeyValuePair<int, string> record = (KeyValuePair<int, string>)e.UserState;
     4 
     5             this.lblProcess.Text =
     6 
     7                  string.Format("There are {0} records retrieved!", record.Key);
     8 
     9 
    10 
    11             this.progressBar1.Value = e.ProgressPercentage;
    12 
    13             this.listBox1.Items.Add(record.Value);
    14         }
    复制代码

    注:这些操作需要操作UI上的控件,只能在Main Thread中进行。如何在

    RetrieveData方法进行的话,由于该方式是一个异步方法,是会抛出异常的。

    由于操作的时间可能无法预知,在长时间不能完全获取数据的情况下,用户可以需要手工结束掉当前的操作。这个操作实现在Stop按钮的Click事件中:

    如何操作正常地结束,BackgroundWorker的RunWorkerCompleted会被触发,

    下面是RunWorkerCompleted

    复制代码
     1 private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
     2         {
     3             try
     4             {
     5 
     6                 this.lblProcess.Text = string.Format("Total records: {0}", e.Result);
     7 
     8                 this.btnStart.Enabled = true;
     9                 this.btnCancle.Enabled = false;
    10 
    11             }
    12 
    13             catch (TargetInvocationException ex)
    14             {
    15 
    16                 MessageBox.Show(ex.InnerException.GetType().ToString());
    17 
    18             }
    19         }
    复制代码

    event handler的定义:

    上面介绍的是功能的实现.

     
     
  • 相关阅读:
    POJ 1811 Prime Test 素性测试 分解素因子
    sysbench的安装与使用
    电脑中已有VS2005和VS2010安装.NET3.5失败的解决方案
    I.MX6 show battery states in commandLine
    RPi 2B Raspbian system install
    I.MX6 bq27441 driver porting
    I.MX6 隐藏电池图标
    I.MX6 Power off register hacking
    I.MX6 Goodix GT9xx touchscreen driver porting
    busybox filesystem httpd php-5.5.31 sqlite3 webserver
  • 原文地址:https://www.cnblogs.com/amylis_chen/p/3436257.html
Copyright © 2011-2022 走看看