我们使用C#.NET编写WinForm程序时,有时候为了实现在模态对话框中实时显示后台操作的进度,这个时候需要借助于多线程操作在子窗体中显示进度条状态,在父窗体中进行后台操作。你可以在Thread类中自己创建两个线程以完成这个操作,不过C#.NET提供了BackgroundWorker对象可以帮助我们非常方便地来实现这个过程。有关Backgroundworker对象的时候我在“C#遍历文件读取Word内容以及实用BackgroundWoker对象打造平滑进度条”一文中有过介绍,大家可以去看看。
这里是一个示例,其中展示了如何使用Backgroundworker对象在模态对话框中显示后台操作的实时进度条。
首先是主窗体代码:
1
using
System;
2
using
System.Collections.Generic;
3
using
System.ComponentModel;
4
using
System.Data;
5
using
System.Drawing;
6
using
System.Linq;
7
using
System.Text;
8
using
System.Windows.Forms;
9
using
System.Threading;
10
11
namespace
ModalProgressDialog
12
{
13
public
partial
class
Form1 : Form
14
{
15
protected
BackgroundWorker worker
=
new
BackgroundWorker();
16
protected
Form2 frm
=
new
Form2();
17
18
public
Form1()
19
{
20
worker.DoWork
+=
new
DoWorkEventHandler(worker_DoWork);
21
worker.ProgressChanged
+=
new
ProgressChangedEventHandler(worker_ProgressChanged);
22
worker.RunWorkerCompleted
+=
new
RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
23
24
InitializeComponent();
25
}
26
27
private
void
button1_Click(
object
sender, EventArgs e)
28
{
29
worker.WorkerReportsProgress
=
true
;
30
worker.RunWorkerAsync();
31
frm.ShowDialog();
32
}
33
34
void
worker_RunWorkerCompleted(
object
sender, RunWorkerCompletedEventArgs e)
35
{
36
frm.Close();
37
MessageBox.Show(
"
Done
"
);
38
}
39
40
void
worker_ProgressChanged(
object
sender, ProgressChangedEventArgs e)
41
{
42
frm.ProgressValue
=
e.ProgressPercentage;
43
}
44
45
void
worker_DoWork(
object
sender, DoWorkEventArgs e)
46
{
47
CountTheTime();
48
}
49
50
private
void
CountTheTime()
51
{
52
int
initialValue
=
100
;
53
for
(
int
count
=
0
; count
<
initialValue; count
=
count
+
2
)
54
{
55
Thread.Sleep(
1000
);
56
worker.ReportProgress(count);
57
}
58
}
59
}
60
}
主窗体中只有一个按钮,当被点击时,会由BackgroundWorker对象以异步的方式去执行一个假象的后台操作CountTheTime方法。CountTheTime方法从0到100以步长为2每隔1秒更新一下进度条状态,因此这个假象的后台操作大约会持续50秒左右的时间。当程序执行时,进度条指示窗口以模态对话框的形式被弹出,然后实时显示后台操作的进度。
BackgroundWorker对象有三个主要的事件:
DoWork - 当BackgroundWorker对象的多线程操作被执行时触发。
RunWokerCompleted - 当BackgroundWoker对象的多线程操作完成时触发。
ProgressChanged - 当BackgroundWorker对象的多线程操作状态改变时触发。
另外还有一个非常重要的属性WorkerReportsProgress - 如果想让BackgroundWorker对象以异步的方式报告线程实时进度,必须将该属性的值设为true。
BackgroundWorker对象的ReportProgress方法用于向主线程返回后台线程执行的实时进度。
下面是子窗体的代码:
1
using
System;
2
using
System.Collections.Generic;
3
using
System.ComponentModel;
4
using
System.Data;
5
using
System.Drawing;
6
using
System.Linq;
7
using
System.Text;
8
using
System.Windows.Forms;
9
10
namespace
ModalProgressDialog
11
{
12
public
partial
class
Form2 : Form
13
{
14
public
int
ProgressValue
15
{
16
get
{
return
this
.progressBar1.Value; }
17
set
{ progressBar1.Value
=
value; }
18
}
19
20
public
Form2()
21
{
22
InitializeComponent();
23
}
24
}
25
}
子窗体中放置了一个ProgressBar控件,对外可以通过ProgressValue属性来获取和修改进度条的当前值。同时,我们可以将子窗体的FormBorderStyle属性设为FixedDialog以使其看起来更像对话框,然后将MaximizeBox和MinimizeBox都设为false,将ControlBox属性设为false以隐藏窗体关闭按钮。在父窗体中,我们通过BackgroundWorker对象的RunWorkerAsync方法触发DoWork事件,此时CountTheTime()方法被执行。在CountTheTime()方法中,通过ReportProgress()方法从后台进程(父窗体)传递进度指示到主UI线程(子窗体)中,这样同时会触发ProgressChanged事件,然后我们在该事件中更新子窗体的进度条状态。下面是程序执行时的截图。
注意,使用BackgroundWorker时不能在工作线程中访问UI线程部分,即你不能在BackgroundWorker的事件和方法中操作UI,否则会抛跨线程操作无效的异常。常用的方法是在主窗体的构造函数中添加CheckForIllegalCrossThreadCalls = false;语句。或者使用Thread类创建一个单独的线程,然后使用Invoke方法。可以参考下面这些内容:
http://www.cnblogs.com/chorrysky/archive/2007/02/10/646891.html
http://blog.csdn.net/marklr/archive/2009/07/10/4338518.aspx
http://www.cnblogs.com/kingsky/archive/2009/02/18/1353322.html