在.NET 2.0应用程序中使用BackgroundWorker组件 实现进度条

来源:互联网 发布:vs上mpi编程 编辑:程序博客网 时间:2024/04/27 17:59
原作者: Michael Livshitz    译者: lilyonwin...    发表时间:08/03/2007    原文链接

作者 Michael Livshitz 2007年7月3日

BackgroundWorker可以让窗体异步地完成一个操作。在我们需要执行诸如『数据库事务』或者『图片下载』之类的操作时,这个功能非常有用。此时,我们的可以让用户界面停止响应(或者隐藏起来直到操作结束)。在这篇文章中,我会一步一步教会你如何在.NET 2.0程序中使用BackgroundWorker组件以便处理较耗时的操作。示例程序使用C#编写。

与往常一样,我们创建一个测试工程,取名为"TestBGW",使之只包含一个窗体("FormBGW"):

Image1.jpg

图1.

我们将使用BackgroundWorker完成一些数据库的事务操作(比如,获取一些DataTable)。首先拖一个BackgroundWorker组件到我们窗体上。
Image2.jpg
图2.

我们将用DataTable来设置DataGridView1的DataSource属性。我们还应该刷新我们的用户界面,并告诉用户:所有的操作已经全部“OK”,他/她不用再操心啦。因此,我们还要需要一个StatusStrip和一个Timer.

Image3.jpg
图3.

为了让用户看到我们的处理过程正在运行之中,我们将用到toolStripProgressBar1:
Image4.jpg
图4.

用toolStripStatusLabel1和toolStripStatusLabelTime是来向用户显示处理过程的状态和已经花费的时间。

我们窗体看上去是这样子的:

Image5.jpg
图5.

为了模拟数据库的事务操作,我们将用到 GetData.dll(当然你也可以连接到一个真实的数据库;这个模拟的目的只是为了测试而已)。出于这个目的,我们把GetData.dll添加到引用中,然后写因getDataTable方法:

private DataTable getDataTable(int Rows)
{
    GetData.GetDataHelp getData = new GetData.GetDataHelp();
    return (getData.getDataSetCities(Rows).Tables[0]);
}

我们调用RunWokerAsync方法开始我们的异步操作:

private void FormBGW_Activated(object sender, EventArgs e)
{
    backgroundWorker1.RunWorkerAsync();
}

BackGroundWorker组件有三个事件:
Image6.jpg
图6.

DoWork事件发生在RunWokerAsync方法被调用时。在这个事件的处理函数中,我们“完成”那些的耗时的工作(我们载入至少100000行数据以便使我们的处理过程变得“耗时”),并通知用户载入工作正在进行中,最后将我们的DataTable置为我们的异步操作的结果。

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    DataTable dt;
    toolStripStatusLabel1.Text = "Loading ... " + "Thanks for your patience";
    dt = getDataTable(1000000);
    e.Result = dt;
}

就我们面对的情况而言(我是指真实的情况,即从数据库获得一些DataTable的情况),我们没办法“插入”处理过程以“追踪”已获取的数据行数量(一行接一行)。当然,我们同样也没有办法知道,根据我们的请求最终可以从数据库获得多少数据行。所以呢,我们不会用DoWorkEventArgs(译注:应为BackgroudWorker的笔误)的ReportProgress 方法来触发ProgressChanged事件。我们将用Timer1,向用户显示处理过程正在进行中(我们将把 toolStripProgressBar1“增加”到最大程度,然后再从最短长度继续开始;就是说,我们会制造一些循环)。因此我们给 timer1_Tick加上下面的代码:

if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum)
{
    toolStripProgressBar1.Value = 0;
}

为了让用户知道加载已经花费了多长时间,我们还得给这个方法加上一点代码,比如这个样子:

string sTime =" ..." + ts.Minutes.ToString("00") +
   ":" + ts.Seconds.ToString("00") +
   ":" + ts.Milliseconds.ToString("000");
toolStripStatusLabelTime.Text = sTime;

其中ts指现在的时间(参见下面完整的代码)。

顺便说一句,如果仅仅为了测试ReportProgress如何工作,你可以给backgroundWorker1_DoWork方法加上下面的代码:

//-------to try percentage ...
int iMax = 100000;
for(int i = 0; i < iMax; i++)
{
    backgroundWorker1.ReportProgress((i * 100) / (iMax - 1));
}

然后把这些代码加入到backgroundWorker1_ProgressChanged :

privateProgressChangedEventArgs e)void backgroundWorker1_ProgressChanged(object sender,
{
    //-------to try percentage ...
    toolStripProgressBar1.Value = e.ProgressPercentage;
    toolStripStatusLabel1.Text = "Loading ... " +
        e.ProgressPercentage.ToString() + "%";
    //-------------------------
}

RunWorkerCompleted事件发生在后台操作完成的时候。此时我们将“关闭”所有的关于加载的提示信息,并且把通过使用事件的参数RunWorkerComletedEventArgs,把dataGridViewCites绑定到dataTable:

dataGridViewCities.DataSource = e.Result;

FormBGW.cs的全部代码如下:

using System; using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace TestBGW
{
    public partial class FormBGW : Form
    {
        public FormBGW()
        {
            InitializeComponent();
            ////to try percentage ...
            //backgroundWorker1.WorkerReportsProgress = true;
        }
        #region "forClass"
        DateTime startDate = DateTime.Now;
        #endregion
        private void FormBGW_Activated(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();
            timer1.Start();
        }
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            DataTable dt;
            toolStripStatusLabel1.Text = "Loading ... " +
                "Thanks for your patience";
            dt = getDataTable(1000000);
            ////-------to try percentage ...
            //int iMax = 100000;
            //for (int i = 0; i < iMax; i++)
            //{
            //    backgroundWorker1.ReportProgress
            //        ((i * 100) / (iMax - 1));
            //}
            ////-------------------------
            e.Result = dt;
            toolStripStatusLabel1.Text = "Please, wait ...";
        }
        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            ////-------to try percentage ...
            //toolStripProgressBar1.Value = e.ProgressPercentage;
            //toolStripStatusLabel1.Text = "Loading ... " +
            //    e.ProgressPercentage.ToString() + "%";
            ////-------------------------
        }
        private void backgroundWorker1_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e)
        {
            toolStripProgressBar1.Value = 100;
            dataGridViewCities.DataSource = e.Result;
            toolStripStatusLabel1.Text = "";
            toolStripProgressBar1.Value = 0;
            timer1.Stop();
            toolStripStatusLabelTime.Text = "";
        }
        private DataTable getDataTable(int Rows)
        {
            GetData.GetDataHelp getData = new GetData.GetDataHelp();
            return (getData.getDataSetCities(Rows).Tables[0]);
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            TimeSpan ts = DateTime.Now.Subtract(startDate);
            string sTime =" ..." + ts.Minutes.ToString("00") +
               ":" + ts.Seconds.ToString("00") +
               ":" + ts.Milliseconds.ToString("000");
            toolStripStatusLabelTime.Text = sTime;
            if (toolStripProgressBar1.Value ==
                toolStripProgressBar1.Maximum)
            {
                toolStripProgressBar1.Value = 0;
            }
            toolStripProgressBar1.PerformStep();
        }
    }
}

现在,如果我们运行我们的工程,我们会看到:

Image7.jpg
图7.

加载结束之后:

Image8.jpg
图8.

结论

我希望这篇文中可以帮助你在你的.NET 2.0程序中使用BakcgroundWorker组件以便执行一些耗时的操作。

祝好运与编程同在!

--------------

译者的话,这是我在译言翻译的第一篇。从我的角度讲,我是不赞同一个看不懂这篇文章原文的人做程序员的。不过我天生不爱放弃,既然接下了翻译任务,就完成了它吧。
··············································
附: 我将原来的调用的方法改成了 计算斐波纳西函数,全部代码如下:
 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace TestBGW
{
    public partial class FormBGW : Form
    {
        DateTime startDate = DateTime.Now;
        public FormBGW()
        {
            InitializeComponent();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            toolStripStatusLabel1.Text = "Loading ... " + "Thanks for your patience";

            e.Result = this.ComputeFibonacci(40);

            toolStripStatusLabel1.Text = "Please, wait ...";

 

        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {

        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            toolStripProgressBar1.Value = 100;

            toolStripStatusLabel1.Text = "";

            toolStripProgressBar1.Value = 0;

            timer1.Stop();

            toolStripStatusLabelTime.Text = "";

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            TimeSpan ts = DateTime.Now.Subtract(startDate);

            string sTime = "  ..." + ts.Minutes.ToString("00") +

               ":" + ts.Seconds.ToString("00") +

               ":" + ts.Milliseconds.ToString("000");

            toolStripStatusLabelTime.Text = sTime;

            if (toolStripProgressBar1.Value == toolStripProgressBar1.Maximum)
            {
                toolStripProgressBar1.Value = 0;
            }
            toolStripProgressBar1.PerformStep();

        }

        private void FormBGW_Activated(object sender, EventArgs e)
        {
            backgroundWorker1.RunWorkerAsync();//开始异步操作

            timer1.Start();
        }

        private long  ComputeFibonacci(int n)
        {
            // The parameter n must be >= 0 and <= 91.
            // Fib(n), with n > 91, overflows a long.
            if ((n < 0) || (n > 91))
            {
                throw new ArgumentException(
                    "value must be >= 0 and <= 91", "n");
            }

            long result = 0;

            if (n < 2)
            {
                result = 1;
            }
            else
            {
                result = ComputeFibonacci(n - 1) +
                         ComputeFibonacci(n - 2);
            }
            return result;
        }

    }
}


 
原创粉丝点击