还记得BackgroundWorker吗?

今天一早醒来,猛然发现,很久没写博客了,算一算,其实应该不到一个月,可总觉得寰宇一个月,世上已千年。是啊,于是内心萌发了一股冲动的力量,今天该写点什么,可是,一时想不出来。

打开电脑,突然想起来,昨天下午,有位仁兄和我讨论过一个事,不妨拿来说说。

她说她的牛逼程序要处理一堆东东,要弄个进度条作提示,不过进度条是在另一个窗口中的,她的想法是,在开始处理数据时弹出进度对话框,实时显示处理进度,当处理完成后关闭对话框。乍看起来其实不难,不过她遇到了以下问题,故在群里提问。

1、模态对话框的问题。

这问题好办,一般来说,要长时间来处理数据,应该考虑后台异步操作,用砖家的话讲就是多线程。不过她在显示窗口时调用了ShowDialog方法,这样代码会一直停在那里,直到窗口关闭。

如果是异步操作,通常来说,在启动后台任务后会马上返回,这么一来,只要把代码的顺序调一下就可以解决这问题,先启动后台任务,再调用ShowDialog方法,这样一来,就算代码停在ShowDialog那里也不会影响后台任务的执行

2、如何控制其他窗口中控件。

可以在在窗口类中定义公共方法来对控件进行某些操作,之后在其他地方就可以通过这些公共方法来调控。如果是跨线程调用,应当考虑使用委托或事件来调用。不然你学了委托和事件干吗呢?

另一种方法就是直接把进度窗口中的ProgressBar控件声明为public,这样其他类就可以轻松访问了。

3、后台任务如何更简单。

方法是灵活的,有很多种。最简单的是利用.NET 4.5和C# 5.0 中的新特性,这种方法肯定是最简单的。第二种则是使用.NET 4 中新加的Task类来调度线程;比较传统,在2.0时代用得最多的方法是直接用Thread类。

但是,有一个组件是专为后台任务而开放的,忘了没有?——BackgroundWorker,也许有些朋友是忘了这个组件了。现在很多人就是这样,有了小三就忘了结发妻子,我们众多码农也是这样,常常会忘本。

既有后台任务,又要报告进度,用BackgroundWorker不是更合适吗?于是我问了她两遍:“还记得吗?” 果然不出我所料,记不起来了,呵呵。

说了那么多,估计需要实例才能解决问题,好吧,上菜。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace MyApp
{
    public partial class Form1 : Form
    {
        static string SaveDir = string.Empty;
        private FormProgress fpro = null;
        public Form1()
        {
            InitializeComponent();
            fpro = new FormProgress();
        }


        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            int fileCount = Convert.ToInt32(e.Argument);
            Random rand = new Random();
            byte[] buffer = new byte[2048];
            for (int i = 0; i < fileCount; i++)
            {
                try
                {
                    string fileName = Path.Combine(SaveDir, i.ToString() + ".tmp");
                    using (var stream = File.Create(fileName))
                    {
                        int n = 0;
                        int maxByte = 8 * 1024 * 1024;
                        while (n < maxByte)
                        {
                            rand.NextBytes(buffer);
                            stream.Write(buffer, 0, buffer.Length);
                            n += buffer.Length;
                        }
                    }
                }
                catch
                {
                    continue;
                }
                finally
                {
                    // 报告进度
                    this.backgroundWorker1.ReportProgress(i + 1);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (Directory.Exists(SaveDir) == false)
            {
                return;
            }
            button1.Enabled = false;
            int cout = Convert.ToInt32(this.nmbud.Value);
            this.fpro.progressBar1.Minimum = 0;
            this.fpro.progressBar1.Maximum = cout;
            this.fpro.progressBar1.Value = this.fpro.progressBar1.Minimum;
            this.backgroundWorker1.RunWorkerAsync(cout);
            // 在开始异步操作后ShowDialog
            // 这样即使代码停在那里也不会影响后台任务的执行
            fpro.ShowDialog(this);
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            int val = e.ProgressPercentage;
            this.fpro.lblText.Text = string.Format("已生成{0}个文件。", val);
            this.fpro.progressBar1.Value = val;
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            button1.Enabled = true;
            fpro.Hide();
            MessageBox.Show("操作完成。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void btnBrowsFd_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fd = new FolderBrowserDialog();
            fd.RootFolder = Environment.SpecialFolder.Desktop;
            if (DialogResult.OK == fd.ShowDialog())
            {
                SaveDir = fd.SelectedPath;
            }
            fd.Dispose();
        }
    }
}


FormProgress是放置了ProgressBar的另一个窗体,backgroundWorker1由设计器生成,在设计窗口,可以直接从工具箱中把BackgroundWorker拖到设计器窗口上。

代码没有难度,相信小姑娘能看懂的。下面我们总结一下BackgroundWorker的用法,相信很多书上都有介绍,哪怕是抄MSDN的书。

1、如果希望报告进度,WorkerReportsProgress属性必须为true,否则报告进度时会异常。如果允许通过调用CancelAsync方法取消任务,WorkerSupportsCancellation属性要为true。

2、处理DoWork事件,后台要处理的任务代码就写在该事件的处理方法中。

3、ProgressChanged事件,当调用ReportProgress方法报告进度后,会引发该事件,处理该事件实时更新进度条的显示。

4、当任务执行完成后会引发RunWorkerCompleted事件,如果后台任务需要返回结果,可从事件参数RunWorkerCompletedEventArgs.Result属性中取得结果。那么,这个结果是怎么设置的呢?不妨再看看前面的DoWork事件,它的事件参数DoWorkEventArgs的Result属性,当我们的任务执行完成时,把结果赋给该属性,随后引发RunWorkerCompleted事件时,会把结果传递到RunWorkerCompletedEventArgs.Result属性。

现在,我们可以看看最终的效果,是否达到预期要求。

猜你喜欢

转载自blog.csdn.net/tcjiaan/article/details/9149573
今日推荐