眾所周知, WPF 的 UI 渲染是單線程的,所以如果我們異步或者新建線程去進行數據處理的時候,處理完,想要更新 UI 的時候,需要調用一下 Dispatcher.Invoke,將處理完的數據推入到 Dispatcher 中,等待更新界面,不然就會報調用線程無法訪問此對象,因為另一個線程擁有該對象的錯誤。
這就是為什么 WPF 中的大多數對象派生自 DispatcherObject,因為需要 Dispatcher 處理並發和線程的情況。
但是工作中,有時候會遇到 UI 密集型的情況,也就是界面不停渲染導致界面出現卡住的情況,這時候就是 Dispatcher 忙不過來了,一般這時候,都會想要用多線程來渲染界面,但是一個 Dispatcher 只能為一個線程服務,所以我一般會將這種 UI 密集型的界面,單獨放到一個彈窗中,再新建一個 UI 線程並指定 Dispatcher 來渲染。
樣例代碼
代碼也很簡單,我直接貼啦!
我在界面中放了兩個按鈕,一個用來打開負責大量渲染的窗口,一個用來關閉該窗口。
Window w = null;
newWindowButton.Click += (sender, args) =>
{
var thread = new Thread(() =>
{
w = new Window
{
Content = new LargeRenderView(),
Width = 1200,
Height = 1000
};
w.Show();
Dispatcher.Run(); // 運行 Dispatcher,為新建的 UI 線程服務
});
thread.SetApartmentState(ApartmentState.STA); // // 指定線程為單線程模式
thread.Start();
};
closeWindowButton.Click += (sender, args) =>
{
w.Close();
if (w == null) return;
if (w.Dispatcher.CheckAccess())
w.Close();
else
w.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(w.Close));
};
效果
可以看到新彈窗因為大量渲染,鼠標一直在轉圈,無法操作,但是主窗口還是可以進行 UI 操作,所以主窗口沒有被這個大量渲染影響到。