想法大概是:一個 Timer 定期檢查、一個 ProgressBar 顯示進度、一個 backgroundworker 避免影響使用者、一個 NotifyIcon 可以出現桌面的右下角
程式寫法大致上是:
新增一個 Form,在上面放一個 ProgressBar
然後拉一個 Timer
一個 BackgroundWorker,並設定 WorkerReportsProgress = true,才可以修改 ProgressBar 的進度 然後在初始化後設定 form 的位置
在 BackgroundWorker (Name 設為 bw) 的事件上分別寫上
- 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);
- }
- 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);
- }
再來在 form load 事件內啟用 timer1
- void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
- {
- this.WindowState = FormWindowState.Minimized; // 隱藏視窗
- timer1.Enabled = true; // 工作完成了,再次啟用 timer1
- }
後來看到另一篇文章寫到,原來也可以在 BackgroundWorker_Completed 事件中,再次執行耗時的工作即可,減少使用一個 timer 元件,
- 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);
- }
把timer1_Tick()事件用另一個function 包起來,然後稍微修改一下
然後在 Form1() 建構式中直接執行上述的 function
- void PrepareToRunWorkerAsync()
- {
- // 如果背景程式忙碌中,就停止這次的背景
- if (bw.IsBusy)
- return;
- // 這邊可以收集要的參數
- // 若收集參數也很耗時,則可以另開一個backgroundworker來做,然後在backgroundworker_completed事件內執行主要的忙碌工作
- // 或是將收集參數的工作也被到bw裡面,但此時可能不單純只顯示progressbar,可能還需要顯示進度的內容說明
- // 然後呼叫backgroundworker 執行
- bw.RunWorkerAsync(myParameter);
- }
修改一下 BackgroundWorker_Completed 事件
- 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();
- }
當然上述做法需再搭配一個啟動耗時工作的事件,不然一但取消後,就只有重開執行檔一途了
- void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
- {
- this.WindowState = FormWindowState.Minimized; // 隱藏視窗
- // 若使用者沒有下取消命令,則在Completed 事件下再次執行耗時的工作
- if (e.Cancelled == false)
- PrepareToRunWorkerAsync();
- }
參考:C# 使用 BackgroundWorker 背景執行
- void buttonStart(object sender, EventArgs e)
- {
- PrepareToRunWorkerAsync();
- }