轉(zhuǎn)載請(qǐng)注明原作者
目錄
WPF中UI線程頻繁操作造成卡頓的處理(一)
WPF中UI線程頻繁操作造成卡頓的處理(二)
問題描述
有些時(shí)候,在項(xiàng)目開發(fā)中可能需要在某一很短時(shí)間內(nèi)頻繁操作UI控件,如以循環(huán)方式中瞬間向UI界面上添加1000張圖片。這個(gè)任務(wù)場(chǎng)景有2個(gè)特點(diǎn):
(1)所完成的任務(wù)是耗時(shí)任務(wù)——要頻繁讀取圖片和加載圖片。
(2)加載圖片是UI操作,無法在非UI線程中完成。
解決辦法:
能否每加載一個(gè)圖片更新一下UI界面?而不是1000張圖片讀取完畢后一次性附加到界面上顯示。
預(yù)備知識(shí):
(1)WPF中Invoke和BeginInvoke方法,參考鏈接:http://www.cnblogs.com/Z-King/archive/2011/11/03/2234337.html
(2)Dispatcher的認(rèn)識(shí),參考鏈接:http://blog.csdn.net/albert528108/article/details/51503955
(3)DispatcherPriority的認(rèn)識(shí),參考鏈接:https://msdn.microsoft.com/zh-cn/library/system.windows.threading.dispatcherpriority.aspx
(4)依賴屬性、INotifyPropertyChanged、ObservableCollection相關(guān)知識(shí)。
分析:
(1)所有UI的操作必須在UI線程上進(jìn)行,無法在子線程中進(jìn)行。
(2)線程上的操作又由Dispatcher分為不同的優(yōu)先級(jí)。如果不希望UI上出現(xiàn)卡頓的情況,就必須將UI線程的圖片加載(render)操作的優(yōu)先級(jí)別降到UI線程上輸入(input)操作的優(yōu)先級(jí)之下。也就是input操作優(yōu)先于圖片呈現(xiàn),界面就不會(huì)出現(xiàn)卡死狀態(tài)。
(3)所有操作必須異步進(jìn)行,這樣不會(huì)堵塞線程。為了不讓1000張圖片同時(shí)加載到界面上,這里逐步調(diào)用BeginInvoke方法,每一次只比上一次多加載一張圖片。何時(shí)終止,采用遞歸方式反復(fù)調(diào)用直到讀完圖片為止。
做法一:
未逐步加載,一次加載圖片的做法。代碼:
private void Btn_Click(object sender, RoutedEventArgs e)
{
strings = loadDir(@"G:\BaiduYunDownload\風(fēng)景圖片壁紙\風(fēng)景圖片壁紙100張");
lb.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
lb.ItemsSource = loadDir(@"G:\BaiduYunDownload\風(fēng)景圖片壁紙\風(fēng)景圖片壁紙100張");
}));
}
效果圖如下:

所有圖片明顯一次性出現(xiàn),按鈕點(diǎn)擊后直接卡住。等所有圖片都出現(xiàn)后,按鈕的卡住狀態(tài)恢復(fù)正常。
做法二:
圖片一張張逐步加載,每次多增加一張圖片。代碼:
int i;
List<String> strings;
ObservableCollection<String> strs = new ObservableCollection<string>();
private void Btn_Click(object sender, RoutedEventArgs e)
{
strings = loadDir(@"G:\BaiduYunDownload\風(fēng)景圖片壁紙\風(fēng)景圖片壁紙100張");
strs.Clear();
i = 0;
lb.ItemsSource = strs;
lb.Dispatcher.BeginInvoke(DispatcherPriority.Background, new AddItemDelegate(addItem));
}
private delegate void AddItemDelegate();
private void addItem()
{
if (i < strings.Count)
{
strs.Add(strings[i++]);
lb.Dispatcher.BeginInvoke(DispatcherPriority.Background,new AddItemDelegate(addItem));
}
}
private List<String> loadDir(string dirpath)
{
List<String> strs = new List<string>();
if (Directory.Exists(dirpath))
{
foreach (var item in new DirectoryInfo(dirpath).GetFiles("*.jpg"))
{
strs.Add(item.FullName);
}
}
return strs;
}
效果圖如下:

圖片逐步加載,每次比上次多增加一張。按鈕點(diǎn)擊后立即恢復(fù)原來狀態(tài),UI無卡頓。