介紹
這是我在CodeProject上的第一篇文章。我希望對你有用
當我開發軟件的時候。我通常因為一個很耗時是任務需要完成。而請求讓用戶等待,並且通過也允許用戶取消。不論我做何種操作(比如下載文件。保存大文件等等)。我都需要做下面幾件事:
- 通過一個模態對話框來讓用戶等待操作完成
- 能讓用戶看到進度。
- 能讓用戶隨時取消。
我搜了好久也沒找到拿來就能用的窗體控件,也許是我沒找到。於是我自己寫。。
圖1
背景
BackgroundWorker 類包含了我需要完成任務的所有東西。我只需要給他提供一個對話框。
使用代碼
ProgressForm 包含了一個BackgroundWorker ,你要做的僅僅就是提供了一個完成工作的方法。
ProgressForm form = new ProgressForm(); form.DoWork += new ProgressForm.DoWorkEventHandler(form_DoWork); //如果想為后台任務提供參數的話 form.Argument = something;
為了開始BackgroundWorker,只需要調用ShowDialog 方法。返回值則取決於任務是怎么完成的。
DialogResult result = form.ShowDialog(); if (result == DialogResult.Cancel) { //用戶點擊了取消 } else if (result == DialogResult.Abort) { /未處理的異常拋出 //你可以得到異常信息 MessageBox.Show(form.Result.Error.Message); } else if (result == DialogResult.OK) { //正常完成 //結果存儲在 form.Result里 }
最后。任務方法看起來是這樣的。
void form_DoWork(ProgressForm sender, DoWorkEventArgs e) { //得到參數 object myArgument = e.Argument; //做一些耗時的任務... for (int i = 0; i < 100; i++) { //通知進度 sender.SetProgress(i, "Step " + i.ToString() + " / 100..."); //... //檢查是否點擊了取消 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) { //后台任務開始 //調用用戶的事件處理程序 if (DoWork != null) DoWork(this, e); }
好。我們已經有了任務和事件。先愛。我們希望當窗體顯示的時候。后台任務盡可能開始。我們在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) { //通過worker我們要取消 worker.CancelAsync(); //使取消按鈕不可用,改變狀態文本 buttonCancel.Enabled = false; labelStatus.Text = "Cancelling..." } 最后一件事是我們想要當worker完成的時候自動關閉窗體,因為我們的worker通過ShowDialog 方法啟動。如果直接接收返回結果會很好 void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //ShowDialog返回值會指示worker是不是正確完成了 if (e.Error != null) DialogResult = DialogResult.Abort; else if (e.Cancelled) DialogResult = DialogResult.Cancel; else DialogResult = DialogResult.OK; //關閉窗體 Close(); }
主要的工作就完成了。我添加了一些預定義的字符串啊。如果正在取消。保護狀態不會改變。還有傳遞參數啊。
完整的代碼如下:
/// <summary> /// 簡單的進度窗體 /// </summary> public partial class ProgressForm : Form { /// <summary> /// 獲得進度條以對他自定義 /// 在顯示窗體之前. /// 不要在后台任務中直接使用 ! /// </summary> public ProgressBar ProgressBar { get { return progressBar; } } /// <summary> /// 傳遞給后台任務的參數. /// </summary> public object Argument { get; set; } /// <summary> /// 后台任務的結果. ///也可以檢查ShowDialog返回值 ///來看看是不是正確完成了. /// </summary> public RunWorkerCompletedEventArgs Result { get; private set; } /// <summary> /// 如果點擊了取消按鈕則為true ///后台任務還在執行 /// </summary> public bool CancellationPending { get { return worker.CancellationPending; } } /// <summary> /// 取消按鈕被點擊之后的顯示文本 /// </summary> public string CancellingText { get; set; } /// <summary> /// 缺省狀態文本. /// </summary> public string DefaultStatusText { get; set; } /// <summary> /// DoWork事件的委托. /// </summary> /// <param name="sender">T事件源.</param> /// <param name="e">包含事件數據.</param> public delegate void DoWorkEventHandler(ProgressForm sender, DoWorkEventArgs e); /// <summary> /// 當后台任務開始的時候發生. /// </summary> public event DoWorkEventHandler DoWork; /// <summary> /// 構造函數. /// </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> /// 改變狀態文本. /// </summary> /// <param name="status">新狀態文本.</param> public void SetProgress(string status) { //如果沒有改變 //或者取消請求還在處理就不改變狀態文本 if (status != lastStatus && !worker.CancellationPending) { lastStatus = status; worker.ReportProgress(progressBar.Minimum - 1, status); } } /// <summary> ///改變進度條的值 /// </summary> /// <param name="percent">新值.</param> public void SetProgress(int percent) { //如果值沒有改變就不要更新進度條 if (percent != lastPercent) { lastPercent = percent; worker.ReportProgress(percent); } } /// <summary> ///改變進度條值和文本. /// </summary> /// <param name="percent">N新值.</param> /// <param name="status">新文本.</param> public void SetProgress(int percent, string status) { //如果至少一個改變就調用 if (percent != lastPercent || (status != lastStatus && !worker.CancellationPending)) { lastPercent = percent; lastStatus = status; worker.ReportProgress(percent, status); } } private void ProgressForm_Load(object sender, EventArgs e) { //重用窗體,恢復缺省值 Result = null; buttonCancel.Enabled = true; progressBar.Value = progressBar.Minimum; labelStatus.Text = DefaultStatusText; lastStatus = DefaultStatusText; lastPercent = progressBar.Minimum; //窗體已載入就開始后台任務 worker.RunWorkerAsync(Argument); } private void buttonCancel_Click(object sender, EventArgs e) { //通知后台任務,我們要取消 worker.CancelAsync(); //取消按鈕不可用,改變文本 buttonCancel.Enabled = false; labelStatus.Text = CancellingText; } void worker_DoWork(object sender, DoWorkEventArgs e) { //后台任務開始 //調用用戶的處理程序 if (DoWork != null) DoWork(this, e); } void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { //確保新值可用並更新 if (e.ProgressPercentage >= progressBar.Minimum && e.ProgressPercentage <= progressBar.Maximum) { progressBar.Value = e.ProgressPercentage; } //如果取消請求正在處理就不要更新 if (e.UserState != null && !worker.CancellationPending) labelStatus.Text = e.UserState.ToString(); } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { //后台任務完成 //保持結果,關閉窗體 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; }
結論
窗體簡單,我通常用。希望對你們也有用
Demo下載
許可
本文,包括源代碼和文件在CPOL下授權。
原文地址:ProgressForm-A-simple-form-linked-to-a-BackgroundW
著作權聲明:本文由http://leaver.me 翻譯,歡迎轉載分享。請尊重作者勞動,轉載時保留該聲明和作者博客鏈接,謝謝!