在寫 WinForm 應用程式的時候,只要遇到程式需要處理比較久的情況,介面就會卡住、按鈕無法點擊、視窗無法拖曳,甚至是應用程式沒有回應,要等到程式處理完才可以恢復。
為什麼會這樣呢?
因為負責處理 UI (使用者介面)的主執行序被叫去做複雜的運算,所以沒有空管 UI 的事情,要等他把當前的事情做完,才會回來更新使用者介面,介面才會有反應。
先看一個最簡單的案例(註: 為了方便說明,程式的部分和 gif 中的範例不同),按下按鈕後 Label 顯示「處理中」,使用等待三秒模擬複雜的程式邏輯處理需要的時間,然後再將 Label 顯示為「完成」
看起來沒有什麼問題,但是執行上面的程式碼後會發現按下按鈕後 Label 不會顯示「處理中」,而是介面卡住,然後 3 秒後直接顯示「完成」。
Label 不會更新是因為被判斷在這個方法中 Label 改為「處理中」後會被直接改為「完成」,所以就很「聰明」的直接等後面要顯示「完成」的時候直接顯示「完成」。
稍微提一下,如果這裡想要顯示「處理中」,我們可以加上 Refresh 讓他馬上更新,不過如果後面使用非同步之後就不會需要用到這行了
如果想要讓主執行序可以專心的處理 UI ,那我們應該要把程式主要處理的邏輯放到背景,就是使用不同的執行序來處理,也就是非同步
太棒了,問題解決了,不會再卡住了!
等等,不對阿,怎麼沒有等待 3 秒了?
原來是整個 Task.Run 裡面的程式交由其他執行序處理,所以在安排好其他執行序處理後就直接執行後面的程式讓 Label 顯示「完成」了。
所以可以使用 await 等待 Task.Run 處理結束後再繼續執行上下的程式,也因為使用 await ,所以要在方法上面加上 async,說明使用非同步來處理此方法
為什麼會這樣呢?
因為負責處理 UI (使用者介面)的主執行序被叫去做複雜的運算,所以沒有空管 UI 的事情,要等他把當前的事情做完,才會回來更新使用者介面,介面才會有反應。
先看一個最簡單的案例(註: 為了方便說明,程式的部分和 gif 中的範例不同),按下按鈕後 Label 顯示「處理中」,使用等待三秒模擬複雜的程式邏輯處理需要的時間,然後再將 Label 顯示為「完成」
private void Button_Click(object sender, RoutedEventArgs e)
{
label.Text = "處理中";
// 這裡代表複雜的運算
Thread.Sleep(3000); // 等待 3 秒
label.Text = "完成";
}
看起來沒有什麼問題,但是執行上面的程式碼後會發現按下按鈕後 Label 不會顯示「處理中」,而是介面卡住,然後 3 秒後直接顯示「完成」。
Label 不會更新是因為被判斷在這個方法中 Label 改為「處理中」後會被直接改為「完成」,所以就很「聰明」的直接等後面要顯示「完成」的時候直接顯示「完成」。
稍微提一下,如果這裡想要顯示「處理中」,我們可以加上 Refresh 讓他馬上更新,不過如果後面使用非同步之後就不會需要用到這行了
private void Button_Click(object sender, RoutedEventArgs e)
{
label.Text = "處理中";
label.Refresh();
// 這裡代表複雜的運算
Thread.Sleep(3000); // 等待 3 秒
label.Text = "完成";
}
如果想要讓主執行序可以專心的處理 UI ,那我們應該要把程式主要處理的邏輯放到背景,就是使用不同的執行序來處理,也就是非同步
private void Button_Click(object sender, RoutedEventArgs e)
{
label.Text = "處理中";
label.Refresh();
Task.Run(() =>
{
// 這裡代表複雜的運算
Thread.Sleep(3000); // 等待 3 秒
});
label.Text = "完成";
}
太棒了,問題解決了,不會再卡住了!
等等,不對阿,怎麼沒有等待 3 秒了?
原來是整個 Task.Run 裡面的程式交由其他執行序處理,所以在安排好其他執行序處理後就直接執行後面的程式讓 Label 顯示「完成」了。
所以可以使用 await 等待 Task.Run 處理結束後再繼續執行上下的程式,也因為使用 await ,所以要在方法上面加上 async,說明使用非同步來處理此方法
private async void Button_Click(object sender, RoutedEventArgs e)
{
label.Text = "處理中";
label.Refresh();
await Task.Run(() =>
{
// 這裡代表複雜的運算
Thread.Sleep(3000); // 等待 3 秒
});
label.Text = "完成";
}
謝謝你的分享,幫了我一個大忙.
回覆刪除感謝分享,幫大忙了
回覆刪除感謝分享
回覆刪除請問如何使時間跟UI同步更新 邊執行邊更新
回覆刪除