想法大概是:一個 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 背景執行