ProgressForm:一个与 BackgroundWorker 链接的简单表单






4.78/5 (34投票s)
ProgressForm 在加载时自动启动一个 BackgroundWorker,并提供进度条和取消按钮。
引言
这是我在 CodeProject 上的第一篇文章,希望您觉得它有用。
当我开发软件时,我经常需要要求用户等待一个长时间的操作完成,并且通常允许他/她取消它。无论我在该操作中做什么(它可以是下载文件、将大文件保存到磁盘等),我总是需要相同的东西
- 我希望用户通过模态对话框等待操作完成
- 我希望用户能看到其进度
- 我希望用户能够取消它
由于我没有在网上找到用于此目的的“即用型”表单(也许我没有搜索好?),我决定编写自己的表单。
背景
BackgroundWorker
类包含我们需要完成此操作的所有内容。我们只需要在它周围提供一个对话框即可。
使用代码
ProgressForm
已经包含一个 BackgroundWorker
,您只需要提供一个执行工作的 方法即可。
ProgressForm form = new ProgressForm();
form.DoWork += new ProgressForm.DoWorkEventHandler(form_DoWork);
//if you want to provide an argument to your background worker
form.Argument = something;
要启动 BackgroundWorker
,只需调用 ShowDialog
。返回值将取决于 worker 的完成方式
DialogResult result = form.ShowDialog();
if (result == DialogResult.Cancel)
{
//the user clicked cancel
}
else if (result == DialogResult.Abort)
{
//an unhandled exception occured in user function
//you may get the exception information:
MessageBox.Show(form.Result.Error.Message);
}
else if (result == DialogResult.OK)
{
//the background worker finished normally
//the result of the background worker is stored in form.Result
}
最后,worker 方法将如下所示
void form_DoWork(ProgressForm sender, DoWorkEventArgs e)
{
//get the provided argument as usual
object myArgument = e.Argument;
//do something long...
for (int i = 0; i < 100; i++)
{
//notify progress to the form
sender.SetProgress(i, "Step " + i.ToString() + " / 100...");
//...
//check if the user clicked cancel
if (sender.CancellationPending)
{
e.Cancel = true;
return;
}
}
}
如果您只想更改进度条或仅更改进度文本,SetProgress
有几个重载
public void SetProgress(string status);
public void SetProgress(int percent);
public void SetProgress(int percent, string status);
最后可自定义的内容:有两个预定义的字符串:CancellingText
和 DefaultStatusText
。CancellingText
是用户单击“取消”时将显示的文本。
工作原理
ProgressForm
只是嵌入一个 BackgroundWorker
并包装其主要功能。
首先,我设计了一个如图所示的表单。然后我添加了带有主要事件处理程序的 BackgroundWorker
public partial class ProgressForm : Form
{
public ProgressForm()
{
InitializeComponent();
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(
worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
worker_RunWorkerCompleted);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
BackgroundWorker worker;
}
现在我们必须向用户公开 DoWork
事件。我添加了一个新的委托,以便我们可以轻松访问表单成员
public delegate void DoWorkEventHandler(ProgressForm sender, DoWorkEventArgs e);
public event DoWorkEventHandler DoWork;
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//the background worker started
//let's call the user's event handler
if (DoWork != null)
DoWork(this, e);
}
好的,我们有了我们的 worker 和用户的事件。现在我们希望 worker 在显示表单后立即启动。让我们在 Load
事件中执行此操作
void ProgressForm_Load(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
现在,让我们编写一个方法来通知进度,并在我们的 ProgressChanged
事件处理程序中添加代码
public void SetProgress(int percent, string status)
{
worker.ReportProgress(percent, status);
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage >= progressBar.Minimum &&
e.ProgressPercentage <= progressBar.Maximum)
{
progressBar.Value = e.ProgressPercentage;
}
if (e.UserState != null)
labelStatus.Text = e.UserState.ToString();
}
我们快完成了。现在我们只需要处理“取消”按钮
void buttonCancel_Click(object sender, EventArgs e)
{
//notify the worker we want to cancel
worker.CancelAsync();
//disable the cancel button and change the status text
buttonCancel.Enabled = false;
labelStatus.Text = "Cancelling..."
}
最后一件事:我们希望在 worker 完成后自动关闭表单,并且由于我们的 worker 将通过 ShowDialog
方法启动,如果它可以直接返回结果会很好
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//ShowDialog return value will inform whether the worker finished properly or not
if (e.Error != null)
DialogResult = DialogResult.Abort;
else if (e.Cancelled)
DialogResult = DialogResult.Cancel;
else
DialogResult = DialogResult.OK;
//close the form
Close();
}
主要工作完成了!我添加了一些其他内容,例如状态的预定义字符串、防止在取消挂起时更改状态,或者将参数传递给 worker 函数。
您将在此处找到该类的完整源代码
/// <summary>
/// Simple progress form.
/// </summary>
public partial class ProgressForm : Form
{
/// <summary>
/// Gets the progress bar so it is possible to customize it
/// before displaying the form.
/// Do not use it directly from the background worker function!
/// </summary>
public ProgressBar ProgressBar { get { return progressBar; } }
/// <summary>
/// Will be passed to the background worker.
/// </summary>
public object Argument { get; set; }
/// <summary>
/// Background worker's result.
/// You may also check ShowDialog return value
/// to know how the background worker finished.
/// </summary>
public RunWorkerCompletedEventArgs Result { get; private set; }
/// <summary>
/// True if the user clicked the Cancel button
/// and the background worker is still running.
/// </summary>
public bool CancellationPending
{
get { return worker.CancellationPending; }
}
/// <summary>
/// Text displayed once the Cancel button is clicked.
/// </summary>
public string CancellingText { get; set; }
/// <summary>
/// Default status text.
/// </summary>
public string DefaultStatusText { get; set; }
/// <summary>
/// Delegate for the DoWork event.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">Contains the event data.</param>
public delegate void DoWorkEventHandler(ProgressForm sender, DoWorkEventArgs e);
/// <summary>
/// Occurs when the background worker starts.
/// </summary>
public event DoWorkEventHandler DoWork;
/// <summary>
/// Constructor.
/// </summary>
public ProgressForm()
{
InitializeComponent();
DefaultStatusText = "Please wait...";
CancellingText = "Cancelling operation...";
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
worker_RunWorkerCompleted);
}
/// <summary>
/// Changes the status text only.
/// </summary>
/// <param name="status">New status text.</param>
public void SetProgress(string status)
{
//do not update the text if it didn't change
//or if a cancellation request is pending
if (status != lastStatus && !worker.CancellationPending)
{
lastStatus = status;
worker.ReportProgress(progressBar.Minimum - 1, status);
}
}
/// <summary>
/// Changes the progress bar value only.
/// </summary>
/// <param name="percent">New value for the progress bar.</param>
public void SetProgress(int percent)
{
//do not update the progress bar if the value didn't change
if (percent != lastPercent)
{
lastPercent = percent;
worker.ReportProgress(percent);
}
}
/// <summary>
/// Changes both progress bar value and status text.
/// </summary>
/// <param name="percent">New value for the progress bar.</param>
/// <param name="status">New status text.</param>
public void SetProgress(int percent, string status)
{
//update the form is at least one of the values need to be updated
if (percent != lastPercent || (status != lastStatus && !worker.CancellationPending))
{
lastPercent = percent;
lastStatus = status;
worker.ReportProgress(percent, status);
}
}
private void ProgressForm_Load(object sender, EventArgs e)
{
//reset to defaults just in case the user wants to reuse the form
Result = null;
buttonCancel.Enabled = true;
progressBar.Value = progressBar.Minimum;
labelStatus.Text = DefaultStatusText;
lastStatus = DefaultStatusText;
lastPercent = progressBar.Minimum;
//start the background worker as soon as the form is loaded
worker.RunWorkerAsync(Argument);
}
private void buttonCancel_Click(object sender, EventArgs e)
{
//notify the background worker we want to cancel
worker.CancelAsync();
//disable the cancel button and change the status text
buttonCancel.Enabled = false;
labelStatus.Text = CancellingText;
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//the background worker started
//let's call the user's event handler
if (DoWork != null)
DoWork(this, e);
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//make sure the new value is valid for the progress bar and update it
if (e.ProgressPercentage >= progressBar.Minimum &&
e.ProgressPercentage <= progressBar.Maximum)
{
progressBar.Value = e.ProgressPercentage;
}
//do not update the text if a cancellation request is pending
if (e.UserState != null && !worker.CancellationPending)
labelStatus.Text = e.UserState.ToString();
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//the background worker completed
//keep the resul and close the form
Result = e;
if (e.Error != null)
DialogResult = DialogResult.Abort;
else if (e.Cancelled)
DialogResult = DialogResult.Cancel;
else
DialogResult = DialogResult.OK;
Close();
}
BackgroundWorker worker;
int lastPercent;
string lastStatus;
}
结论
此表单非常简单,但由于我经常使用它,我认为它对你们中的一些人会很有用。
历史
- 修订版 2:添加了“工作原理”部分。
- 修订版 3:再次上传了 SampleApplication.zip。
- 修订版 4:添加了
ProgressBar
属性并更改了SetProgress
函数,以便它们仅在需要时调用ReportProgress
。