想法大概是:一個 Timer 定期檢查、一個 ProgressBar 顯示進度、一個 backgroundworker 避免影響使用者、一個 NotifyIcon 可以出現桌面的右下角
程式寫法大致上是:
新增一個 Form,在上面放一個 ProgressBar
然後拉一個 Timer
一個 BackgroundWorker,並設定 WorkerReportsProgress = true,才可以修改 ProgressBar 的進度 然後在初始化後設定 form 的位置
public Form1() { InitializeComponent(); // 縮小視窗,以觸發 notifyIcon (不直接觸發是為了保留之後若要點擊縮小的 Icon 時可以恢復視窗) this.WindowState = FormWindowState.Minimized; this.TopLevel = true; this.screenWidth = Screen.PrimaryScreen.Bounds.Width; this.screenHeight = Screen.PrimaryScreen.Bounds.Height; //this.Location = new Point(screenWidth - Width - 5, screenHeight); //設定視窗位置在右下角最高可以顯示的位置, 工具列上方 this.stopHightLocation = Screen.PrimaryScreen.Bounds.Height - (Screen.PrimaryScreen.Bounds.Height - Screen.PrimaryScreen.WorkingArea.Height + this.Height); this.Location = new Point(screenWidth - Width - 5, stopHightLocation); }在 BackgroundWorker (Name 設為 bw) 的事件上分別寫上
void bw_DoWork(object sender, DoWorkEventArgs e) { object myParameter = e.Argument; // myParameter 是帶過來要用的參數 // 開始執行要很久的動作 ...(略) // 結束 }
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { // 把視窗叫起來讓使用者看到進度條 if (this.WindowState == FormWindowState.Minimized) { this.Show(); // 顯示視窗 this.WindowState = FormWindowState.Normal; } // 修改進度條的進度 progressBar1.Value = e.ProgressPercentage; // 以文字的方式顯示進度條的百分比 label2.Text = string.Format("{0:f0}%", (progressBar1.Value / Convert.ToDouble(progressBar1.Maximum)) * 100); }
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.WindowState = FormWindowState.Minimized; // 隱藏視窗 timer1.Enabled = true; // 工作完成了,再次啟用 timer1 }再來在 form load 事件內啟用 timer1
private void timer1_Tick(object sender, EventArgs e) { // 如果背景程式忙碌中,就停止這次的背景 if (bw.IsBusy) return; timer1.Enabled = false; // 然後將 timer 停用 // 這邊可以收集要的參數 // 若收集參數也很耗時,則可以另開一個backgroundworker來做,然後在backgroundworker_completed事件內執行主要的忙碌工作 // 或是將收集參數的工作也被到bw裡面,但此時可能不單純只顯示progressbar,可能還需要顯示進度的內容說明 // 然後呼叫backgroundworker 執行 (可以加判斷是否真的需要呼叫背景程式,減少觸發次數) if (myParameter != null) bw.RunWorkerAsync(myParameter); }後來看到另一篇文章寫到,原來也可以在 BackgroundWorker_Completed 事件中,再次執行耗時的工作即可,減少使用一個 timer 元件,
把timer1_Tick()事件用另一個function 包起來,然後稍微修改一下
void PrepareToRunWorkerAsync() { // 如果背景程式忙碌中,就停止這次的背景 if (bw.IsBusy) return; // 這邊可以收集要的參數 // 若收集參數也很耗時,則可以另開一個backgroundworker來做,然後在backgroundworker_completed事件內執行主要的忙碌工作 // 或是將收集參數的工作也被到bw裡面,但此時可能不單純只顯示progressbar,可能還需要顯示進度的內容說明 // 然後呼叫backgroundworker 執行 bw.RunWorkerAsync(myParameter); }然後在 Form1() 建構式中直接執行上述的 function
public Form1() { InitializeComponent(); // 縮小視窗,以觸發 notifyIcon (不直接觸發是為了保留之後若要點擊縮小的 Icon 時可以恢復視窗) this.WindowState = FormWindowState.Minimized; this.TopLevel = true; this.screenWidth = Screen.PrimaryScreen.Bounds.Width; this.screenHeight = Screen.PrimaryScreen.Bounds.Height; //this.Location = new Point(screenWidth - Width - 5, screenHeight); //設定視窗位置在右下角最高可以顯示的位置, 工具列上方 this.stopHightLocation = Screen.PrimaryScreen.Bounds.Height - (Screen.PrimaryScreen.Bounds.Height - Screen.PrimaryScreen.WorkingArea.Height + this.Height); this.Location = new Point(screenWidth - Width - 5, stopHightLocation); PrepareToRunWorkerAsync(); }修改一下 BackgroundWorker_Completed 事件
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.WindowState = FormWindowState.Minimized; // 隱藏視窗 // 若使用者沒有下取消命令,則在Completed 事件下再次執行耗時的工作 if (e.Cancelled == false) PrepareToRunWorkerAsync(); }當然上述做法需再搭配一個啟動耗時工作的事件,不然一但取消後,就只有重開執行檔一途了
void buttonStart(object sender, EventArgs e) { PrepareToRunWorkerAsync(); }參考:C# 使用 BackgroundWorker 背景執行