本文通过实例介绍BackgroundWorker组件的使用方法。使用BackgroundWorker前需要添加命名空间System.ComponentModel。本实例模拟一个耗时操作,并在操作过程中报告工作进度。这种情况使用BackgroundWorker非常合适。下面是实例程序的主画面。
下面给出程序的完整代码。
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace BackgroundWorkerExp
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
BackgroundWorker bw;
public MainWindow()
{
InitializeComponent();
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
}
private void Button_Start_Click(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 100000; i++)
{
if (i % 1000 == 0)
{
bw.ReportProgress(i / 1000);
Thread.Sleep(50);
}
}
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Label_CurrentProgress.Content = "操作完成";
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Label_CurrentProgress.Content = e.ProgressPercentage + "%";
}
}
}
代码中在窗体被创建时,初始化BackgroundWorker,设置BackgroundWorker相关属性,注册BackgroundWorker相关事件,下面对代码中涉及到的属性和事件进行说明。
(1)WorkerSupportsCancellation属性:表示BackgroundWorker是否支持异步取消。当该属性设置为true时,可以调用异步方法CancelAsync来取消异步操作。
(2)WorkerReportsProgress属性:表示BackgroundWorker是否可以报告当前进度。当该属性设置为true时,可以调用ReportProgress方法来报告当前进度。
(3)DoWork事件:当调用RunWorkerAsync方法时,该事件被触发,在事件处理方法中执行耗时操作,此方法中的代码在子线程中执行,可看成使用Thread新起的一个线程。
(4)ProgressChanged事件:当调用ReportProgress方法时,该事件会被触发,在事件处理方法中可以更新画面显示当前执行进度,类似于使用BeginInvoke在子线程中刷新UI主画面的操作,这部分代码在主线程中执行。
(5)RunWorkerCompleted事件:当DoWork事件处理方法中所有代码执行完后触发该事件,该事件处理器中的代码也是在主线程中执行。
在开发中,不小心将调用RunWorkerAsync方法的代码放置在子线程的代码中。
private void Button_Start_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(()=>ThreadMethod());
t.Start();
}
void ThreadMethod()
{
bw.RunWorkerAsync();
}
使用上面代码会在下面的代码中出现错误“调用线程无法访问此对象,因为另一个线程拥有该对象”。
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.Label_CurrentProgress.Content = e.ProgressPercentage + "%";
}
原因极其明显,代码“this.Label_CurrentProgress.Content = e.ProgressPercentage + "%";”只有在主线程中执行才能正常工作,因为Label_CurrentProgres的拥有者是主线程而不是某个子线程,在子线程直接操作UI主线程中的对象是不允许的。